1*cda5da8dSAndroid Build Coastguard Worker# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) 2*cda5da8dSAndroid Build Coastguard Worker# Licensed under the MIT license: https://opensource.org/licenses/mit-license.php 3*cda5da8dSAndroid Build Coastguard Worker# Also licenced under the Apache License, 2.0: https://opensource.org/licenses/apache2.0.php 4*cda5da8dSAndroid Build Coastguard Worker# Licensed to PSF under a Contributor Agreement 5*cda5da8dSAndroid Build Coastguard Worker""" 6*cda5da8dSAndroid Build Coastguard WorkerMiddleware to check for obedience to the WSGI specification. 7*cda5da8dSAndroid Build Coastguard Worker 8*cda5da8dSAndroid Build Coastguard WorkerSome of the things this checks: 9*cda5da8dSAndroid Build Coastguard Worker 10*cda5da8dSAndroid Build Coastguard Worker* Signature of the application and start_response (including that 11*cda5da8dSAndroid Build Coastguard Worker keyword arguments are not used). 12*cda5da8dSAndroid Build Coastguard Worker 13*cda5da8dSAndroid Build Coastguard Worker* Environment checks: 14*cda5da8dSAndroid Build Coastguard Worker 15*cda5da8dSAndroid Build Coastguard Worker - Environment is a dictionary (and not a subclass). 16*cda5da8dSAndroid Build Coastguard Worker 17*cda5da8dSAndroid Build Coastguard Worker - That all the required keys are in the environment: REQUEST_METHOD, 18*cda5da8dSAndroid Build Coastguard Worker SERVER_NAME, SERVER_PORT, wsgi.version, wsgi.input, wsgi.errors, 19*cda5da8dSAndroid Build Coastguard Worker wsgi.multithread, wsgi.multiprocess, wsgi.run_once 20*cda5da8dSAndroid Build Coastguard Worker 21*cda5da8dSAndroid Build Coastguard Worker - That HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH are not in the 22*cda5da8dSAndroid Build Coastguard Worker environment (these headers should appear as CONTENT_LENGTH and 23*cda5da8dSAndroid Build Coastguard Worker CONTENT_TYPE). 24*cda5da8dSAndroid Build Coastguard Worker 25*cda5da8dSAndroid Build Coastguard Worker - Warns if QUERY_STRING is missing, as the cgi module acts 26*cda5da8dSAndroid Build Coastguard Worker unpredictably in that case. 27*cda5da8dSAndroid Build Coastguard Worker 28*cda5da8dSAndroid Build Coastguard Worker - That CGI-style variables (that don't contain a .) have 29*cda5da8dSAndroid Build Coastguard Worker (non-unicode) string values 30*cda5da8dSAndroid Build Coastguard Worker 31*cda5da8dSAndroid Build Coastguard Worker - That wsgi.version is a tuple 32*cda5da8dSAndroid Build Coastguard Worker 33*cda5da8dSAndroid Build Coastguard Worker - That wsgi.url_scheme is 'http' or 'https' (@@: is this too 34*cda5da8dSAndroid Build Coastguard Worker restrictive?) 35*cda5da8dSAndroid Build Coastguard Worker 36*cda5da8dSAndroid Build Coastguard Worker - Warns if the REQUEST_METHOD is not known (@@: probably too 37*cda5da8dSAndroid Build Coastguard Worker restrictive). 38*cda5da8dSAndroid Build Coastguard Worker 39*cda5da8dSAndroid Build Coastguard Worker - That SCRIPT_NAME and PATH_INFO are empty or start with / 40*cda5da8dSAndroid Build Coastguard Worker 41*cda5da8dSAndroid Build Coastguard Worker - That at least one of SCRIPT_NAME or PATH_INFO are set. 42*cda5da8dSAndroid Build Coastguard Worker 43*cda5da8dSAndroid Build Coastguard Worker - That CONTENT_LENGTH is a positive integer. 44*cda5da8dSAndroid Build Coastguard Worker 45*cda5da8dSAndroid Build Coastguard Worker - That SCRIPT_NAME is not '/' (it should be '', and PATH_INFO should 46*cda5da8dSAndroid Build Coastguard Worker be '/'). 47*cda5da8dSAndroid Build Coastguard Worker 48*cda5da8dSAndroid Build Coastguard Worker - That wsgi.input has the methods read, readline, readlines, and 49*cda5da8dSAndroid Build Coastguard Worker __iter__ 50*cda5da8dSAndroid Build Coastguard Worker 51*cda5da8dSAndroid Build Coastguard Worker - That wsgi.errors has the methods flush, write, writelines 52*cda5da8dSAndroid Build Coastguard Worker 53*cda5da8dSAndroid Build Coastguard Worker* The status is a string, contains a space, starts with an integer, 54*cda5da8dSAndroid Build Coastguard Worker and that integer is in range (> 100). 55*cda5da8dSAndroid Build Coastguard Worker 56*cda5da8dSAndroid Build Coastguard Worker* That the headers is a list (not a subclass, not another kind of 57*cda5da8dSAndroid Build Coastguard Worker sequence). 58*cda5da8dSAndroid Build Coastguard Worker 59*cda5da8dSAndroid Build Coastguard Worker* That the items of the headers are tuples of strings. 60*cda5da8dSAndroid Build Coastguard Worker 61*cda5da8dSAndroid Build Coastguard Worker* That there is no 'status' header (that is used in CGI, but not in 62*cda5da8dSAndroid Build Coastguard Worker WSGI). 63*cda5da8dSAndroid Build Coastguard Worker 64*cda5da8dSAndroid Build Coastguard Worker* That the headers don't contain newlines or colons, end in _ or -, or 65*cda5da8dSAndroid Build Coastguard Worker contain characters codes below 037. 66*cda5da8dSAndroid Build Coastguard Worker 67*cda5da8dSAndroid Build Coastguard Worker* That Content-Type is given if there is content (CGI often has a 68*cda5da8dSAndroid Build Coastguard Worker default content type, but WSGI does not). 69*cda5da8dSAndroid Build Coastguard Worker 70*cda5da8dSAndroid Build Coastguard Worker* That no Content-Type is given when there is no content (@@: is this 71*cda5da8dSAndroid Build Coastguard Worker too restrictive?) 72*cda5da8dSAndroid Build Coastguard Worker 73*cda5da8dSAndroid Build Coastguard Worker* That the exc_info argument to start_response is a tuple or None. 74*cda5da8dSAndroid Build Coastguard Worker 75*cda5da8dSAndroid Build Coastguard Worker* That all calls to the writer are with strings, and no other methods 76*cda5da8dSAndroid Build Coastguard Worker on the writer are accessed. 77*cda5da8dSAndroid Build Coastguard Worker 78*cda5da8dSAndroid Build Coastguard Worker* That wsgi.input is used properly: 79*cda5da8dSAndroid Build Coastguard Worker 80*cda5da8dSAndroid Build Coastguard Worker - .read() is called with exactly one argument 81*cda5da8dSAndroid Build Coastguard Worker 82*cda5da8dSAndroid Build Coastguard Worker - That it returns a string 83*cda5da8dSAndroid Build Coastguard Worker 84*cda5da8dSAndroid Build Coastguard Worker - That readline, readlines, and __iter__ return strings 85*cda5da8dSAndroid Build Coastguard Worker 86*cda5da8dSAndroid Build Coastguard Worker - That .close() is not called 87*cda5da8dSAndroid Build Coastguard Worker 88*cda5da8dSAndroid Build Coastguard Worker - No other methods are provided 89*cda5da8dSAndroid Build Coastguard Worker 90*cda5da8dSAndroid Build Coastguard Worker* That wsgi.errors is used properly: 91*cda5da8dSAndroid Build Coastguard Worker 92*cda5da8dSAndroid Build Coastguard Worker - .write() and .writelines() is called with a string 93*cda5da8dSAndroid Build Coastguard Worker 94*cda5da8dSAndroid Build Coastguard Worker - That .close() is not called, and no other methods are provided. 95*cda5da8dSAndroid Build Coastguard Worker 96*cda5da8dSAndroid Build Coastguard Worker* The response iterator: 97*cda5da8dSAndroid Build Coastguard Worker 98*cda5da8dSAndroid Build Coastguard Worker - That it is not a string (it should be a list of a single string; a 99*cda5da8dSAndroid Build Coastguard Worker string will work, but perform horribly). 100*cda5da8dSAndroid Build Coastguard Worker 101*cda5da8dSAndroid Build Coastguard Worker - That .__next__() returns a string 102*cda5da8dSAndroid Build Coastguard Worker 103*cda5da8dSAndroid Build Coastguard Worker - That the iterator is not iterated over until start_response has 104*cda5da8dSAndroid Build Coastguard Worker been called (that can signal either a server or application 105*cda5da8dSAndroid Build Coastguard Worker error). 106*cda5da8dSAndroid Build Coastguard Worker 107*cda5da8dSAndroid Build Coastguard Worker - That .close() is called (doesn't raise exception, only prints to 108*cda5da8dSAndroid Build Coastguard Worker sys.stderr, because we only know it isn't called when the object 109*cda5da8dSAndroid Build Coastguard Worker is garbage collected). 110*cda5da8dSAndroid Build Coastguard Worker""" 111*cda5da8dSAndroid Build Coastguard Worker__all__ = ['validator'] 112*cda5da8dSAndroid Build Coastguard Worker 113*cda5da8dSAndroid Build Coastguard Worker 114*cda5da8dSAndroid Build Coastguard Workerimport re 115*cda5da8dSAndroid Build Coastguard Workerimport sys 116*cda5da8dSAndroid Build Coastguard Workerimport warnings 117*cda5da8dSAndroid Build Coastguard Worker 118*cda5da8dSAndroid Build Coastguard Workerheader_re = re.compile(r'^[a-zA-Z][a-zA-Z0-9\-_]*$') 119*cda5da8dSAndroid Build Coastguard Workerbad_header_value_re = re.compile(r'[\000-\037]') 120*cda5da8dSAndroid Build Coastguard Worker 121*cda5da8dSAndroid Build Coastguard Workerclass WSGIWarning(Warning): 122*cda5da8dSAndroid Build Coastguard Worker """ 123*cda5da8dSAndroid Build Coastguard Worker Raised in response to WSGI-spec-related warnings 124*cda5da8dSAndroid Build Coastguard Worker """ 125*cda5da8dSAndroid Build Coastguard Worker 126*cda5da8dSAndroid Build Coastguard Workerdef assert_(cond, *args): 127*cda5da8dSAndroid Build Coastguard Worker if not cond: 128*cda5da8dSAndroid Build Coastguard Worker raise AssertionError(*args) 129*cda5da8dSAndroid Build Coastguard Worker 130*cda5da8dSAndroid Build Coastguard Workerdef check_string_type(value, title): 131*cda5da8dSAndroid Build Coastguard Worker if type (value) is str: 132*cda5da8dSAndroid Build Coastguard Worker return value 133*cda5da8dSAndroid Build Coastguard Worker raise AssertionError( 134*cda5da8dSAndroid Build Coastguard Worker "{0} must be of type str (got {1})".format(title, repr(value))) 135*cda5da8dSAndroid Build Coastguard Worker 136*cda5da8dSAndroid Build Coastguard Workerdef validator(application): 137*cda5da8dSAndroid Build Coastguard Worker 138*cda5da8dSAndroid Build Coastguard Worker """ 139*cda5da8dSAndroid Build Coastguard Worker When applied between a WSGI server and a WSGI application, this 140*cda5da8dSAndroid Build Coastguard Worker middleware will check for WSGI compliance on a number of levels. 141*cda5da8dSAndroid Build Coastguard Worker This middleware does not modify the request or response in any 142*cda5da8dSAndroid Build Coastguard Worker way, but will raise an AssertionError if anything seems off 143*cda5da8dSAndroid Build Coastguard Worker (except for a failure to close the application iterator, which 144*cda5da8dSAndroid Build Coastguard Worker will be printed to stderr -- there's no way to raise an exception 145*cda5da8dSAndroid Build Coastguard Worker at that point). 146*cda5da8dSAndroid Build Coastguard Worker """ 147*cda5da8dSAndroid Build Coastguard Worker 148*cda5da8dSAndroid Build Coastguard Worker def lint_app(*args, **kw): 149*cda5da8dSAndroid Build Coastguard Worker assert_(len(args) == 2, "Two arguments required") 150*cda5da8dSAndroid Build Coastguard Worker assert_(not kw, "No keyword arguments allowed") 151*cda5da8dSAndroid Build Coastguard Worker environ, start_response = args 152*cda5da8dSAndroid Build Coastguard Worker 153*cda5da8dSAndroid Build Coastguard Worker check_environ(environ) 154*cda5da8dSAndroid Build Coastguard Worker 155*cda5da8dSAndroid Build Coastguard Worker # We use this to check if the application returns without 156*cda5da8dSAndroid Build Coastguard Worker # calling start_response: 157*cda5da8dSAndroid Build Coastguard Worker start_response_started = [] 158*cda5da8dSAndroid Build Coastguard Worker 159*cda5da8dSAndroid Build Coastguard Worker def start_response_wrapper(*args, **kw): 160*cda5da8dSAndroid Build Coastguard Worker assert_(len(args) == 2 or len(args) == 3, ( 161*cda5da8dSAndroid Build Coastguard Worker "Invalid number of arguments: %s" % (args,))) 162*cda5da8dSAndroid Build Coastguard Worker assert_(not kw, "No keyword arguments allowed") 163*cda5da8dSAndroid Build Coastguard Worker status = args[0] 164*cda5da8dSAndroid Build Coastguard Worker headers = args[1] 165*cda5da8dSAndroid Build Coastguard Worker if len(args) == 3: 166*cda5da8dSAndroid Build Coastguard Worker exc_info = args[2] 167*cda5da8dSAndroid Build Coastguard Worker else: 168*cda5da8dSAndroid Build Coastguard Worker exc_info = None 169*cda5da8dSAndroid Build Coastguard Worker 170*cda5da8dSAndroid Build Coastguard Worker check_status(status) 171*cda5da8dSAndroid Build Coastguard Worker check_headers(headers) 172*cda5da8dSAndroid Build Coastguard Worker check_content_type(status, headers) 173*cda5da8dSAndroid Build Coastguard Worker check_exc_info(exc_info) 174*cda5da8dSAndroid Build Coastguard Worker 175*cda5da8dSAndroid Build Coastguard Worker start_response_started.append(None) 176*cda5da8dSAndroid Build Coastguard Worker return WriteWrapper(start_response(*args)) 177*cda5da8dSAndroid Build Coastguard Worker 178*cda5da8dSAndroid Build Coastguard Worker environ['wsgi.input'] = InputWrapper(environ['wsgi.input']) 179*cda5da8dSAndroid Build Coastguard Worker environ['wsgi.errors'] = ErrorWrapper(environ['wsgi.errors']) 180*cda5da8dSAndroid Build Coastguard Worker 181*cda5da8dSAndroid Build Coastguard Worker iterator = application(environ, start_response_wrapper) 182*cda5da8dSAndroid Build Coastguard Worker assert_(iterator is not None and iterator != False, 183*cda5da8dSAndroid Build Coastguard Worker "The application must return an iterator, if only an empty list") 184*cda5da8dSAndroid Build Coastguard Worker 185*cda5da8dSAndroid Build Coastguard Worker check_iterator(iterator) 186*cda5da8dSAndroid Build Coastguard Worker 187*cda5da8dSAndroid Build Coastguard Worker return IteratorWrapper(iterator, start_response_started) 188*cda5da8dSAndroid Build Coastguard Worker 189*cda5da8dSAndroid Build Coastguard Worker return lint_app 190*cda5da8dSAndroid Build Coastguard Worker 191*cda5da8dSAndroid Build Coastguard Workerclass InputWrapper: 192*cda5da8dSAndroid Build Coastguard Worker 193*cda5da8dSAndroid Build Coastguard Worker def __init__(self, wsgi_input): 194*cda5da8dSAndroid Build Coastguard Worker self.input = wsgi_input 195*cda5da8dSAndroid Build Coastguard Worker 196*cda5da8dSAndroid Build Coastguard Worker def read(self, *args): 197*cda5da8dSAndroid Build Coastguard Worker assert_(len(args) == 1) 198*cda5da8dSAndroid Build Coastguard Worker v = self.input.read(*args) 199*cda5da8dSAndroid Build Coastguard Worker assert_(type(v) is bytes) 200*cda5da8dSAndroid Build Coastguard Worker return v 201*cda5da8dSAndroid Build Coastguard Worker 202*cda5da8dSAndroid Build Coastguard Worker def readline(self, *args): 203*cda5da8dSAndroid Build Coastguard Worker assert_(len(args) <= 1) 204*cda5da8dSAndroid Build Coastguard Worker v = self.input.readline(*args) 205*cda5da8dSAndroid Build Coastguard Worker assert_(type(v) is bytes) 206*cda5da8dSAndroid Build Coastguard Worker return v 207*cda5da8dSAndroid Build Coastguard Worker 208*cda5da8dSAndroid Build Coastguard Worker def readlines(self, *args): 209*cda5da8dSAndroid Build Coastguard Worker assert_(len(args) <= 1) 210*cda5da8dSAndroid Build Coastguard Worker lines = self.input.readlines(*args) 211*cda5da8dSAndroid Build Coastguard Worker assert_(type(lines) is list) 212*cda5da8dSAndroid Build Coastguard Worker for line in lines: 213*cda5da8dSAndroid Build Coastguard Worker assert_(type(line) is bytes) 214*cda5da8dSAndroid Build Coastguard Worker return lines 215*cda5da8dSAndroid Build Coastguard Worker 216*cda5da8dSAndroid Build Coastguard Worker def __iter__(self): 217*cda5da8dSAndroid Build Coastguard Worker while 1: 218*cda5da8dSAndroid Build Coastguard Worker line = self.readline() 219*cda5da8dSAndroid Build Coastguard Worker if not line: 220*cda5da8dSAndroid Build Coastguard Worker return 221*cda5da8dSAndroid Build Coastguard Worker yield line 222*cda5da8dSAndroid Build Coastguard Worker 223*cda5da8dSAndroid Build Coastguard Worker def close(self): 224*cda5da8dSAndroid Build Coastguard Worker assert_(0, "input.close() must not be called") 225*cda5da8dSAndroid Build Coastguard Worker 226*cda5da8dSAndroid Build Coastguard Workerclass ErrorWrapper: 227*cda5da8dSAndroid Build Coastguard Worker 228*cda5da8dSAndroid Build Coastguard Worker def __init__(self, wsgi_errors): 229*cda5da8dSAndroid Build Coastguard Worker self.errors = wsgi_errors 230*cda5da8dSAndroid Build Coastguard Worker 231*cda5da8dSAndroid Build Coastguard Worker def write(self, s): 232*cda5da8dSAndroid Build Coastguard Worker assert_(type(s) is str) 233*cda5da8dSAndroid Build Coastguard Worker self.errors.write(s) 234*cda5da8dSAndroid Build Coastguard Worker 235*cda5da8dSAndroid Build Coastguard Worker def flush(self): 236*cda5da8dSAndroid Build Coastguard Worker self.errors.flush() 237*cda5da8dSAndroid Build Coastguard Worker 238*cda5da8dSAndroid Build Coastguard Worker def writelines(self, seq): 239*cda5da8dSAndroid Build Coastguard Worker for line in seq: 240*cda5da8dSAndroid Build Coastguard Worker self.write(line) 241*cda5da8dSAndroid Build Coastguard Worker 242*cda5da8dSAndroid Build Coastguard Worker def close(self): 243*cda5da8dSAndroid Build Coastguard Worker assert_(0, "errors.close() must not be called") 244*cda5da8dSAndroid Build Coastguard Worker 245*cda5da8dSAndroid Build Coastguard Workerclass WriteWrapper: 246*cda5da8dSAndroid Build Coastguard Worker 247*cda5da8dSAndroid Build Coastguard Worker def __init__(self, wsgi_writer): 248*cda5da8dSAndroid Build Coastguard Worker self.writer = wsgi_writer 249*cda5da8dSAndroid Build Coastguard Worker 250*cda5da8dSAndroid Build Coastguard Worker def __call__(self, s): 251*cda5da8dSAndroid Build Coastguard Worker assert_(type(s) is bytes) 252*cda5da8dSAndroid Build Coastguard Worker self.writer(s) 253*cda5da8dSAndroid Build Coastguard Worker 254*cda5da8dSAndroid Build Coastguard Workerclass PartialIteratorWrapper: 255*cda5da8dSAndroid Build Coastguard Worker 256*cda5da8dSAndroid Build Coastguard Worker def __init__(self, wsgi_iterator): 257*cda5da8dSAndroid Build Coastguard Worker self.iterator = wsgi_iterator 258*cda5da8dSAndroid Build Coastguard Worker 259*cda5da8dSAndroid Build Coastguard Worker def __iter__(self): 260*cda5da8dSAndroid Build Coastguard Worker # We want to make sure __iter__ is called 261*cda5da8dSAndroid Build Coastguard Worker return IteratorWrapper(self.iterator, None) 262*cda5da8dSAndroid Build Coastguard Worker 263*cda5da8dSAndroid Build Coastguard Workerclass IteratorWrapper: 264*cda5da8dSAndroid Build Coastguard Worker 265*cda5da8dSAndroid Build Coastguard Worker def __init__(self, wsgi_iterator, check_start_response): 266*cda5da8dSAndroid Build Coastguard Worker self.original_iterator = wsgi_iterator 267*cda5da8dSAndroid Build Coastguard Worker self.iterator = iter(wsgi_iterator) 268*cda5da8dSAndroid Build Coastguard Worker self.closed = False 269*cda5da8dSAndroid Build Coastguard Worker self.check_start_response = check_start_response 270*cda5da8dSAndroid Build Coastguard Worker 271*cda5da8dSAndroid Build Coastguard Worker def __iter__(self): 272*cda5da8dSAndroid Build Coastguard Worker return self 273*cda5da8dSAndroid Build Coastguard Worker 274*cda5da8dSAndroid Build Coastguard Worker def __next__(self): 275*cda5da8dSAndroid Build Coastguard Worker assert_(not self.closed, 276*cda5da8dSAndroid Build Coastguard Worker "Iterator read after closed") 277*cda5da8dSAndroid Build Coastguard Worker v = next(self.iterator) 278*cda5da8dSAndroid Build Coastguard Worker if type(v) is not bytes: 279*cda5da8dSAndroid Build Coastguard Worker assert_(False, "Iterator yielded non-bytestring (%r)" % (v,)) 280*cda5da8dSAndroid Build Coastguard Worker if self.check_start_response is not None: 281*cda5da8dSAndroid Build Coastguard Worker assert_(self.check_start_response, 282*cda5da8dSAndroid Build Coastguard Worker "The application returns and we started iterating over its body, but start_response has not yet been called") 283*cda5da8dSAndroid Build Coastguard Worker self.check_start_response = None 284*cda5da8dSAndroid Build Coastguard Worker return v 285*cda5da8dSAndroid Build Coastguard Worker 286*cda5da8dSAndroid Build Coastguard Worker def close(self): 287*cda5da8dSAndroid Build Coastguard Worker self.closed = True 288*cda5da8dSAndroid Build Coastguard Worker if hasattr(self.original_iterator, 'close'): 289*cda5da8dSAndroid Build Coastguard Worker self.original_iterator.close() 290*cda5da8dSAndroid Build Coastguard Worker 291*cda5da8dSAndroid Build Coastguard Worker def __del__(self): 292*cda5da8dSAndroid Build Coastguard Worker if not self.closed: 293*cda5da8dSAndroid Build Coastguard Worker sys.stderr.write( 294*cda5da8dSAndroid Build Coastguard Worker "Iterator garbage collected without being closed") 295*cda5da8dSAndroid Build Coastguard Worker assert_(self.closed, 296*cda5da8dSAndroid Build Coastguard Worker "Iterator garbage collected without being closed") 297*cda5da8dSAndroid Build Coastguard Worker 298*cda5da8dSAndroid Build Coastguard Workerdef check_environ(environ): 299*cda5da8dSAndroid Build Coastguard Worker assert_(type(environ) is dict, 300*cda5da8dSAndroid Build Coastguard Worker "Environment is not of the right type: %r (environment: %r)" 301*cda5da8dSAndroid Build Coastguard Worker % (type(environ), environ)) 302*cda5da8dSAndroid Build Coastguard Worker 303*cda5da8dSAndroid Build Coastguard Worker for key in ['REQUEST_METHOD', 'SERVER_NAME', 'SERVER_PORT', 304*cda5da8dSAndroid Build Coastguard Worker 'wsgi.version', 'wsgi.input', 'wsgi.errors', 305*cda5da8dSAndroid Build Coastguard Worker 'wsgi.multithread', 'wsgi.multiprocess', 306*cda5da8dSAndroid Build Coastguard Worker 'wsgi.run_once']: 307*cda5da8dSAndroid Build Coastguard Worker assert_(key in environ, 308*cda5da8dSAndroid Build Coastguard Worker "Environment missing required key: %r" % (key,)) 309*cda5da8dSAndroid Build Coastguard Worker 310*cda5da8dSAndroid Build Coastguard Worker for key in ['HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH']: 311*cda5da8dSAndroid Build Coastguard Worker assert_(key not in environ, 312*cda5da8dSAndroid Build Coastguard Worker "Environment should not have the key: %s " 313*cda5da8dSAndroid Build Coastguard Worker "(use %s instead)" % (key, key[5:])) 314*cda5da8dSAndroid Build Coastguard Worker 315*cda5da8dSAndroid Build Coastguard Worker if 'QUERY_STRING' not in environ: 316*cda5da8dSAndroid Build Coastguard Worker warnings.warn( 317*cda5da8dSAndroid Build Coastguard Worker 'QUERY_STRING is not in the WSGI environment; the cgi ' 318*cda5da8dSAndroid Build Coastguard Worker 'module will use sys.argv when this variable is missing, ' 319*cda5da8dSAndroid Build Coastguard Worker 'so application errors are more likely', 320*cda5da8dSAndroid Build Coastguard Worker WSGIWarning) 321*cda5da8dSAndroid Build Coastguard Worker 322*cda5da8dSAndroid Build Coastguard Worker for key in environ.keys(): 323*cda5da8dSAndroid Build Coastguard Worker if '.' in key: 324*cda5da8dSAndroid Build Coastguard Worker # Extension, we don't care about its type 325*cda5da8dSAndroid Build Coastguard Worker continue 326*cda5da8dSAndroid Build Coastguard Worker assert_(type(environ[key]) is str, 327*cda5da8dSAndroid Build Coastguard Worker "Environmental variable %s is not a string: %r (value: %r)" 328*cda5da8dSAndroid Build Coastguard Worker % (key, type(environ[key]), environ[key])) 329*cda5da8dSAndroid Build Coastguard Worker 330*cda5da8dSAndroid Build Coastguard Worker assert_(type(environ['wsgi.version']) is tuple, 331*cda5da8dSAndroid Build Coastguard Worker "wsgi.version should be a tuple (%r)" % (environ['wsgi.version'],)) 332*cda5da8dSAndroid Build Coastguard Worker assert_(environ['wsgi.url_scheme'] in ('http', 'https'), 333*cda5da8dSAndroid Build Coastguard Worker "wsgi.url_scheme unknown: %r" % environ['wsgi.url_scheme']) 334*cda5da8dSAndroid Build Coastguard Worker 335*cda5da8dSAndroid Build Coastguard Worker check_input(environ['wsgi.input']) 336*cda5da8dSAndroid Build Coastguard Worker check_errors(environ['wsgi.errors']) 337*cda5da8dSAndroid Build Coastguard Worker 338*cda5da8dSAndroid Build Coastguard Worker # @@: these need filling out: 339*cda5da8dSAndroid Build Coastguard Worker if environ['REQUEST_METHOD'] not in ( 340*cda5da8dSAndroid Build Coastguard Worker 'GET', 'HEAD', 'POST', 'OPTIONS', 'PATCH', 'PUT', 'DELETE', 'TRACE'): 341*cda5da8dSAndroid Build Coastguard Worker warnings.warn( 342*cda5da8dSAndroid Build Coastguard Worker "Unknown REQUEST_METHOD: %r" % environ['REQUEST_METHOD'], 343*cda5da8dSAndroid Build Coastguard Worker WSGIWarning) 344*cda5da8dSAndroid Build Coastguard Worker 345*cda5da8dSAndroid Build Coastguard Worker assert_(not environ.get('SCRIPT_NAME') 346*cda5da8dSAndroid Build Coastguard Worker or environ['SCRIPT_NAME'].startswith('/'), 347*cda5da8dSAndroid Build Coastguard Worker "SCRIPT_NAME doesn't start with /: %r" % environ['SCRIPT_NAME']) 348*cda5da8dSAndroid Build Coastguard Worker assert_(not environ.get('PATH_INFO') 349*cda5da8dSAndroid Build Coastguard Worker or environ['PATH_INFO'].startswith('/'), 350*cda5da8dSAndroid Build Coastguard Worker "PATH_INFO doesn't start with /: %r" % environ['PATH_INFO']) 351*cda5da8dSAndroid Build Coastguard Worker if environ.get('CONTENT_LENGTH'): 352*cda5da8dSAndroid Build Coastguard Worker assert_(int(environ['CONTENT_LENGTH']) >= 0, 353*cda5da8dSAndroid Build Coastguard Worker "Invalid CONTENT_LENGTH: %r" % environ['CONTENT_LENGTH']) 354*cda5da8dSAndroid Build Coastguard Worker 355*cda5da8dSAndroid Build Coastguard Worker if not environ.get('SCRIPT_NAME'): 356*cda5da8dSAndroid Build Coastguard Worker assert_('PATH_INFO' in environ, 357*cda5da8dSAndroid Build Coastguard Worker "One of SCRIPT_NAME or PATH_INFO are required (PATH_INFO " 358*cda5da8dSAndroid Build Coastguard Worker "should at least be '/' if SCRIPT_NAME is empty)") 359*cda5da8dSAndroid Build Coastguard Worker assert_(environ.get('SCRIPT_NAME') != '/', 360*cda5da8dSAndroid Build Coastguard Worker "SCRIPT_NAME cannot be '/'; it should instead be '', and " 361*cda5da8dSAndroid Build Coastguard Worker "PATH_INFO should be '/'") 362*cda5da8dSAndroid Build Coastguard Worker 363*cda5da8dSAndroid Build Coastguard Workerdef check_input(wsgi_input): 364*cda5da8dSAndroid Build Coastguard Worker for attr in ['read', 'readline', 'readlines', '__iter__']: 365*cda5da8dSAndroid Build Coastguard Worker assert_(hasattr(wsgi_input, attr), 366*cda5da8dSAndroid Build Coastguard Worker "wsgi.input (%r) doesn't have the attribute %s" 367*cda5da8dSAndroid Build Coastguard Worker % (wsgi_input, attr)) 368*cda5da8dSAndroid Build Coastguard Worker 369*cda5da8dSAndroid Build Coastguard Workerdef check_errors(wsgi_errors): 370*cda5da8dSAndroid Build Coastguard Worker for attr in ['flush', 'write', 'writelines']: 371*cda5da8dSAndroid Build Coastguard Worker assert_(hasattr(wsgi_errors, attr), 372*cda5da8dSAndroid Build Coastguard Worker "wsgi.errors (%r) doesn't have the attribute %s" 373*cda5da8dSAndroid Build Coastguard Worker % (wsgi_errors, attr)) 374*cda5da8dSAndroid Build Coastguard Worker 375*cda5da8dSAndroid Build Coastguard Workerdef check_status(status): 376*cda5da8dSAndroid Build Coastguard Worker status = check_string_type(status, "Status") 377*cda5da8dSAndroid Build Coastguard Worker # Implicitly check that we can turn it into an integer: 378*cda5da8dSAndroid Build Coastguard Worker status_code = status.split(None, 1)[0] 379*cda5da8dSAndroid Build Coastguard Worker assert_(len(status_code) == 3, 380*cda5da8dSAndroid Build Coastguard Worker "Status codes must be three characters: %r" % status_code) 381*cda5da8dSAndroid Build Coastguard Worker status_int = int(status_code) 382*cda5da8dSAndroid Build Coastguard Worker assert_(status_int >= 100, "Status code is invalid: %r" % status_int) 383*cda5da8dSAndroid Build Coastguard Worker if len(status) < 4 or status[3] != ' ': 384*cda5da8dSAndroid Build Coastguard Worker warnings.warn( 385*cda5da8dSAndroid Build Coastguard Worker "The status string (%r) should be a three-digit integer " 386*cda5da8dSAndroid Build Coastguard Worker "followed by a single space and a status explanation" 387*cda5da8dSAndroid Build Coastguard Worker % status, WSGIWarning) 388*cda5da8dSAndroid Build Coastguard Worker 389*cda5da8dSAndroid Build Coastguard Workerdef check_headers(headers): 390*cda5da8dSAndroid Build Coastguard Worker assert_(type(headers) is list, 391*cda5da8dSAndroid Build Coastguard Worker "Headers (%r) must be of type list: %r" 392*cda5da8dSAndroid Build Coastguard Worker % (headers, type(headers))) 393*cda5da8dSAndroid Build Coastguard Worker for item in headers: 394*cda5da8dSAndroid Build Coastguard Worker assert_(type(item) is tuple, 395*cda5da8dSAndroid Build Coastguard Worker "Individual headers (%r) must be of type tuple: %r" 396*cda5da8dSAndroid Build Coastguard Worker % (item, type(item))) 397*cda5da8dSAndroid Build Coastguard Worker assert_(len(item) == 2) 398*cda5da8dSAndroid Build Coastguard Worker name, value = item 399*cda5da8dSAndroid Build Coastguard Worker name = check_string_type(name, "Header name") 400*cda5da8dSAndroid Build Coastguard Worker value = check_string_type(value, "Header value") 401*cda5da8dSAndroid Build Coastguard Worker assert_(name.lower() != 'status', 402*cda5da8dSAndroid Build Coastguard Worker "The Status header cannot be used; it conflicts with CGI " 403*cda5da8dSAndroid Build Coastguard Worker "script, and HTTP status is not given through headers " 404*cda5da8dSAndroid Build Coastguard Worker "(value: %r)." % value) 405*cda5da8dSAndroid Build Coastguard Worker assert_('\n' not in name and ':' not in name, 406*cda5da8dSAndroid Build Coastguard Worker "Header names may not contain ':' or '\\n': %r" % name) 407*cda5da8dSAndroid Build Coastguard Worker assert_(header_re.search(name), "Bad header name: %r" % name) 408*cda5da8dSAndroid Build Coastguard Worker assert_(not name.endswith('-') and not name.endswith('_'), 409*cda5da8dSAndroid Build Coastguard Worker "Names may not end in '-' or '_': %r" % name) 410*cda5da8dSAndroid Build Coastguard Worker if bad_header_value_re.search(value): 411*cda5da8dSAndroid Build Coastguard Worker assert_(0, "Bad header value: %r (bad char: %r)" 412*cda5da8dSAndroid Build Coastguard Worker % (value, bad_header_value_re.search(value).group(0))) 413*cda5da8dSAndroid Build Coastguard Worker 414*cda5da8dSAndroid Build Coastguard Workerdef check_content_type(status, headers): 415*cda5da8dSAndroid Build Coastguard Worker status = check_string_type(status, "Status") 416*cda5da8dSAndroid Build Coastguard Worker code = int(status.split(None, 1)[0]) 417*cda5da8dSAndroid Build Coastguard Worker # @@: need one more person to verify this interpretation of RFC 2616 418*cda5da8dSAndroid Build Coastguard Worker # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 419*cda5da8dSAndroid Build Coastguard Worker NO_MESSAGE_BODY = (204, 304) 420*cda5da8dSAndroid Build Coastguard Worker for name, value in headers: 421*cda5da8dSAndroid Build Coastguard Worker name = check_string_type(name, "Header name") 422*cda5da8dSAndroid Build Coastguard Worker if name.lower() == 'content-type': 423*cda5da8dSAndroid Build Coastguard Worker if code not in NO_MESSAGE_BODY: 424*cda5da8dSAndroid Build Coastguard Worker return 425*cda5da8dSAndroid Build Coastguard Worker assert_(0, ("Content-Type header found in a %s response, " 426*cda5da8dSAndroid Build Coastguard Worker "which must not return content.") % code) 427*cda5da8dSAndroid Build Coastguard Worker if code not in NO_MESSAGE_BODY: 428*cda5da8dSAndroid Build Coastguard Worker assert_(0, "No Content-Type header found in headers (%s)" % headers) 429*cda5da8dSAndroid Build Coastguard Worker 430*cda5da8dSAndroid Build Coastguard Workerdef check_exc_info(exc_info): 431*cda5da8dSAndroid Build Coastguard Worker assert_(exc_info is None or type(exc_info) is tuple, 432*cda5da8dSAndroid Build Coastguard Worker "exc_info (%r) is not a tuple: %r" % (exc_info, type(exc_info))) 433*cda5da8dSAndroid Build Coastguard Worker # More exc_info checks? 434*cda5da8dSAndroid Build Coastguard Worker 435*cda5da8dSAndroid Build Coastguard Workerdef check_iterator(iterator): 436*cda5da8dSAndroid Build Coastguard Worker # Technically a bytestring is legal, which is why it's a really bad 437*cda5da8dSAndroid Build Coastguard Worker # idea, because it may cause the response to be returned 438*cda5da8dSAndroid Build Coastguard Worker # character-by-character 439*cda5da8dSAndroid Build Coastguard Worker assert_(not isinstance(iterator, (str, bytes)), 440*cda5da8dSAndroid Build Coastguard Worker "You should not return a string as your application iterator, " 441*cda5da8dSAndroid Build Coastguard Worker "instead return a single-item list containing a bytestring.") 442