158 # Ensure the slicing of path below is valid |
158 # Ensure the slicing of path below is valid |
159 if path != self.server.prefix and not path.startswith( |
159 if path != self.server.prefix and not path.startswith( |
160 self.server.prefix + b'/' |
160 self.server.prefix + b'/' |
161 ): |
161 ): |
162 self._start_response(pycompat.strurl(common.statusmessage(404)), []) |
162 self._start_response(pycompat.strurl(common.statusmessage(404)), []) |
163 if self.command == r'POST': |
163 if self.command == 'POST': |
164 # Paranoia: tell the client we're going to close the |
164 # Paranoia: tell the client we're going to close the |
165 # socket so they don't try and reuse a socket that |
165 # socket so they don't try and reuse a socket that |
166 # might have a POST body waiting to confuse us. We do |
166 # might have a POST body waiting to confuse us. We do |
167 # this by directly munging self.saved_headers because |
167 # this by directly munging self.saved_headers because |
168 # self._start_response ignores Connection headers. |
168 # self._start_response ignores Connection headers. |
169 self.saved_headers = [(r'Connection', r'Close')] |
169 self.saved_headers = [('Connection', 'Close')] |
170 self._write(b"Not Found") |
170 self._write(b"Not Found") |
171 self._done() |
171 self._done() |
172 return |
172 return |
173 |
173 |
174 env = {} |
174 env = {} |
175 env[r'GATEWAY_INTERFACE'] = r'CGI/1.1' |
175 env['GATEWAY_INTERFACE'] = 'CGI/1.1' |
176 env[r'REQUEST_METHOD'] = self.command |
176 env['REQUEST_METHOD'] = self.command |
177 env[r'SERVER_NAME'] = self.server.server_name |
177 env['SERVER_NAME'] = self.server.server_name |
178 env[r'SERVER_PORT'] = str(self.server.server_port) |
178 env['SERVER_PORT'] = str(self.server.server_port) |
179 env[r'REQUEST_URI'] = self.path |
179 env['REQUEST_URI'] = self.path |
180 env[r'SCRIPT_NAME'] = pycompat.sysstr(self.server.prefix) |
180 env['SCRIPT_NAME'] = pycompat.sysstr(self.server.prefix) |
181 env[r'PATH_INFO'] = pycompat.sysstr(path[len(self.server.prefix) :]) |
181 env['PATH_INFO'] = pycompat.sysstr(path[len(self.server.prefix) :]) |
182 env[r'REMOTE_HOST'] = self.client_address[0] |
182 env['REMOTE_HOST'] = self.client_address[0] |
183 env[r'REMOTE_ADDR'] = self.client_address[0] |
183 env['REMOTE_ADDR'] = self.client_address[0] |
184 env[r'QUERY_STRING'] = query or r'' |
184 env['QUERY_STRING'] = query or '' |
185 |
185 |
186 if pycompat.ispy3: |
186 if pycompat.ispy3: |
187 if self.headers.get_content_type() is None: |
187 if self.headers.get_content_type() is None: |
188 env[r'CONTENT_TYPE'] = self.headers.get_default_type() |
188 env['CONTENT_TYPE'] = self.headers.get_default_type() |
189 else: |
189 else: |
190 env[r'CONTENT_TYPE'] = self.headers.get_content_type() |
190 env['CONTENT_TYPE'] = self.headers.get_content_type() |
191 length = self.headers.get(r'content-length') |
191 length = self.headers.get('content-length') |
192 else: |
192 else: |
193 if self.headers.typeheader is None: |
193 if self.headers.typeheader is None: |
194 env[r'CONTENT_TYPE'] = self.headers.type |
194 env['CONTENT_TYPE'] = self.headers.type |
195 else: |
195 else: |
196 env[r'CONTENT_TYPE'] = self.headers.typeheader |
196 env['CONTENT_TYPE'] = self.headers.typeheader |
197 length = self.headers.getheader(r'content-length') |
197 length = self.headers.getheader('content-length') |
198 if length: |
198 if length: |
199 env[r'CONTENT_LENGTH'] = length |
199 env['CONTENT_LENGTH'] = length |
200 for header in [ |
200 for header in [ |
201 h |
201 h |
202 for h in self.headers.keys() |
202 for h in self.headers.keys() |
203 if h.lower() not in (r'content-type', r'content-length') |
203 if h.lower() not in ('content-type', 'content-length') |
204 ]: |
204 ]: |
205 hkey = r'HTTP_' + header.replace(r'-', r'_').upper() |
205 hkey = 'HTTP_' + header.replace('-', '_').upper() |
206 hval = self.headers.get(header) |
206 hval = self.headers.get(header) |
207 hval = hval.replace(r'\n', r'').strip() |
207 hval = hval.replace('\n', '').strip() |
208 if hval: |
208 if hval: |
209 env[hkey] = hval |
209 env[hkey] = hval |
210 env[r'SERVER_PROTOCOL'] = self.request_version |
210 env['SERVER_PROTOCOL'] = self.request_version |
211 env[r'wsgi.version'] = (1, 0) |
211 env['wsgi.version'] = (1, 0) |
212 env[r'wsgi.url_scheme'] = pycompat.sysstr(self.url_scheme) |
212 env['wsgi.url_scheme'] = pycompat.sysstr(self.url_scheme) |
213 if env.get(r'HTTP_EXPECT', b'').lower() == b'100-continue': |
213 if env.get('HTTP_EXPECT', b'').lower() == b'100-continue': |
214 self.rfile = common.continuereader(self.rfile, self.wfile.write) |
214 self.rfile = common.continuereader(self.rfile, self.wfile.write) |
215 |
215 |
216 env[r'wsgi.input'] = self.rfile |
216 env['wsgi.input'] = self.rfile |
217 env[r'wsgi.errors'] = _error_logger(self) |
217 env['wsgi.errors'] = _error_logger(self) |
218 env[r'wsgi.multithread'] = isinstance( |
218 env['wsgi.multithread'] = isinstance( |
219 self.server, socketserver.ThreadingMixIn |
219 self.server, socketserver.ThreadingMixIn |
220 ) |
220 ) |
221 if util.safehasattr(socketserver, b'ForkingMixIn'): |
221 if util.safehasattr(socketserver, b'ForkingMixIn'): |
222 env[r'wsgi.multiprocess'] = isinstance( |
222 env['wsgi.multiprocess'] = isinstance( |
223 self.server, socketserver.ForkingMixIn |
223 self.server, socketserver.ForkingMixIn |
224 ) |
224 ) |
225 else: |
225 else: |
226 env[r'wsgi.multiprocess'] = False |
226 env['wsgi.multiprocess'] = False |
227 |
227 |
228 env[r'wsgi.run_once'] = 0 |
228 env['wsgi.run_once'] = 0 |
229 |
229 |
230 wsgiref.validate.check_environ(env) |
230 wsgiref.validate.check_environ(env) |
231 |
231 |
232 self.saved_status = None |
232 self.saved_status = None |
233 self.saved_headers = [] |
233 self.saved_headers = [] |
249 self.send_response(*saved_status) |
249 self.send_response(*saved_status) |
250 self.length = None |
250 self.length = None |
251 self._chunked = False |
251 self._chunked = False |
252 for h in self.saved_headers: |
252 for h in self.saved_headers: |
253 self.send_header(*h) |
253 self.send_header(*h) |
254 if h[0].lower() == r'content-length': |
254 if h[0].lower() == 'content-length': |
255 self.length = int(h[1]) |
255 self.length = int(h[1]) |
256 if self.length is None and saved_status[0] != common.HTTP_NOT_MODIFIED: |
256 if self.length is None and saved_status[0] != common.HTTP_NOT_MODIFIED: |
257 self._chunked = ( |
257 self._chunked = ( |
258 not self.close_connection |
258 not self.close_connection and self.request_version == 'HTTP/1.1' |
259 and self.request_version == r'HTTP/1.1' |
|
260 ) |
259 ) |
261 if self._chunked: |
260 if self._chunked: |
262 self.send_header(r'Transfer-Encoding', r'chunked') |
261 self.send_header('Transfer-Encoding', 'chunked') |
263 else: |
262 else: |
264 self.send_header(r'Connection', r'close') |
263 self.send_header('Connection', 'close') |
265 self.end_headers() |
264 self.end_headers() |
266 self.sent_headers = True |
265 self.sent_headers = True |
267 |
266 |
268 def _start_response(self, http_status, headers, exc_info=None): |
267 def _start_response(self, http_status, headers, exc_info=None): |
269 assert isinstance(http_status, str) |
268 assert isinstance(http_status, str) |
270 code, msg = http_status.split(None, 1) |
269 code, msg = http_status.split(None, 1) |
271 code = int(code) |
270 code = int(code) |
272 self.saved_status = http_status |
271 self.saved_status = http_status |
273 bad_headers = (r'connection', r'transfer-encoding') |
272 bad_headers = ('connection', 'transfer-encoding') |
274 self.saved_headers = [ |
273 self.saved_headers = [ |
275 h for h in headers if h[0].lower() not in bad_headers |
274 h for h in headers if h[0].lower() not in bad_headers |
276 ] |
275 ] |
277 return self._write |
276 return self._write |
278 |
277 |