Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/core/servers/basehttp.py: 26%

127 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1""" 

2HTTP server that implements the Python WSGI protocol (PEP 333, rev 1.21). 

3 

4Based on wsgiref.simple_server which is part of the standard library since 2.5. 

5 

6This is a simple server for use in testing or debugging Django apps. It hasn't 

7been reviewed for security issues. DON'T USE IT FOR PRODUCTION USE! 

8""" 

9 

10import logging 

11import socket 

12import socketserver 

13import sys 

14from wsgiref import simple_server 

15 

16from django.core.exceptions import ImproperlyConfigured 

17from django.core.handlers.wsgi import LimitedStream 

18from django.core.wsgi import get_wsgi_application 

19from django.db import connections 

20from django.utils.module_loading import import_string 

21 

22__all__ = ("WSGIServer", "WSGIRequestHandler") 

23 

24logger = logging.getLogger("django.server") 

25 

26 

27def get_internal_wsgi_application(): 

28 """ 

29 Load and return the WSGI application as configured by the user in 

30 ``settings.WSGI_APPLICATION``. With the default ``startproject`` layout, 

31 this will be the ``application`` object in ``projectname/wsgi.py``. 

32 

33 This function, and the ``WSGI_APPLICATION`` setting itself, are only useful 

34 for Django's internal server (runserver); external WSGI servers should just 

35 be configured to point to the correct application object directly. 

36 

37 If settings.WSGI_APPLICATION is not set (is ``None``), return 

38 whatever ``django.core.wsgi.get_wsgi_application`` returns. 

39 """ 

40 from django.conf import settings 

41 

42 app_path = getattr(settings, "WSGI_APPLICATION") 

43 if app_path is None: 

44 return get_wsgi_application() 

45 

46 try: 

47 return import_string(app_path) 

48 except ImportError as err: 

49 raise ImproperlyConfigured( 

50 "WSGI application '%s' could not be loaded; " 

51 "Error importing module." % app_path 

52 ) from err 

53 

54 

55def is_broken_pipe_error(): 

56 exc_type, _, _ = sys.exc_info() 

57 return issubclass( 

58 exc_type, 

59 ( 

60 BrokenPipeError, 

61 ConnectionAbortedError, 

62 ConnectionResetError, 

63 ), 

64 ) 

65 

66 

67class WSGIServer(simple_server.WSGIServer): 

68 """BaseHTTPServer that implements the Python WSGI protocol""" 

69 

70 request_queue_size = 10 

71 

72 def __init__(self, *args, ipv6=False, allow_reuse_address=True, **kwargs): 

73 if ipv6: 

74 self.address_family = socket.AF_INET6 

75 self.allow_reuse_address = allow_reuse_address 

76 super().__init__(*args, **kwargs) 

77 

78 def handle_error(self, request, client_address): 

79 if is_broken_pipe_error(): 

80 logger.info("- Broken pipe from %s\n", client_address) 

81 else: 

82 super().handle_error(request, client_address) 

83 

84 

85class ThreadedWSGIServer(socketserver.ThreadingMixIn, WSGIServer): 

86 """A threaded version of the WSGIServer""" 

87 

88 daemon_threads = True 

89 

90 def __init__(self, *args, connections_override=None, **kwargs): 

91 super().__init__(*args, **kwargs) 

92 self.connections_override = connections_override 

93 

94 # socketserver.ThreadingMixIn.process_request() passes this method as 

95 # the target to a new Thread object. 

96 def process_request_thread(self, request, client_address): 

97 if self.connections_override: 

98 # Override this thread's database connections with the ones 

99 # provided by the parent thread. 

100 for alias, conn in self.connections_override.items(): 

101 connections[alias] = conn 

102 super().process_request_thread(request, client_address) 

103 

104 def _close_connections(self): 

105 # Used for mocking in tests. 

106 connections.close_all() 

107 

108 def close_request(self, request): 

109 self._close_connections() 

110 super().close_request(request) 

111 

112 

113class ServerHandler(simple_server.ServerHandler): 

114 http_version = "1.1" 

115 

116 def __init__(self, stdin, stdout, stderr, environ, **kwargs): 

117 """ 

118 Use a LimitedStream so that unread request data will be ignored at 

119 the end of the request. WSGIRequest uses a LimitedStream but it 

120 shouldn't discard the data since the upstream servers usually do this. 

121 This fix applies only for testserver/runserver. 

122 """ 

123 try: 

124 content_length = int(environ.get("CONTENT_LENGTH")) 

