1*cda5da8dSAndroid Build Coastguard Worker"""Base classes for server/gateway implementations""" 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard Workerfrom .util import FileWrapper, guess_scheme, is_hop_by_hop 4*cda5da8dSAndroid Build Coastguard Workerfrom .headers import Headers 5*cda5da8dSAndroid Build Coastguard Worker 6*cda5da8dSAndroid Build Coastguard Workerimport sys, os, time 7*cda5da8dSAndroid Build Coastguard Worker 8*cda5da8dSAndroid Build Coastguard Worker__all__ = [ 9*cda5da8dSAndroid Build Coastguard Worker 'BaseHandler', 'SimpleHandler', 'BaseCGIHandler', 'CGIHandler', 10*cda5da8dSAndroid Build Coastguard Worker 'IISCGIHandler', 'read_environ' 11*cda5da8dSAndroid Build Coastguard Worker] 12*cda5da8dSAndroid Build Coastguard Worker 13*cda5da8dSAndroid Build Coastguard Worker# Weekday and month names for HTTP date/time formatting; always English! 14*cda5da8dSAndroid Build Coastguard Worker_weekdayname = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] 15*cda5da8dSAndroid Build Coastguard Worker_monthname = [None, # Dummy so we can use 1-based month numbers 16*cda5da8dSAndroid Build Coastguard Worker "Jan", "Feb", "Mar", "Apr", "May", "Jun", 17*cda5da8dSAndroid Build Coastguard Worker "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 18*cda5da8dSAndroid Build Coastguard Worker 19*cda5da8dSAndroid Build Coastguard Workerdef format_date_time(timestamp): 20*cda5da8dSAndroid Build Coastguard Worker year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp) 21*cda5da8dSAndroid Build Coastguard Worker return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( 22*cda5da8dSAndroid Build Coastguard Worker _weekdayname[wd], day, _monthname[month], year, hh, mm, ss 23*cda5da8dSAndroid Build Coastguard Worker ) 24*cda5da8dSAndroid Build Coastguard Worker 25*cda5da8dSAndroid Build Coastguard Worker_is_request = { 26*cda5da8dSAndroid Build Coastguard Worker 'SCRIPT_NAME', 'PATH_INFO', 'QUERY_STRING', 'REQUEST_METHOD', 'AUTH_TYPE', 27*cda5da8dSAndroid Build Coastguard Worker 'CONTENT_TYPE', 'CONTENT_LENGTH', 'HTTPS', 'REMOTE_USER', 'REMOTE_IDENT', 28*cda5da8dSAndroid Build Coastguard Worker}.__contains__ 29*cda5da8dSAndroid Build Coastguard Worker 30*cda5da8dSAndroid Build Coastguard Workerdef _needs_transcode(k): 31*cda5da8dSAndroid Build Coastguard Worker return _is_request(k) or k.startswith('HTTP_') or k.startswith('SSL_') \ 32*cda5da8dSAndroid Build Coastguard Worker or (k.startswith('REDIRECT_') and _needs_transcode(k[9:])) 33*cda5da8dSAndroid Build Coastguard Worker 34*cda5da8dSAndroid Build Coastguard Workerdef read_environ(): 35*cda5da8dSAndroid Build Coastguard Worker """Read environment, fixing HTTP variables""" 36*cda5da8dSAndroid Build Coastguard Worker enc = sys.getfilesystemencoding() 37*cda5da8dSAndroid Build Coastguard Worker esc = 'surrogateescape' 38*cda5da8dSAndroid Build Coastguard Worker try: 39*cda5da8dSAndroid Build Coastguard Worker ''.encode('utf-8', esc) 40*cda5da8dSAndroid Build Coastguard Worker except LookupError: 41*cda5da8dSAndroid Build Coastguard Worker esc = 'replace' 42*cda5da8dSAndroid Build Coastguard Worker environ = {} 43*cda5da8dSAndroid Build Coastguard Worker 44*cda5da8dSAndroid Build Coastguard Worker # Take the basic environment from native-unicode os.environ. Attempt to 45*cda5da8dSAndroid Build Coastguard Worker # fix up the variables that come from the HTTP request to compensate for 46*cda5da8dSAndroid Build Coastguard Worker # the bytes->unicode decoding step that will already have taken place. 47*cda5da8dSAndroid Build Coastguard Worker for k, v in os.environ.items(): 48*cda5da8dSAndroid Build Coastguard Worker if _needs_transcode(k): 49*cda5da8dSAndroid Build Coastguard Worker 50*cda5da8dSAndroid Build Coastguard Worker # On win32, the os.environ is natively Unicode. Different servers 51*cda5da8dSAndroid Build Coastguard Worker # decode the request bytes using different encodings. 52*cda5da8dSAndroid Build Coastguard Worker if sys.platform == 'win32': 53*cda5da8dSAndroid Build Coastguard Worker software = os.environ.get('SERVER_SOFTWARE', '').lower() 54*cda5da8dSAndroid Build Coastguard Worker 55*cda5da8dSAndroid Build Coastguard Worker # On IIS, the HTTP request will be decoded as UTF-8 as long 56*cda5da8dSAndroid Build Coastguard Worker # as the input is a valid UTF-8 sequence. Otherwise it is 57*cda5da8dSAndroid Build Coastguard Worker # decoded using the system code page (mbcs), with no way to 58*cda5da8dSAndroid Build Coastguard Worker # detect this has happened. Because UTF-8 is the more likely 59*cda5da8dSAndroid Build Coastguard Worker # encoding, and mbcs is inherently unreliable (an mbcs string 60*cda5da8dSAndroid Build Coastguard Worker # that happens to be valid UTF-8 will not be decoded as mbcs) 61*cda5da8dSAndroid Build Coastguard Worker # always recreate the original bytes as UTF-8. 62*cda5da8dSAndroid Build Coastguard Worker if software.startswith('microsoft-iis/'): 63*cda5da8dSAndroid Build Coastguard Worker v = v.encode('utf-8').decode('iso-8859-1') 64*cda5da8dSAndroid Build Coastguard Worker 65*cda5da8dSAndroid Build Coastguard Worker # Apache mod_cgi writes bytes-as-unicode (as if ISO-8859-1) direct 66*cda5da8dSAndroid Build Coastguard Worker # to the Unicode environ. No modification needed. 67*cda5da8dSAndroid Build Coastguard Worker elif software.startswith('apache/'): 68*cda5da8dSAndroid Build Coastguard Worker pass 69*cda5da8dSAndroid Build Coastguard Worker 70*cda5da8dSAndroid Build Coastguard Worker # Python 3's http.server.CGIHTTPRequestHandler decodes 71*cda5da8dSAndroid Build Coastguard Worker # using the urllib.unquote default of UTF-8, amongst other 72*cda5da8dSAndroid Build Coastguard Worker # issues. 73*cda5da8dSAndroid Build Coastguard Worker elif ( 74*cda5da8dSAndroid Build Coastguard Worker software.startswith('simplehttp/') 75*cda5da8dSAndroid Build Coastguard Worker and 'python/3' in software 76*cda5da8dSAndroid Build Coastguard Worker ): 77*cda5da8dSAndroid Build Coastguard Worker v = v.encode('utf-8').decode('iso-8859-1') 78*cda5da8dSAndroid Build Coastguard Worker 79*cda5da8dSAndroid Build Coastguard Worker # For other servers, guess that they have written bytes to 80*cda5da8dSAndroid Build Coastguard Worker # the environ using stdio byte-oriented interfaces, ending up 81*cda5da8dSAndroid Build Coastguard Worker # with the system code page. 82*cda5da8dSAndroid Build Coastguard Worker else: 83*cda5da8dSAndroid Build Coastguard Worker v = v.encode(enc, 'replace').decode('iso-8859-1') 84*cda5da8dSAndroid Build Coastguard Worker 85*cda5da8dSAndroid Build Coastguard Worker # Recover bytes from unicode environ, using surrogate escapes 86*cda5da8dSAndroid Build Coastguard Worker # where available (Python 3.1+). 87*cda5da8dSAndroid Build Coastguard Worker else: 88*cda5da8dSAndroid Build Coastguard Worker v = v.encode(enc, esc).decode('iso-8859-1') 89*cda5da8dSAndroid Build Coastguard Worker 90*cda5da8dSAndroid Build Coastguard Worker environ[k] = v 91*cda5da8dSAndroid Build Coastguard Worker return environ 92*cda5da8dSAndroid Build Coastguard Worker 93*cda5da8dSAndroid Build Coastguard Worker 94*cda5da8dSAndroid Build Coastguard Workerclass BaseHandler: 95*cda5da8dSAndroid Build Coastguard Worker """Manage the invocation of a WSGI application""" 96*cda5da8dSAndroid Build Coastguard Worker 97*cda5da8dSAndroid Build Coastguard Worker # Configuration parameters; can override per-subclass or per-instance 98*cda5da8dSAndroid Build Coastguard Worker wsgi_version = (1,0) 99*cda5da8dSAndroid Build Coastguard Worker wsgi_multithread = True 100*cda5da8dSAndroid Build Coastguard Worker wsgi_multiprocess = True 101*cda5da8dSAndroid Build Coastguard Worker wsgi_run_once = False 102*cda5da8dSAndroid Build Coastguard Worker 103*cda5da8dSAndroid Build Coastguard Worker origin_server = True # We are transmitting direct to client 104*cda5da8dSAndroid Build Coastguard Worker http_version = "1.0" # Version that should be used for response 105*cda5da8dSAndroid Build Coastguard Worker server_software = None # String name of server software, if any 106*cda5da8dSAndroid Build Coastguard Worker 107*cda5da8dSAndroid Build Coastguard Worker # os_environ is used to supply configuration from the OS environment: 108*cda5da8dSAndroid Build Coastguard Worker # by default it's a copy of 'os.environ' as of import time, but you can 109*cda5da8dSAndroid Build Coastguard Worker # override this in e.g. your __init__ method. 110*cda5da8dSAndroid Build Coastguard Worker os_environ= read_environ() 111*cda5da8dSAndroid Build Coastguard Worker 112*cda5da8dSAndroid Build Coastguard Worker # Collaborator classes 113*cda5da8dSAndroid Build Coastguard Worker wsgi_file_wrapper = FileWrapper # set to None to disable 114*cda5da8dSAndroid Build Coastguard Worker headers_class = Headers # must be a Headers-like class 115*cda5da8dSAndroid Build Coastguard Worker 116*cda5da8dSAndroid Build Coastguard Worker # Error handling (also per-subclass or per-instance) 117*cda5da8dSAndroid Build Coastguard Worker traceback_limit = None # Print entire traceback to self.get_stderr() 118*cda5da8dSAndroid Build Coastguard Worker error_status = "500 Internal Server Error" 119*cda5da8dSAndroid Build Coastguard Worker error_headers = [('Content-Type','text/plain')] 120*cda5da8dSAndroid Build Coastguard Worker error_body = b"A server error occurred. Please contact the administrator." 121*cda5da8dSAndroid Build Coastguard Worker 122*cda5da8dSAndroid Build Coastguard Worker # State variables (don't mess with these) 123*cda5da8dSAndroid Build Coastguard Worker status = result = None 124*cda5da8dSAndroid Build Coastguard Worker headers_sent = False 125*cda5da8dSAndroid Build Coastguard Worker headers = None 126*cda5da8dSAndroid Build Coastguard Worker bytes_sent = 0 127*cda5da8dSAndroid Build Coastguard Worker 128*cda5da8dSAndroid Build Coastguard Worker def run(self, application): 129*cda5da8dSAndroid Build Coastguard Worker """Invoke the application""" 130*cda5da8dSAndroid Build Coastguard Worker # Note to self: don't move the close()! Asynchronous servers shouldn't 131*cda5da8dSAndroid Build Coastguard Worker # call close() from finish_response(), so if you close() anywhere but 132*cda5da8dSAndroid Build Coastguard Worker # the double-error branch here, you'll break asynchronous servers by 133*cda5da8dSAndroid Build Coastguard Worker # prematurely closing. Async servers must return from 'run()' without 134*cda5da8dSAndroid Build Coastguard Worker # closing if there might still be output to iterate over. 135*cda5da8dSAndroid Build Coastguard Worker try: 136*cda5da8dSAndroid Build Coastguard Worker self.setup_environ() 137*cda5da8dSAndroid Build Coastguard Worker self.result = application(self.environ, self.start_response) 138*cda5da8dSAndroid Build Coastguard Worker self.finish_response() 139*cda5da8dSAndroid Build Coastguard Worker except (ConnectionAbortedError, BrokenPipeError, ConnectionResetError): 140*cda5da8dSAndroid Build Coastguard Worker # We expect the client to close the connection abruptly from time 141*cda5da8dSAndroid Build Coastguard Worker # to time. 142*cda5da8dSAndroid Build Coastguard Worker return 143*cda5da8dSAndroid Build Coastguard Worker except: 144*cda5da8dSAndroid Build Coastguard Worker try: 145*cda5da8dSAndroid Build Coastguard Worker self.handle_error() 146*cda5da8dSAndroid Build Coastguard Worker except: 147*cda5da8dSAndroid Build Coastguard Worker # If we get an error handling an error, just give up already! 148*cda5da8dSAndroid Build Coastguard Worker self.close() 149*cda5da8dSAndroid Build Coastguard Worker raise # ...and let the actual server figure it out. 150*cda5da8dSAndroid Build Coastguard Worker 151*cda5da8dSAndroid Build Coastguard Worker 152*cda5da8dSAndroid Build Coastguard Worker def setup_environ(self): 153*cda5da8dSAndroid Build Coastguard Worker """Set up the environment for one request""" 154*cda5da8dSAndroid Build Coastguard Worker 155*cda5da8dSAndroid Build Coastguard Worker env = self.environ = self.os_environ.copy() 156*cda5da8dSAndroid Build Coastguard Worker self.add_cgi_vars() 157*cda5da8dSAndroid Build Coastguard Worker 158*cda5da8dSAndroid Build Coastguard Worker env['wsgi.input'] = self.get_stdin() 159*cda5da8dSAndroid Build Coastguard Worker env['wsgi.errors'] = self.get_stderr() 160*cda5da8dSAndroid Build Coastguard Worker env['wsgi.version'] = self.wsgi_version 161*cda5da8dSAndroid Build Coastguard Worker env['wsgi.run_once'] = self.wsgi_run_once 162*cda5da8dSAndroid Build Coastguard Worker env['wsgi.url_scheme'] = self.get_scheme() 163*cda5da8dSAndroid Build Coastguard Worker env['wsgi.multithread'] = self.wsgi_multithread 164*cda5da8dSAndroid Build Coastguard Worker env['wsgi.multiprocess'] = self.wsgi_multiprocess 165*cda5da8dSAndroid Build Coastguard Worker 166*cda5da8dSAndroid Build Coastguard Worker if self.wsgi_file_wrapper is not None: 167*cda5da8dSAndroid Build Coastguard Worker env['wsgi.file_wrapper'] = self.wsgi_file_wrapper 168*cda5da8dSAndroid Build Coastguard Worker 169*cda5da8dSAndroid Build Coastguard Worker if self.origin_server and self.server_software: 170*cda5da8dSAndroid Build Coastguard Worker env.setdefault('SERVER_SOFTWARE',self.server_software) 171*cda5da8dSAndroid Build Coastguard Worker 172*cda5da8dSAndroid Build Coastguard Worker 173*cda5da8dSAndroid Build Coastguard Worker def finish_response(self): 174*cda5da8dSAndroid Build Coastguard Worker """Send any iterable data, then close self and the iterable 175*cda5da8dSAndroid Build Coastguard Worker 176*cda5da8dSAndroid Build Coastguard Worker Subclasses intended for use in asynchronous servers will 177*cda5da8dSAndroid Build Coastguard Worker want to redefine this method, such that it sets up callbacks 178*cda5da8dSAndroid Build Coastguard Worker in the event loop to iterate over the data, and to call 179*cda5da8dSAndroid Build Coastguard Worker 'self.close()' once the response is finished. 180*cda5da8dSAndroid Build Coastguard Worker """ 181*cda5da8dSAndroid Build Coastguard Worker try: 182*cda5da8dSAndroid Build Coastguard Worker if not self.result_is_file() or not self.sendfile(): 183*cda5da8dSAndroid Build Coastguard Worker for data in self.result: 184*cda5da8dSAndroid Build Coastguard Worker self.write(data) 185*cda5da8dSAndroid Build Coastguard Worker self.finish_content() 186*cda5da8dSAndroid Build Coastguard Worker except: 187*cda5da8dSAndroid Build Coastguard Worker # Call close() on the iterable returned by the WSGI application 188*cda5da8dSAndroid Build Coastguard Worker # in case of an exception. 189*cda5da8dSAndroid Build Coastguard Worker if hasattr(self.result, 'close'): 190*cda5da8dSAndroid Build Coastguard Worker self.result.close() 191*cda5da8dSAndroid Build Coastguard Worker raise 192*cda5da8dSAndroid Build Coastguard Worker else: 193*cda5da8dSAndroid Build Coastguard Worker # We only call close() when no exception is raised, because it 194*cda5da8dSAndroid Build Coastguard Worker # will set status, result, headers, and environ fields to None. 195*cda5da8dSAndroid Build Coastguard Worker # See bpo-29183 for more details. 196*cda5da8dSAndroid Build Coastguard Worker self.close() 197*cda5da8dSAndroid Build Coastguard Worker 198*cda5da8dSAndroid Build Coastguard Worker 199*cda5da8dSAndroid Build Coastguard Worker def get_scheme(self): 200*cda5da8dSAndroid Build Coastguard Worker """Return the URL scheme being used""" 201*cda5da8dSAndroid Build Coastguard Worker return guess_scheme(self.environ) 202*cda5da8dSAndroid Build Coastguard Worker 203*cda5da8dSAndroid Build Coastguard Worker 204*cda5da8dSAndroid Build Coastguard Worker def set_content_length(self): 205*cda5da8dSAndroid Build Coastguard Worker """Compute Content-Length or switch to chunked encoding if possible""" 206*cda5da8dSAndroid Build Coastguard Worker try: 207*cda5da8dSAndroid Build Coastguard Worker blocks = len(self.result) 208*cda5da8dSAndroid Build Coastguard Worker except (TypeError,AttributeError,NotImplementedError): 209*cda5da8dSAndroid Build Coastguard Worker pass 210*cda5da8dSAndroid Build Coastguard Worker else: 211*cda5da8dSAndroid Build Coastguard Worker if blocks==1: 212*cda5da8dSAndroid Build Coastguard Worker self.headers['Content-Length'] = str(self.bytes_sent) 213*cda5da8dSAndroid Build Coastguard Worker return 214*cda5da8dSAndroid Build Coastguard Worker # XXX Try for chunked encoding if origin server and client is 1.1 215*cda5da8dSAndroid Build Coastguard Worker 216*cda5da8dSAndroid Build Coastguard Worker 217*cda5da8dSAndroid Build Coastguard Worker def cleanup_headers(self): 218*cda5da8dSAndroid Build Coastguard Worker """Make any necessary header changes or defaults 219*cda5da8dSAndroid Build Coastguard Worker 220*cda5da8dSAndroid Build Coastguard Worker Subclasses can extend this to add other defaults. 221*cda5da8dSAndroid Build Coastguard Worker """ 222*cda5da8dSAndroid Build Coastguard Worker if 'Content-Length' not in self.headers: 223*cda5da8dSAndroid Build Coastguard Worker self.set_content_length() 224*cda5da8dSAndroid Build Coastguard Worker 225*cda5da8dSAndroid Build Coastguard Worker def start_response(self, status, headers,exc_info=None): 226*cda5da8dSAndroid Build Coastguard Worker """'start_response()' callable as specified by PEP 3333""" 227*cda5da8dSAndroid Build Coastguard Worker 228*cda5da8dSAndroid Build Coastguard Worker if exc_info: 229*cda5da8dSAndroid Build Coastguard Worker try: 230*cda5da8dSAndroid Build Coastguard Worker if self.headers_sent: 231*cda5da8dSAndroid Build Coastguard Worker raise 232*cda5da8dSAndroid Build Coastguard Worker finally: 233*cda5da8dSAndroid Build Coastguard Worker exc_info = None # avoid dangling circular ref 234*cda5da8dSAndroid Build Coastguard Worker elif self.headers is not None: 235*cda5da8dSAndroid Build Coastguard Worker raise AssertionError("Headers already set!") 236*cda5da8dSAndroid Build Coastguard Worker 237*cda5da8dSAndroid Build Coastguard Worker self.status = status 238*cda5da8dSAndroid Build Coastguard Worker self.headers = self.headers_class(headers) 239*cda5da8dSAndroid Build Coastguard Worker status = self._convert_string_type(status, "Status") 240*cda5da8dSAndroid Build Coastguard Worker assert len(status)>=4,"Status must be at least 4 characters" 241*cda5da8dSAndroid Build Coastguard Worker assert status[:3].isdigit(), "Status message must begin w/3-digit code" 242*cda5da8dSAndroid Build Coastguard Worker assert status[3]==" ", "Status message must have a space after code" 243*cda5da8dSAndroid Build Coastguard Worker 244*cda5da8dSAndroid Build Coastguard Worker if __debug__: 245*cda5da8dSAndroid Build Coastguard Worker for name, val in headers: 246*cda5da8dSAndroid Build Coastguard Worker name = self._convert_string_type(name, "Header name") 247*cda5da8dSAndroid Build Coastguard Worker val = self._convert_string_type(val, "Header value") 248*cda5da8dSAndroid Build Coastguard Worker assert not is_hop_by_hop(name),\ 249*cda5da8dSAndroid Build Coastguard Worker f"Hop-by-hop header, '{name}: {val}', not allowed" 250*cda5da8dSAndroid Build Coastguard Worker 251*cda5da8dSAndroid Build Coastguard Worker return self.write 252*cda5da8dSAndroid Build Coastguard Worker 253*cda5da8dSAndroid Build Coastguard Worker def _convert_string_type(self, value, title): 254*cda5da8dSAndroid Build Coastguard Worker """Convert/check value type.""" 255*cda5da8dSAndroid Build Coastguard Worker if type(value) is str: 256*cda5da8dSAndroid Build Coastguard Worker return value 257*cda5da8dSAndroid Build Coastguard Worker raise AssertionError( 258*cda5da8dSAndroid Build Coastguard Worker "{0} must be of type str (got {1})".format(title, repr(value)) 259*cda5da8dSAndroid Build Coastguard Worker ) 260*cda5da8dSAndroid Build Coastguard Worker 261*cda5da8dSAndroid Build Coastguard Worker def send_preamble(self): 262*cda5da8dSAndroid Build Coastguard Worker """Transmit version/status/date/server, via self._write()""" 263*cda5da8dSAndroid Build Coastguard Worker if self.origin_server: 264*cda5da8dSAndroid Build Coastguard Worker if self.client_is_modern(): 265*cda5da8dSAndroid Build Coastguard Worker self._write(('HTTP/%s %s\r\n' % (self.http_version,self.status)).encode('iso-8859-1')) 266*cda5da8dSAndroid Build Coastguard Worker if 'Date' not in self.headers: 267*cda5da8dSAndroid Build Coastguard Worker self._write( 268*cda5da8dSAndroid Build Coastguard Worker ('Date: %s\r\n' % format_date_time(time.time())).encode('iso-8859-1') 269*cda5da8dSAndroid Build Coastguard Worker ) 270*cda5da8dSAndroid Build Coastguard Worker if self.server_software and 'Server' not in self.headers: 271*cda5da8dSAndroid Build Coastguard Worker self._write(('Server: %s\r\n' % self.server_software).encode('iso-8859-1')) 272*cda5da8dSAndroid Build Coastguard Worker else: 273*cda5da8dSAndroid Build Coastguard Worker self._write(('Status: %s\r\n' % self.status).encode('iso-8859-1')) 274*cda5da8dSAndroid Build Coastguard Worker 275*cda5da8dSAndroid Build Coastguard Worker def write(self, data): 276*cda5da8dSAndroid Build Coastguard Worker """'write()' callable as specified by PEP 3333""" 277*cda5da8dSAndroid Build Coastguard Worker 278*cda5da8dSAndroid Build Coastguard Worker assert type(data) is bytes, \ 279*cda5da8dSAndroid Build Coastguard Worker "write() argument must be a bytes instance" 280*cda5da8dSAndroid Build Coastguard Worker 281*cda5da8dSAndroid Build Coastguard Worker if not self.status: 282*cda5da8dSAndroid Build Coastguard Worker raise AssertionError("write() before start_response()") 283*cda5da8dSAndroid Build Coastguard Worker 284*cda5da8dSAndroid Build Coastguard Worker elif not self.headers_sent: 285*cda5da8dSAndroid Build Coastguard Worker # Before the first output, send the stored headers 286*cda5da8dSAndroid Build Coastguard Worker self.bytes_sent = len(data) # make sure we know content-length 287*cda5da8dSAndroid Build Coastguard Worker self.send_headers() 288*cda5da8dSAndroid Build Coastguard Worker else: 289*cda5da8dSAndroid Build Coastguard Worker self.bytes_sent += len(data) 290*cda5da8dSAndroid Build Coastguard Worker 291*cda5da8dSAndroid Build Coastguard Worker # XXX check Content-Length and truncate if too many bytes written? 292*cda5da8dSAndroid Build Coastguard Worker self._write(data) 293*cda5da8dSAndroid Build Coastguard Worker self._flush() 294*cda5da8dSAndroid Build Coastguard Worker 295*cda5da8dSAndroid Build Coastguard Worker 296*cda5da8dSAndroid Build Coastguard Worker def sendfile(self): 297*cda5da8dSAndroid Build Coastguard Worker """Platform-specific file transmission 298*cda5da8dSAndroid Build Coastguard Worker 299*cda5da8dSAndroid Build Coastguard Worker Override this method in subclasses to support platform-specific 300*cda5da8dSAndroid Build Coastguard Worker file transmission. It is only called if the application's 301*cda5da8dSAndroid Build Coastguard Worker return iterable ('self.result') is an instance of 302*cda5da8dSAndroid Build Coastguard Worker 'self.wsgi_file_wrapper'. 303*cda5da8dSAndroid Build Coastguard Worker 304*cda5da8dSAndroid Build Coastguard Worker This method should return a true value if it was able to actually 305*cda5da8dSAndroid Build Coastguard Worker transmit the wrapped file-like object using a platform-specific 306*cda5da8dSAndroid Build Coastguard Worker approach. It should return a false value if normal iteration 307*cda5da8dSAndroid Build Coastguard Worker should be used instead. An exception can be raised to indicate 308*cda5da8dSAndroid Build Coastguard Worker that transmission was attempted, but failed. 309*cda5da8dSAndroid Build Coastguard Worker 310*cda5da8dSAndroid Build Coastguard Worker NOTE: this method should call 'self.send_headers()' if 311*cda5da8dSAndroid Build Coastguard Worker 'self.headers_sent' is false and it is going to attempt direct 312*cda5da8dSAndroid Build Coastguard Worker transmission of the file. 313*cda5da8dSAndroid Build Coastguard Worker """ 314*cda5da8dSAndroid Build Coastguard Worker return False # No platform-specific transmission by default 315*cda5da8dSAndroid Build Coastguard Worker 316*cda5da8dSAndroid Build Coastguard Worker 317*cda5da8dSAndroid Build Coastguard Worker def finish_content(self): 318*cda5da8dSAndroid Build Coastguard Worker """Ensure headers and content have both been sent""" 319*cda5da8dSAndroid Build Coastguard Worker if not self.headers_sent: 320*cda5da8dSAndroid Build Coastguard Worker # Only zero Content-Length if not set by the application (so 321*cda5da8dSAndroid Build Coastguard Worker # that HEAD requests can be satisfied properly, see #3839) 322*cda5da8dSAndroid Build Coastguard Worker self.headers.setdefault('Content-Length', "0") 323*cda5da8dSAndroid Build Coastguard Worker self.send_headers() 324*cda5da8dSAndroid Build Coastguard Worker else: 325*cda5da8dSAndroid Build Coastguard Worker pass # XXX check if content-length was too short? 326*cda5da8dSAndroid Build Coastguard Worker 327*cda5da8dSAndroid Build Coastguard Worker def close(self): 328*cda5da8dSAndroid Build Coastguard Worker """Close the iterable (if needed) and reset all instance vars 329*cda5da8dSAndroid Build Coastguard Worker 330*cda5da8dSAndroid Build Coastguard Worker Subclasses may want to also drop the client connection. 331*cda5da8dSAndroid Build Coastguard Worker """ 332*cda5da8dSAndroid Build Coastguard Worker try: 333*cda5da8dSAndroid Build Coastguard Worker if hasattr(self.result,'close'): 334*cda5da8dSAndroid Build Coastguard Worker self.result.close() 335*cda5da8dSAndroid Build Coastguard Worker finally: 336*cda5da8dSAndroid Build Coastguard Worker self.result = self.headers = self.status = self.environ = None 337*cda5da8dSAndroid Build Coastguard Worker self.bytes_sent = 0; self.headers_sent = False 338*cda5da8dSAndroid Build Coastguard Worker 339*cda5da8dSAndroid Build Coastguard Worker 340*cda5da8dSAndroid Build Coastguard Worker def send_headers(self): 341*cda5da8dSAndroid Build Coastguard Worker """Transmit headers to the client, via self._write()""" 342*cda5da8dSAndroid Build Coastguard Worker self.cleanup_headers() 343*cda5da8dSAndroid Build Coastguard Worker self.headers_sent = True 344*cda5da8dSAndroid Build Coastguard Worker if not self.origin_server or self.client_is_modern(): 345*cda5da8dSAndroid Build Coastguard Worker self.send_preamble() 346*cda5da8dSAndroid Build Coastguard Worker self._write(bytes(self.headers)) 347*cda5da8dSAndroid Build Coastguard Worker 348*cda5da8dSAndroid Build Coastguard Worker 349*cda5da8dSAndroid Build Coastguard Worker def result_is_file(self): 350*cda5da8dSAndroid Build Coastguard Worker """True if 'self.result' is an instance of 'self.wsgi_file_wrapper'""" 351*cda5da8dSAndroid Build Coastguard Worker wrapper = self.wsgi_file_wrapper 352*cda5da8dSAndroid Build Coastguard Worker return wrapper is not None and isinstance(self.result,wrapper) 353*cda5da8dSAndroid Build Coastguard Worker 354*cda5da8dSAndroid Build Coastguard Worker 355*cda5da8dSAndroid Build Coastguard Worker def client_is_modern(self): 356*cda5da8dSAndroid Build Coastguard Worker """True if client can accept status and headers""" 357*cda5da8dSAndroid Build Coastguard Worker return self.environ['SERVER_PROTOCOL'].upper() != 'HTTP/0.9' 358*cda5da8dSAndroid Build Coastguard Worker 359*cda5da8dSAndroid Build Coastguard Worker 360*cda5da8dSAndroid Build Coastguard Worker def log_exception(self,exc_info): 361*cda5da8dSAndroid Build Coastguard Worker """Log the 'exc_info' tuple in the server log 362*cda5da8dSAndroid Build Coastguard Worker 363*cda5da8dSAndroid Build Coastguard Worker Subclasses may override to retarget the output or change its format. 364*cda5da8dSAndroid Build Coastguard Worker """ 365*cda5da8dSAndroid Build Coastguard Worker try: 366*cda5da8dSAndroid Build Coastguard Worker from traceback import print_exception 367*cda5da8dSAndroid Build Coastguard Worker stderr = self.get_stderr() 368*cda5da8dSAndroid Build Coastguard Worker print_exception( 369*cda5da8dSAndroid Build Coastguard Worker exc_info[0], exc_info[1], exc_info[2], 370*cda5da8dSAndroid Build Coastguard Worker self.traceback_limit, stderr 371*cda5da8dSAndroid Build Coastguard Worker ) 372*cda5da8dSAndroid Build Coastguard Worker stderr.flush() 373*cda5da8dSAndroid Build Coastguard Worker finally: 374*cda5da8dSAndroid Build Coastguard Worker exc_info = None 375*cda5da8dSAndroid Build Coastguard Worker 376*cda5da8dSAndroid Build Coastguard Worker def handle_error(self): 377*cda5da8dSAndroid Build Coastguard Worker """Log current error, and send error output to client if possible""" 378*cda5da8dSAndroid Build Coastguard Worker self.log_exception(sys.exc_info()) 379*cda5da8dSAndroid Build Coastguard Worker if not self.headers_sent: 380*cda5da8dSAndroid Build Coastguard Worker self.result = self.error_output(self.environ, self.start_response) 381*cda5da8dSAndroid Build Coastguard Worker self.finish_response() 382*cda5da8dSAndroid Build Coastguard Worker # XXX else: attempt advanced recovery techniques for HTML or text? 383*cda5da8dSAndroid Build Coastguard Worker 384*cda5da8dSAndroid Build Coastguard Worker def error_output(self, environ, start_response): 385*cda5da8dSAndroid Build Coastguard Worker """WSGI mini-app to create error output 386*cda5da8dSAndroid Build Coastguard Worker 387*cda5da8dSAndroid Build Coastguard Worker By default, this just uses the 'error_status', 'error_headers', 388*cda5da8dSAndroid Build Coastguard Worker and 'error_body' attributes to generate an output page. It can 389*cda5da8dSAndroid Build Coastguard Worker be overridden in a subclass to dynamically generate diagnostics, 390*cda5da8dSAndroid Build Coastguard Worker choose an appropriate message for the user's preferred language, etc. 391*cda5da8dSAndroid Build Coastguard Worker 392*cda5da8dSAndroid Build Coastguard Worker Note, however, that it's not recommended from a security perspective to 393*cda5da8dSAndroid Build Coastguard Worker spit out diagnostics to any old user; ideally, you should have to do 394*cda5da8dSAndroid Build Coastguard Worker something special to enable diagnostic output, which is why we don't 395*cda5da8dSAndroid Build Coastguard Worker include any here! 396*cda5da8dSAndroid Build Coastguard Worker """ 397*cda5da8dSAndroid Build Coastguard Worker start_response(self.error_status,self.error_headers[:],sys.exc_info()) 398*cda5da8dSAndroid Build Coastguard Worker return [self.error_body] 399*cda5da8dSAndroid Build Coastguard Worker 400*cda5da8dSAndroid Build Coastguard Worker 401*cda5da8dSAndroid Build Coastguard Worker # Pure abstract methods; *must* be overridden in subclasses 402*cda5da8dSAndroid Build Coastguard Worker 403*cda5da8dSAndroid Build Coastguard Worker def _write(self,data): 404*cda5da8dSAndroid Build Coastguard Worker """Override in subclass to buffer data for send to client 405*cda5da8dSAndroid Build Coastguard Worker 406*cda5da8dSAndroid Build Coastguard Worker It's okay if this method actually transmits the data; BaseHandler 407*cda5da8dSAndroid Build Coastguard Worker just separates write and flush operations for greater efficiency 408*cda5da8dSAndroid Build Coastguard Worker when the underlying system actually has such a distinction. 409*cda5da8dSAndroid Build Coastguard Worker """ 410*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError 411*cda5da8dSAndroid Build Coastguard Worker 412*cda5da8dSAndroid Build Coastguard Worker def _flush(self): 413*cda5da8dSAndroid Build Coastguard Worker """Override in subclass to force sending of recent '_write()' calls 414*cda5da8dSAndroid Build Coastguard Worker 415*cda5da8dSAndroid Build Coastguard Worker It's okay if this method is a no-op (i.e., if '_write()' actually 416*cda5da8dSAndroid Build Coastguard Worker sends the data. 417*cda5da8dSAndroid Build Coastguard Worker """ 418*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError 419*cda5da8dSAndroid Build Coastguard Worker 420*cda5da8dSAndroid Build Coastguard Worker def get_stdin(self): 421*cda5da8dSAndroid Build Coastguard Worker """Override in subclass to return suitable 'wsgi.input'""" 422*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError 423*cda5da8dSAndroid Build Coastguard Worker 424*cda5da8dSAndroid Build Coastguard Worker def get_stderr(self): 425*cda5da8dSAndroid Build Coastguard Worker """Override in subclass to return suitable 'wsgi.errors'""" 426*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError 427*cda5da8dSAndroid Build Coastguard Worker 428*cda5da8dSAndroid Build Coastguard Worker def add_cgi_vars(self): 429*cda5da8dSAndroid Build Coastguard Worker """Override in subclass to insert CGI variables in 'self.environ'""" 430*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError 431*cda5da8dSAndroid Build Coastguard Worker 432*cda5da8dSAndroid Build Coastguard Worker 433*cda5da8dSAndroid Build Coastguard Workerclass SimpleHandler(BaseHandler): 434*cda5da8dSAndroid Build Coastguard Worker """Handler that's just initialized with streams, environment, etc. 435*cda5da8dSAndroid Build Coastguard Worker 436*cda5da8dSAndroid Build Coastguard Worker This handler subclass is intended for synchronous HTTP/1.0 origin servers, 437*cda5da8dSAndroid Build Coastguard Worker and handles sending the entire response output, given the correct inputs. 438*cda5da8dSAndroid Build Coastguard Worker 439*cda5da8dSAndroid Build Coastguard Worker Usage:: 440*cda5da8dSAndroid Build Coastguard Worker 441*cda5da8dSAndroid Build Coastguard Worker handler = SimpleHandler( 442*cda5da8dSAndroid Build Coastguard Worker inp,out,err,env, multithread=False, multiprocess=True 443*cda5da8dSAndroid Build Coastguard Worker ) 444*cda5da8dSAndroid Build Coastguard Worker handler.run(app)""" 445*cda5da8dSAndroid Build Coastguard Worker 446*cda5da8dSAndroid Build Coastguard Worker def __init__(self,stdin,stdout,stderr,environ, 447*cda5da8dSAndroid Build Coastguard Worker multithread=True, multiprocess=False 448*cda5da8dSAndroid Build Coastguard Worker ): 449*cda5da8dSAndroid Build Coastguard Worker self.stdin = stdin 450*cda5da8dSAndroid Build Coastguard Worker self.stdout = stdout 451*cda5da8dSAndroid Build Coastguard Worker self.stderr = stderr 452*cda5da8dSAndroid Build Coastguard Worker self.base_env = environ 453*cda5da8dSAndroid Build Coastguard Worker self.wsgi_multithread = multithread 454*cda5da8dSAndroid Build Coastguard Worker self.wsgi_multiprocess = multiprocess 455*cda5da8dSAndroid Build Coastguard Worker 456*cda5da8dSAndroid Build Coastguard Worker def get_stdin(self): 457*cda5da8dSAndroid Build Coastguard Worker return self.stdin 458*cda5da8dSAndroid Build Coastguard Worker 459*cda5da8dSAndroid Build Coastguard Worker def get_stderr(self): 460*cda5da8dSAndroid Build Coastguard Worker return self.stderr 461*cda5da8dSAndroid Build Coastguard Worker 462*cda5da8dSAndroid Build Coastguard Worker def add_cgi_vars(self): 463*cda5da8dSAndroid Build Coastguard Worker self.environ.update(self.base_env) 464*cda5da8dSAndroid Build Coastguard Worker 465*cda5da8dSAndroid Build Coastguard Worker def _write(self,data): 466*cda5da8dSAndroid Build Coastguard Worker result = self.stdout.write(data) 467*cda5da8dSAndroid Build Coastguard Worker if result is None or result == len(data): 468*cda5da8dSAndroid Build Coastguard Worker return 469*cda5da8dSAndroid Build Coastguard Worker from warnings import warn 470*cda5da8dSAndroid Build Coastguard Worker warn("SimpleHandler.stdout.write() should not do partial writes", 471*cda5da8dSAndroid Build Coastguard Worker DeprecationWarning) 472*cda5da8dSAndroid Build Coastguard Worker while True: 473*cda5da8dSAndroid Build Coastguard Worker data = data[result:] 474*cda5da8dSAndroid Build Coastguard Worker if not data: 475*cda5da8dSAndroid Build Coastguard Worker break 476*cda5da8dSAndroid Build Coastguard Worker result = self.stdout.write(data) 477*cda5da8dSAndroid Build Coastguard Worker 478*cda5da8dSAndroid Build Coastguard Worker def _flush(self): 479*cda5da8dSAndroid Build Coastguard Worker self.stdout.flush() 480*cda5da8dSAndroid Build Coastguard Worker self._flush = self.stdout.flush 481*cda5da8dSAndroid Build Coastguard Worker 482*cda5da8dSAndroid Build Coastguard Worker 483*cda5da8dSAndroid Build Coastguard Workerclass BaseCGIHandler(SimpleHandler): 484*cda5da8dSAndroid Build Coastguard Worker 485*cda5da8dSAndroid Build Coastguard Worker """CGI-like systems using input/output/error streams and environ mapping 486*cda5da8dSAndroid Build Coastguard Worker 487*cda5da8dSAndroid Build Coastguard Worker Usage:: 488*cda5da8dSAndroid Build Coastguard Worker 489*cda5da8dSAndroid Build Coastguard Worker handler = BaseCGIHandler(inp,out,err,env) 490*cda5da8dSAndroid Build Coastguard Worker handler.run(app) 491*cda5da8dSAndroid Build Coastguard Worker 492*cda5da8dSAndroid Build Coastguard Worker This handler class is useful for gateway protocols like ReadyExec and 493*cda5da8dSAndroid Build Coastguard Worker FastCGI, that have usable input/output/error streams and an environment 494*cda5da8dSAndroid Build Coastguard Worker mapping. It's also the base class for CGIHandler, which just uses 495*cda5da8dSAndroid Build Coastguard Worker sys.stdin, os.environ, and so on. 496*cda5da8dSAndroid Build Coastguard Worker 497*cda5da8dSAndroid Build Coastguard Worker The constructor also takes keyword arguments 'multithread' and 498*cda5da8dSAndroid Build Coastguard Worker 'multiprocess' (defaulting to 'True' and 'False' respectively) to control 499*cda5da8dSAndroid Build Coastguard Worker the configuration sent to the application. It sets 'origin_server' to 500*cda5da8dSAndroid Build Coastguard Worker False (to enable CGI-like output), and assumes that 'wsgi.run_once' is 501*cda5da8dSAndroid Build Coastguard Worker False. 502*cda5da8dSAndroid Build Coastguard Worker """ 503*cda5da8dSAndroid Build Coastguard Worker 504*cda5da8dSAndroid Build Coastguard Worker origin_server = False 505*cda5da8dSAndroid Build Coastguard Worker 506*cda5da8dSAndroid Build Coastguard Worker 507*cda5da8dSAndroid Build Coastguard Workerclass CGIHandler(BaseCGIHandler): 508*cda5da8dSAndroid Build Coastguard Worker 509*cda5da8dSAndroid Build Coastguard Worker """CGI-based invocation via sys.stdin/stdout/stderr and os.environ 510*cda5da8dSAndroid Build Coastguard Worker 511*cda5da8dSAndroid Build Coastguard Worker Usage:: 512*cda5da8dSAndroid Build Coastguard Worker 513*cda5da8dSAndroid Build Coastguard Worker CGIHandler().run(app) 514*cda5da8dSAndroid Build Coastguard Worker 515*cda5da8dSAndroid Build Coastguard Worker The difference between this class and BaseCGIHandler is that it always 516*cda5da8dSAndroid Build Coastguard Worker uses 'wsgi.run_once' of 'True', 'wsgi.multithread' of 'False', and 517*cda5da8dSAndroid Build Coastguard Worker 'wsgi.multiprocess' of 'True'. It does not take any initialization 518*cda5da8dSAndroid Build Coastguard Worker parameters, but always uses 'sys.stdin', 'os.environ', and friends. 519*cda5da8dSAndroid Build Coastguard Worker 520*cda5da8dSAndroid Build Coastguard Worker If you need to override any of these parameters, use BaseCGIHandler 521*cda5da8dSAndroid Build Coastguard Worker instead. 522*cda5da8dSAndroid Build Coastguard Worker """ 523*cda5da8dSAndroid Build Coastguard Worker 524*cda5da8dSAndroid Build Coastguard Worker wsgi_run_once = True 525*cda5da8dSAndroid Build Coastguard Worker # Do not allow os.environ to leak between requests in Google App Engine 526*cda5da8dSAndroid Build Coastguard Worker # and other multi-run CGI use cases. This is not easily testable. 527*cda5da8dSAndroid Build Coastguard Worker # See http://bugs.python.org/issue7250 528*cda5da8dSAndroid Build Coastguard Worker os_environ = {} 529*cda5da8dSAndroid Build Coastguard Worker 530*cda5da8dSAndroid Build Coastguard Worker def __init__(self): 531*cda5da8dSAndroid Build Coastguard Worker BaseCGIHandler.__init__( 532*cda5da8dSAndroid Build Coastguard Worker self, sys.stdin.buffer, sys.stdout.buffer, sys.stderr, 533*cda5da8dSAndroid Build Coastguard Worker read_environ(), multithread=False, multiprocess=True 534*cda5da8dSAndroid Build Coastguard Worker ) 535*cda5da8dSAndroid Build Coastguard Worker 536*cda5da8dSAndroid Build Coastguard Worker 537*cda5da8dSAndroid Build Coastguard Workerclass IISCGIHandler(BaseCGIHandler): 538*cda5da8dSAndroid Build Coastguard Worker """CGI-based invocation with workaround for IIS path bug 539*cda5da8dSAndroid Build Coastguard Worker 540*cda5da8dSAndroid Build Coastguard Worker This handler should be used in preference to CGIHandler when deploying on 541*cda5da8dSAndroid Build Coastguard Worker Microsoft IIS without having set the config allowPathInfo option (IIS>=7) 542*cda5da8dSAndroid Build Coastguard Worker or metabase allowPathInfoForScriptMappings (IIS<7). 543*cda5da8dSAndroid Build Coastguard Worker """ 544*cda5da8dSAndroid Build Coastguard Worker wsgi_run_once = True 545*cda5da8dSAndroid Build Coastguard Worker os_environ = {} 546*cda5da8dSAndroid Build Coastguard Worker 547*cda5da8dSAndroid Build Coastguard Worker # By default, IIS gives a PATH_INFO that duplicates the SCRIPT_NAME at 548*cda5da8dSAndroid Build Coastguard Worker # the front, causing problems for WSGI applications that wish to implement 549*cda5da8dSAndroid Build Coastguard Worker # routing. This handler strips any such duplicated path. 550*cda5da8dSAndroid Build Coastguard Worker 551*cda5da8dSAndroid Build Coastguard Worker # IIS can be configured to pass the correct PATH_INFO, but this causes 552*cda5da8dSAndroid Build Coastguard Worker # another bug where PATH_TRANSLATED is wrong. Luckily this variable is 553*cda5da8dSAndroid Build Coastguard Worker # rarely used and is not guaranteed by WSGI. On IIS<7, though, the 554*cda5da8dSAndroid Build Coastguard Worker # setting can only be made on a vhost level, affecting all other script 555*cda5da8dSAndroid Build Coastguard Worker # mappings, many of which break when exposed to the PATH_TRANSLATED bug. 556*cda5da8dSAndroid Build Coastguard Worker # For this reason IIS<7 is almost never deployed with the fix. (Even IIS7 557*cda5da8dSAndroid Build Coastguard Worker # rarely uses it because there is still no UI for it.) 558*cda5da8dSAndroid Build Coastguard Worker 559*cda5da8dSAndroid Build Coastguard Worker # There is no way for CGI code to tell whether the option was set, so a 560*cda5da8dSAndroid Build Coastguard Worker # separate handler class is provided. 561*cda5da8dSAndroid Build Coastguard Worker def __init__(self): 562*cda5da8dSAndroid Build Coastguard Worker environ= read_environ() 563*cda5da8dSAndroid Build Coastguard Worker path = environ.get('PATH_INFO', '') 564*cda5da8dSAndroid Build Coastguard Worker script = environ.get('SCRIPT_NAME', '') 565*cda5da8dSAndroid Build Coastguard Worker if (path+'/').startswith(script+'/'): 566*cda5da8dSAndroid Build Coastguard Worker environ['PATH_INFO'] = path[len(script):] 567*cda5da8dSAndroid Build Coastguard Worker BaseCGIHandler.__init__( 568*cda5da8dSAndroid Build Coastguard Worker self, sys.stdin.buffer, sys.stdout.buffer, sys.stderr, 569*cda5da8dSAndroid Build Coastguard Worker environ, multithread=False, multiprocess=True 570*cda5da8dSAndroid Build Coastguard Worker ) 571