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