Browse Source

Code cleaning

Deleting code from old standalone server
Yann Weber 8 years ago
parent
commit
93f1f91699
1 changed files with 0 additions and 186 deletions
  1. 0
    186
      lodel/plugins/multisite/main.py

+ 0
- 186
lodel/plugins/multisite/main.py View File

1
-#Known bugs : 
2
-#Sockets are not closed properly leading in a listening socket leak, a patch
3
-#will be submitted for cpython 3.6
4
-#
5
-
6
-import os
7
-import sys
8
-import signal
9
-import socket
10
-import socketserver
11
-import wsgiref
12
-import wsgiref.simple_server
13
-from wsgiref.simple_server import make_server
14
-from io import BufferedWriter
15
-
16
-from lodel.context import LodelContext
17
-from lodel.context import ContextError
18
-
19
-##@brief Set the poll interval to detect shutdown requests (do not work)
20
-SHUTDOWN_POLL_INTERVAL = 0.1 # <-- No impact because of ForkingTCPServer bug
21
-
22
-##@brief Stores the signal we uses to kill childs
23
-KILLING_CHILDS_SIGNAL = signal.SIGTERM
24
-
25
-FAST_APP_EXPOSAL_CACHE = dict()
26
-
27
-##@brief Reimplementation of WSGIRequestHandler
28
-#
29
-#Handler class designed to be called by socketserver child classes to handle
30
-#a request.
31
-#We inherit from wsgiref.simple_server.WSGIRequestHandler to avoid writing
32
-#all the construction of the wsgi variables
33
-class LodelWSGIHandler(wsgiref.simple_server.WSGIRequestHandler):
34
-    
35
-    ##@brief Method called by the socketserver to handle a request
36
-    def handle(self):
37
-        #Register a signal handler for sigint in the child process
38
-        req_ref = self.request
39
-        def sigstop_handler_client(signal, frame):
40
-            req_ref.close()
41
-            print("Client %d stopping by signal" % os.getpid())
42
-            os._exit(0)
43
-        signal.signal(KILLING_CHILDS_SIGNAL, sigstop_handler_client)
44
-        #Dirty copy & past from Lib/http/server.py in Cpython sources       
45
-        try:
46
-            self.raw_requestline = self.rfile.readline(65537)
47
-            if len(self.raw_requestline) > 65536:
48
-                self.requestline = ''
49
-                self.request_version = ''
50
-                self.command = ''
51
-                self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
52
-                return
53
-            if not self.raw_requestline:
54
-                self.close_connection = True
55
-                return
56
-            if not self.parse_request():
57
-                return
58
-            #Here begin custom code
59
-            env = self.get_environ()
60
-            stdout = BufferedWriter(self.wfile)
61
-            try:
62
-                handler = wsgiref.handlers.SimpleHandler(
63
-                    self.rfile, stdout, self.get_stderr(), env)
64
-                handler.request_handler = self      # backpointer for logging
65
-                handler.run(self.server.get_app())
66
-            finally:
67
-                stdout.detach()
68
-        except socket.timeout as e:
69
-            self.log_error("Request timed out: %r", e)
70
-            self.close_connection = True
71
-            return
72
-    
73
-    ##@brief An attempt to solve the socket leak problem
74
-    def close(self):
75
-        self.request.close()
76
-        super().close()
77
-
78
-   
79
-class CustomForkingTCPServer(socketserver.ForkingTCPServer):
80
-    ##@brief static property indicating the max number of childs allowed
81
-    max_children = 40
82
-
83
-    def __init__(self, *args, **kwargs):
84
-        super().__init__(*args, **kwargs)
85
-        if self.__class__.active_children is None:
86
-            self.__class__.active_childer = set()
87
-    
88
-    ##@brief Implements max_children limitations
89
-    def process_request(self, request, client_address):
90
-        while self.active_children is not None and \
91
-                len(self.active_children) > self.__class__.max_children:
92
-            self.collect_children()
93
-        super().process_request(request, client_address)
94
-        
95
-    ##@brief Custom reimplementation of shutdown method in order to ensure
96
-    #that we close all listening sockets
97
-    #
98
-    #This method is here because of a bug (or a missing feature) : 
99
-    #The socketserver implementation force to call the shutdown method
100
-    #from another thread/process else it leads in a deadlock.
101
-    #The problem is that the implementation of shutdown set a private attribute
102
-    #__shutdown_request to true. So we cannot reimplement a method that will
103
-    #just set the flag to True, we have to manually collect each actives 
104
-    #childs. A patch is prepared and will be proposed for cpython upstream.
105
-    def shutdown(self):
106
-        if self.active_children is not None:
107
-            for pid in self.active_children.copy():
108
-                print("Killing : %d"%pid)
109
-                os.kill(pid, KILLING_CHILDS_SIGNAL)
110
-                try:
111
-                    pid, _ = os.waitpid(pid, 0)
112
-                    self.active_children.discard(pid)
113
-                except ChildProcessError:
114
-                    self.active_children.discard(pid)
115
-        self.server_close()
116
-
117
-##@brief WSGIServer implementing ForkingTCPServer.
118
-#
119
-#Same features than wsgiref.simple_server.WSGIServer but process each requests
120
-#in a child process
121
-class ForkingWSGIServer(
122
-        wsgiref.simple_server.WSGIServer, CustomForkingTCPServer):
123
-    pass
124
- 
125
-
126
-##@brief utility function to extract site id from an url
127
-def site_id_from_url(url):
128
-    res = ''
129
-    for c in url[1:]:
130
-        if c == '/':
131
-            break
132
-        res += c
133
-    if len(res) == 0:
134
-        return None
135
-    return res
136
-
137
-##@brief Utility function to return quickly an error
138
-def http_error(env, start_response, status = '500 internal server error', \
139
-        extra = None):
140
-    headers = [('Content-type', 'text/plain; charset=utf-8')]
141
-    start_response(status, headers)
142
-    msg = status
143
-    if extra is not None:
144
-        msg = extra
145
-    return [msg.encode('utf-8')]
146
-
147
-##@brief This method is run in a child process by the handler
148
-def wsgi_router(env, start_response):
149
-    #Attempt to load a context
150
-    site_id = site_id_from_url(env['PATH_INFO'])
151
-    if site_id is None:
152
-        #It can be nice to provide a list of instances here
153
-        return http_error(env, start_response, '404 Not Found')
154
-    try:
155
-        LodelContext.set(site_id)
156
-        #We are in the good context
157
-
158
-    except ContextError as e:
159
-        print(e)
160
-        return http_error(env, start_response, '404 Not found',
161
-            "No site named '%s'" % site_id)
162
-    #Calling webui
163
-    return FAST_APP_EXPOSAL_CACHE[site_id].application(env, start_response)
164
-    #LodelContext.expose_modules(globals(), {
165
-    #    'lodel.plugins.webui.run': ['application']})
166
-    #return application(env, start_response)
167
-
168
-##@brief Starts the server until a SIGINT is received
169
-def main_loop():
170
-    LodelContext.expose_modules(globals(), {'lodel.settings': ['Settings']})
171
-    ForkingWSGIServer.max_children = Settings.server.max_children
172
-    listen_addr = Settings.server.listen_address
173
-    listen_port = Settings.server.listen_port
174
-    server = wsgiref.simple_server.make_server(
175
-        listen_addr, listen_port, wsgi_router,
176
-        server_class=ForkingWSGIServer, handler_class = LodelWSGIHandler)
177
-    #Signal handler to close server properly on sigint
178
-    def sigint_handler(signal, frame):
179
-        print("Ctrl-c pressed, exiting")
180
-        server.shutdown()
181
-        server.server_close()
182
-        exit(0)
183
-    signal.signal(signal.SIGINT, sigint_handler)
184
-    #Listen until SIGINT
185
-    server.serve_forever(SHUTDOWN_POLL_INTERVAL)
186
-

Loading…
Cancel
Save