125 except (ValueError, TypeError): 

126 content_length = 0 

127 super().__init__( 

128 LimitedStream(stdin, content_length), stdout, stderr, environ, **kwargs 

129 ) 

130 

131 def cleanup_headers(self): 

132 super().cleanup_headers() 

133 # HTTP/1.1 requires support for persistent connections. Send 'close' if 

134 # the content length is unknown to prevent clients from reusing the 

135 # connection. 

136 if "Content-Length" not in self.headers: 

137 self.headers["Connection"] = "close" 

138 # Persistent connections require threading server. 

139 elif not isinstance(self.request_handler.server, socketserver.ThreadingMixIn): 

140 self.headers["Connection"] = "close" 

141 # Mark the connection for closing if it's set as such above or if the 

142 # application sent the header. 

143 if self.headers.get("Connection") == "close": 

144 self.request_handler.close_connection = True 

145 

146 def close(self): 

147 self.get_stdin()._read_limited() 

148 super().close() 

149 

150 

151class WSGIRequestHandler(simple_server.WSGIRequestHandler): 

152 protocol_version = "HTTP/1.1" 

153 

154 def address_string(self): 

155 # Short-circuit parent method to not call socket.getfqdn 

156 return self.client_address[0] 

157 

158 def log_message(self, format, *args): 

159 extra = { 

160 "request": self.request, 

161 "server_time": self.log_date_time_string(), 

162 } 

163 if args[1][0] == "4": 

164 # 0x16 = Handshake, 0x03 = SSL 3.0 or TLS 1.x 

165 if args[0].startswith("\x16\x03"): 

166 extra["status_code"] = 500 

167 logger.error( 

168 "You're accessing the development server over HTTPS, but " 

169 "it only supports HTTP.\n", 

170 extra=extra, 

171 ) 

172 return 

173 

174 if args[1].isdigit() and len(args[1]) == 3: 

175 status_code = int(args[1]) 

176 extra["status_code"] = status_code 

177 

178 if status_code >= 500: 

179 level = logger.error 

180 elif status_code >= 400: 

181 level = logger.warning 

182 else: 

183 level = logger.info 

184 else: 

185 level = logger.info 

186 

187 level(format, *args, extra=extra) 

188 

189 def get_environ(self): 

190 # Strip all headers with underscores in the name before constructing 

191 # the WSGI environ. This prevents header-spoofing based on ambiguity 

192 # between underscores and dashes both normalized to underscores in WSGI 

193 # env vars. Nginx and Apache 2.4+ both do this as well. 

194 for k in self.headers: 

195 if "_" in k: 

196 del self.headers[k] 

197 

198 return super().get_environ() 

199 

200 def handle(self): 

201 self.close_connection = True 

202 self.handle_one_request() 

203 while not self.close_connection: 

204 self.handle_one_request() 

205 try: 

206 self.connection.shutdown(socket.SHUT_WR) 

207 except (AttributeError, OSError): 

208 pass 

209 

210 def handle_one_request(self): 

211 """Copy of WSGIRequestHandler.handle() but with different ServerHandler""" 

212 self.raw_requestline = self.rfile.readline(65537) 

213 if len(self.raw_requestline) > 65536: 

214 self.requestline = "" 

215 self.request_version = "" 

216 self.command = "" 

217 self.send_error(414) 

218 return 

219 

220 if not self.parse_request(): # An error code has been sent, just exit 

221 return 

222 

223 handler = ServerHandler( 

224 self.rfile, self.wfile, self.get_stderr(), self.get_environ() 

225 ) 

226 handler.request_handler = self # backpointer for logging & connection closing 

227 handler.run(self.server.get_app()) 

228 

229 

230def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer): 

231 server_address = (addr, port) 

232 if threading: 

233 httpd_cls = type("WSGIServer", (socketserver.ThreadingMixIn, server_cls), {}) 

234 else: 

235 httpd_cls = server_cls 

236 httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6) 

237 if threading: 

238 # ThreadingMixIn.daemon_threads indicates how threads will behave on an 

239 # abrupt shutdown; like quitting the server by the user or restarting 

240 # by the auto-reloader. True means the server will not wait for thread 

241 # termination before it quits. This will make auto-reloader faster 

242 # and will prevent the need to kill the server manually if a thread 

243 # isn't terminating correctly. 

244 httpd.daemon_threads = True 

245 httpd.set_app(wsgi_handler) 

246 httpd.serve_forever()