Browse Source

Solves the client socket leak bug in forking wsgi server

Yann Weber 8 years ago
parent
commit
2a7c196bfb
1 changed files with 38 additions and 11 deletions
  1. 38
    11
      lodel/plugins/multisite/main.py

+ 38
- 11
lodel/plugins/multisite/main.py View File

@@ -2,7 +2,6 @@
2 2
 #Sockets are not closed properly leading in a listening socket leak
3 3
 #
4 4
 
5
-
6 5
 import wsgiref
7 6
 import wsgiref.simple_server
8 7
 from wsgiref.simple_server import make_server
@@ -20,14 +19,32 @@ import sys, signal
20 19
 from lodel.context import LodelContext
21 20
 from lodel.context import ContextError
22 21
 
22
+##@brief On wich addr we want to bind. '' mean all interfaces
23 23
 LISTEN_ADDR = ''
24
+##@brief Listening socket port
24 25
 LISTEN_PORT = 1337
25 26
 
27
+##@brief Set the poll interval to detect shutdown requests (do not work)
26 28
 SHUTDOWN_POLL_INTERVAL = 0.1
27 29
 
30
+##@brief Reimplementation of WSGIRequestHandler
31
+#
32
+#Handler class designed to be called by socketserver child classes to handle
33
+#a request.
34
+#We inherit from wsgiref.simple_server.WSGIRequestHandler to avoid writing
35
+#all the construction of the wsgi variables
28 36
 class HtppHandler(wsgiref.simple_server.WSGIRequestHandler):
37
+    
38
+    ##@brief Method called by the socketserver to handle a request
29 39
     def handle(self):
30 40
         print("addr : %s %s\n" % (self.client_address, type(self.request)))
41
+        #Register a signal handler for sigint in the child process
42
+        req_ref = self.request
43
+        def sigstop_handler_client(signal, frame):
44
+            req_ref.close()
45
+            print("Client %d stopping by signal" % os.getpid())
46
+            os._exit(0)
47
+        signal.signal(signal.SIGTERM, sigstop_handler_client)
31 48
         #Dirty copy & past from Lib/http/server.py in Cpython sources       
32 49
         try:
33 50
             self.raw_requestline = self.rfile.readline(65537)
@@ -129,15 +146,25 @@ class HttpServer(socketserver.ForkingTCPServer):
129 146
     def get_app(self):
130 147
         return wsgi_router
131 148
 
132
-    ##@brief An attempt to solve the socket leak problem
133
-    def close_request(self, request):
134
-        print("Closing client socket in server : %s" % request)
135
-        request.close()
136
-
137 149
     ##@brief An attempt to solve the socket leak problem
138 150
     def server_close(self):
151
+        self.collect_children()
139 152
         print("Closing listening socket")
140 153
         self.socket.close()
154
+    
155
+    ##@brief Custom reimplementation of shutdown method in order to ensure
156
+    #that we close all listening sockets
157
+    def shutdown(self):
158
+        if self.active_children is not None:
159
+            for pid in self.active_children.copy():
160
+                print("Killing : %d"%pid)
161
+                os.kill(pid, signal.SIGTERM)
162
+                try:
163
+                    pid, _ = os.waitpid(pid, 0)
164
+                    self.active_children.discard(pid)
165
+                except ChildProcessError:
166
+                    self.active_children.discard(pid)
167
+        self.server_close()
141 168
 
142 169
 ##@brief utility function to extract site id from an url
143 170
 def site_id_from_url(url):
@@ -193,7 +220,7 @@ def main_loop():
193 220
     
194 221
     #Set the start method for multiprocessing
195 222
     mp.set_start_method('forkserver')
196
-    print("\n\nPID = %d\n\n" % os.getpid())
223
+    print("PID = %d" % os.getpid())
197 224
 
198 225
     listen_addr = LISTEN_ADDR
199 226
     listen_port = LISTEN_PORT
@@ -206,10 +233,10 @@ def main_loop():
206 233
     #Signal handler to close server properly on sigint
207 234
     def sigint_handler(signal, frame):
208 235
         print("Ctrl-c pressed, exiting")
209
-        server.shutdown() # <-- Do not work for unkonwn reasons
236
+        server.shutdown()
210 237
         server.server_close()
211
-        sys.exit(0)
212
-    #signal.signal(signal.SIGINT, sigint_handler)
213
-
238
+        exit(0)
239
+    signal.signal(signal.SIGINT, sigint_handler)
240
+    #Listen until SIGINT
214 241
     server.serve_forever(SHUTDOWN_POLL_INTERVAL)
215 242
 

Loading…
Cancel
Save