1ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikimport binascii 2ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikimport io 3ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikimport os 4ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikimport re 5ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikimport sys 6ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikimport tempfile 7ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikimport mimetypes 8ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craiktry: 9ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik import simplejson as json 10ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikexcept ImportError: 11ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik import json 12ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikimport warnings 13ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 14ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikfrom webob.acceptparse import ( 15ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik AcceptLanguage, 16ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik AcceptCharset, 17ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik MIMEAccept, 18ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik MIMENilAccept, 19ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik NoAccept, 20ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik accept_property, 21ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 22ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 23ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikfrom webob.cachecontrol import ( 24ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik CacheControl, 25ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik serialize_cache_control, 26ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 27ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 28ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikfrom webob.compat import ( 29ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik PY3, 30ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik bytes_, 31ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik integer_types, 32ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik native_, 33ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parse_qsl_text, 34ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik reraise, 35ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik text_type, 36ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url_encode, 37ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url_quote, 38ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url_unquote, 39ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik quote_plus, 40ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik urlparse, 41ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik cgi_FieldStorage 42ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 43ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 44ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikfrom webob.cookies import RequestCookies 45ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 46ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikfrom webob.descriptors import ( 47ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik CHARSET_RE, 48ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik SCHEME_RE, 49ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik converter, 50ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik converter_date, 51ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ_getter, 52ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ_decoder, 53ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parse_auth, 54ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parse_int, 55ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parse_int_safe, 56ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parse_range, 57ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik serialize_auth, 58ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik serialize_if_range, 59ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik serialize_int, 60ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik serialize_range, 61ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik upath_property, 62ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik deprecated_property, 63ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 64ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 65ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikfrom webob.etag import ( 66ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik IfRange, 67ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik AnyETag, 68ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik NoETag, 69ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik etag_property, 70ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 71ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 72ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikfrom webob.headers import EnvironHeaders 73ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 74ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikfrom webob.multidict import ( 75ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik NestedMultiDict, 76ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik MultiDict, 77ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik NoVars, 78ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik GetDict, 79ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 80ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 81ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikfrom webob.util import warn_deprecation 82ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 83ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik__all__ = ['BaseRequest', 'Request', 'LegacyRequest'] 84ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 85ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikclass _NoDefault: 86ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def __repr__(self): 87ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return '(No Default)' 88ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris CraikNoDefault = _NoDefault() 89ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 90ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris CraikPATH_SAFE = '/:@&+$,' 91ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 92ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikhttp_method_probably_has_body = dict.fromkeys( 93ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ('GET', 'HEAD', 'DELETE', 'TRACE'), False) 94ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikhttp_method_probably_has_body.update( 95ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik dict.fromkeys(('POST', 'PUT', 'PATCH'), True)) 96ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 97ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik_LATIN_ENCODINGS = ( 98ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'ascii', 'latin-1', 'latin', 'latin_1', 'l1', 'latin1', 99ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'iso-8859-1', 'iso8859_1', 'iso_8859_1', 'iso8859', '8859', 100ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 101ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 102ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikclass BaseRequest(object): 103ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ## The limit after which request bodies should be stored on disk 104ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ## if they are read in (under this, and the request body is stored 105ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ## in memory): 106ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik request_body_tempfile_limit = 10*1024 107ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 108ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _charset = None 109ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 110ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def __init__(self, environ, charset=None, unicode_errors=None, 111ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik decode_param_names=None, **kw): 112ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 113ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if type(environ) is not dict: 114ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise TypeError( 115ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "WSGI environ must be a dict; you passed %r" % (environ,)) 116ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if unicode_errors is not None: 117ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik warnings.warn( 118ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "You unicode_errors=%r to the Request constructor. Passing a " 119ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "``unicode_errors`` value to the Request is no longer " 120ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "supported in WebOb 1.2+. This value has been ignored " % ( 121ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik unicode_errors,), 122ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik DeprecationWarning 123ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 124ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if decode_param_names is not None: 125ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik warnings.warn( 126ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "You passed decode_param_names=%r to the Request constructor. " 127ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "Passing a ``decode_param_names`` value to the Request " 128ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "is no longer supported in WebOb 1.2+. This value has " 129ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "been ignored " % (decode_param_names,), 130ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik DeprecationWarning 131ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 132ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not _is_utf8(charset): 133ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise DeprecationWarning( 134ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "You passed charset=%r to the Request constructor. As of " 135ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "WebOb 1.2, if your application needs a non-UTF-8 request " 136ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "charset, please construct the request without a charset or " 137ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "with a charset of 'None', then use ``req = " 138ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "req.decode(charset)``" % charset 139ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 140ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 141ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik d = self.__dict__ 142ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik d['environ'] = environ 143ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if kw: 144ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik cls = self.__class__ 145ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'method' in kw: 146ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # set method first, because .body setters 147ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # depend on it for checks 148ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.method = kw.pop('method') 149ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik for name, value in kw.items(): 150ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not hasattr(cls, name): 151ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise TypeError( 152ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "Unexpected keyword: %s=%r" % (name, value)) 153ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik setattr(self, name, value) 154ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 155ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if PY3: # pragma: no cover 156ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def encget(self, key, default=NoDefault, encattr=None): 157ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik val = self.environ.get(key, default) 158ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if val is NoDefault: 159ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise KeyError(key) 160ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if val is default: 161ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return default 162ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not encattr: 163ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return val 164ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik encoding = getattr(self, encattr) 165ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if encoding in _LATIN_ENCODINGS: # shortcut 166ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return val 167ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return bytes_(val, 'latin-1').decode(encoding) 168ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 169ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def encget(self, key, default=NoDefault, encattr=None): 170ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik val = self.environ.get(key, default) 171ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if val is NoDefault: 172ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise KeyError(key) 173ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if val is default: 174ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return default 175ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if encattr is None: 176ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return val 177ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik encoding = getattr(self, encattr) 178ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return val.decode(encoding) 179ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 180ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def encset(self, key, val, encattr=None): 181ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if encattr: 182ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik encoding = getattr(self, encattr) 183ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 184ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik encoding = 'ascii' 185ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if PY3: # pragma: no cover 186ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.environ[key] = bytes_(val, encoding).decode('latin-1') 187ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 188ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.environ[key] = bytes_(val, encoding) 189ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 190ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 191ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def charset(self): 192ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if self._charset is None: 193ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik charset = detect_charset(self._content_type_raw) 194ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if _is_utf8(charset): 195ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik charset = 'UTF-8' 196ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self._charset = charset 197ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self._charset 198ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 199ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @charset.setter 200ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def charset(self, charset): 201ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if _is_utf8(charset): 202ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik charset = 'UTF-8' 203ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if charset != self.charset: 204ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise DeprecationWarning("Use req = req.decode(%r)" % charset) 205ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 206ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def decode(self, charset=None, errors='strict'): 207ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik charset = charset or self.charset 208ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if charset == 'UTF-8': 209ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self 210ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # cookies and path are always utf-8 211ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik t = Transcoder(charset, errors) 212ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 213ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik new_content_type = CHARSET_RE.sub('; charset="UTF-8"', 214ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self._content_type_raw) 215ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_type = self.content_type 216ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r = self.__class__( 217ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.environ.copy(), 218ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik query_string=t.transcode_query(self.query_string), 219ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_type=new_content_type, 220ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 221ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 222ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if content_type == 'application/x-www-form-urlencoded': 223ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r.body = bytes_(t.transcode_query(native_(r.body))) 224ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return r 225ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif content_type != 'multipart/form-data': 226ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return r 227ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 228ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fs_environ = self.environ.copy() 229ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fs_environ.setdefault('CONTENT_LENGTH', '0') 230ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fs_environ['QUERY_STRING'] = '' 231ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if PY3: # pragma: no cover 232ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fs = cgi_FieldStorage(fp=self.body_file, 233ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ=fs_environ, 234ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik keep_blank_values=True, 235ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik encoding=charset, 236ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik errors=errors) 237ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 238ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fs = cgi_FieldStorage(fp=self.body_file, 239ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ=fs_environ, 240ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik keep_blank_values=True) 241ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 242ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 243ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fout = t.transcode_fs(fs, r._content_type_raw) 244ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 245ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # this order is important, because setting body_file 246ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # resets content_length 247ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r.body_file = fout 248ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r.content_length = fout.tell() 249ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fout.seek(0) 250ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return r 251ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 252ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 253ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # this is necessary for correct warnings depth for both 254ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # BaseRequest and Request (due to AdhocAttrMixin.__setattr__) 255ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _setattr_stacklevel = 2 256ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 257ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _body_file__get(self): 258ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 259ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Input stream of the request (wsgi.input). 260ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Setting this property resets the content_length and seekable flag 261ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik (unlike setting req.body_file_raw). 262ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 263ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not self.is_body_readable: 264ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return io.BytesIO() 265ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r = self.body_file_raw 266ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik clen = self.content_length 267ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not self.is_body_seekable and clen is not None: 268ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # we need to wrap input in LimitedLengthFile 269ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # but we have to cache the instance as well 270ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # otherwise this would stop working 271ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # (.remaining counter would reset between calls): 272ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # req.body_file.read(100) 273ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # req.body_file.read(100) 274ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env = self.environ 275ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik wrapped, raw = env.get('webob._body_file', (0,0)) 276ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if raw is not r: 277ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik wrapped = LimitedLengthFile(r, clen) 278ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik wrapped = io.BufferedReader(wrapped) 279ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['webob._body_file'] = wrapped, r 280ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r = wrapped 281ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return r 282ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 283ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _body_file__set(self, value): 284ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if isinstance(value, bytes): 285ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik warn_deprecation( 286ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "Please use req.body = b'bytes' or req.body_file = fileobj", 287ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik '1.2', 288ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self._setattr_stacklevel 289ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 290ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.content_length = None 291ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body_file_raw = value 292ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.is_body_seekable = False 293ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.is_body_readable = True 294ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _body_file__del(self): 295ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body = b'' 296ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik body_file = property(_body_file__get, 297ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _body_file__set, 298ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _body_file__del, 299ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik doc=_body_file__get.__doc__) 300ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik body_file_raw = environ_getter('wsgi.input') 301ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 302ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def body_file_seekable(self): 303ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 304ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Get the body of the request (wsgi.input) as a seekable file-like 305ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik object. Middleware and routing applications should use this 306ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik attribute over .body_file. 307ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 308ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik If you access this value, CONTENT_LENGTH will also be updated. 309ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 310ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not self.is_body_seekable: 311ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.make_body_seekable() 312ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.body_file_raw 313ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 314ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url_encoding = environ_getter('webob.url_encoding', 'UTF-8') 315ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik scheme = environ_getter('wsgi.url_scheme') 316ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik method = environ_getter('REQUEST_METHOD', 'GET') 317ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik http_version = environ_getter('SERVER_PROTOCOL') 318ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_length = converter( 319ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ_getter('CONTENT_LENGTH', None, '14.13'), 320ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parse_int_safe, serialize_int, 'int') 321ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik remote_user = environ_getter('REMOTE_USER', None) 322ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik remote_addr = environ_getter('REMOTE_ADDR', None) 323ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik query_string = environ_getter('QUERY_STRING', '') 324ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik server_name = environ_getter('SERVER_NAME') 325ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik server_port = converter( 326ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ_getter('SERVER_PORT'), 327ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parse_int, serialize_int, 'int') 328ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 329ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik script_name = environ_decoder('SCRIPT_NAME', '', encattr='url_encoding') 330ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik path_info = environ_decoder('PATH_INFO', encattr='url_encoding') 331ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 332ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # bw compat 333ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik uscript_name = script_name 334ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik upath_info = path_info 335ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 336ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _content_type_raw = environ_getter('CONTENT_TYPE', '') 337ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 338ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _content_type__get(self): 339ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """Return the content type, but leaving off any parameters (like 340ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik charset, but also things like the type in ``application/atom+xml; 341ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik type=entry``) 342ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 343ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik If you set this property, you can include parameters, or if 344ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik you don't include any parameters in the value then existing 345ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parameters will be preserved. 346ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 347ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self._content_type_raw.split(';', 1)[0] 348ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _content_type__set(self, value=None): 349ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if value is not None: 350ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik value = str(value) 351ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if ';' not in value: 352ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_type = self._content_type_raw 353ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if ';' in content_type: 354ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik value += ';' + content_type.split(';', 1)[1] 355ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self._content_type_raw = value 356ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 357ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_type = property(_content_type__get, 358ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _content_type__set, 359ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _content_type__set, 360ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _content_type__get.__doc__) 361ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 362ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _headers = None 363ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 364ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _headers__get(self): 365ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 366ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik All the request headers as a case-insensitive dictionary-like 367ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik object. 368ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 369ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if self._headers is None: 370ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self._headers = EnvironHeaders(self.environ) 371ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self._headers 372ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 373ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _headers__set(self, value): 374ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.headers.clear() 375ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.headers.update(value) 376ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 377ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik headers = property(_headers__get, _headers__set, doc=_headers__get.__doc__) 378ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 379ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 380ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def client_addr(self): 381ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 382ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik The effective client IP address as a string. If the 383ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ``HTTP_X_FORWARDED_FOR`` header exists in the WSGI environ, this 384ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik attribute returns the client IP address present in that header 385ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik (e.g. if the header value is ``192.168.1.1, 192.168.1.2``, the value 386ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik will be ``192.168.1.1``). If no ``HTTP_X_FORWARDED_FOR`` header is 387ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik present in the environ at all, this attribute will return the value 388ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik of the ``REMOTE_ADDR`` header. If the ``REMOTE_ADDR`` header is 389ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik unset, this attribute will return the value ``None``. 390ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 391ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik .. warning:: 392ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 393ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik It is possible for user agents to put someone else's IP or just 394ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik any string in ``HTTP_X_FORWARDED_FOR`` as it is a normal HTTP 395ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik header. Forward proxies can also provide incorrect values (private 396ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik IP addresses etc). You cannot "blindly" trust the result of this 397ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik method to provide you with valid data unless you're certain that 398ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ``HTTP_X_FORWARDED_FOR`` has the correct values. The WSGI server 399ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik must be behind a trusted proxy for this to be true. 400ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 401ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik e = self.environ 402ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik xff = e.get('HTTP_X_FORWARDED_FOR') 403ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if xff is not None: 404ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik addr = xff.split(',')[0].strip() 405ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 406ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik addr = e.get('REMOTE_ADDR') 407ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return addr 408ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 409ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 410ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def host_port(self): 411ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 412ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik The effective server port number as a string. If the ``HTTP_HOST`` 413ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik header exists in the WSGI environ, this attribute returns the port 414ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik number present in that header. If the ``HTTP_HOST`` header exists but 415ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik contains no explicit port number: if the WSGI url scheme is "https" , 416ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik this attribute returns "443", if the WSGI url scheme is "http", this 417ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik attribute returns "80" . If no ``HTTP_HOST`` header is present in 418ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik the environ at all, this attribute will return the value of the 419ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ``SERVER_PORT`` header (which is guaranteed to be present). 420ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 421ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik e = self.environ 422ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik host = e.get('HTTP_HOST') 423ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if host is not None: 424ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if ':' in host: 425ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik host, port = host.split(':', 1) 426ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 427ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url_scheme = e['wsgi.url_scheme'] 428ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if url_scheme == 'https': 429ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik port = '443' 430ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 431ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik port = '80' 432ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 433ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik port = e['SERVER_PORT'] 434ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return port 435ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 436ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 437ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def host_url(self): 438ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 439ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik The URL through the host (no path) 440ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 441ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik e = self.environ 442ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik scheme = e.get('wsgi.url_scheme') 443ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url = scheme + '://' 444ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik host = e.get('HTTP_HOST') 445ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if host is not None: 446ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if ':' in host: 447ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik host, port = host.split(':', 1) 448ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 449ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik port = None 450ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 451ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik host = e.get('SERVER_NAME') 452ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik port = e.get('SERVER_PORT') 453ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if scheme == 'https': 454ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if port == '443': 455ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik port = None 456ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif scheme == 'http': 457ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if port == '80': 458ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik port = None 459ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url += host 460ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if port: 461ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url += ':%s' % port 462ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return url 463ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 464ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 465ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def application_url(self): 466ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 467ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik The URL including SCRIPT_NAME (no PATH_INFO or query string) 468ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 469ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik bscript_name = bytes_(self.script_name, self.url_encoding) 470ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.host_url + url_quote(bscript_name, PATH_SAFE) 471ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 472ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 473ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def path_url(self): 474ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 475ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik The URL including SCRIPT_NAME and PATH_INFO, but not QUERY_STRING 476ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 477ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik bpath_info = bytes_(self.path_info, self.url_encoding) 478ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.application_url + url_quote(bpath_info, PATH_SAFE) 479ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 480ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 481ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def path(self): 482ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 483ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik The path of the request, without host or query string 484ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 485ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik bscript = bytes_(self.script_name, self.url_encoding) 486ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik bpath = bytes_(self.path_info, self.url_encoding) 487ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return url_quote(bscript, PATH_SAFE) + url_quote(bpath, PATH_SAFE) 488ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 489ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 490ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def path_qs(self): 491ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 492ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik The path of the request, without host but with query string 493ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 494ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik path = self.path 495ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik qs = self.environ.get('QUERY_STRING') 496ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if qs: 497ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik path += '?' + qs 498ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return path 499ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 500ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 501ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def url(self): 502ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 503ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik The full request URL, including QUERY_STRING 504ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 505ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url = self.path_url 506ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik qs = self.environ.get('QUERY_STRING') 507ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if qs: 508ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url += '?' + qs 509ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return url 510ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 511ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def relative_url(self, other_url, to_application=False): 512ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 513ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Resolve other_url relative to the request URL. 514ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 515ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik If ``to_application`` is True, then resolve it relative to the 516ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik URL with only SCRIPT_NAME 517ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 518ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if to_application: 519ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url = self.application_url 520ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not url.endswith('/'): 521ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url += '/' 522ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 523ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url = self.path_url 524ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return urlparse.urljoin(url, other_url) 525ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 526ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def path_info_pop(self, pattern=None): 527ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 528ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'Pops' off the next segment of PATH_INFO, pushing it onto 529ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik SCRIPT_NAME, and returning the popped segment. Returns None if 530ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik there is nothing left on PATH_INFO. 531ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 532ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Does not return ``''`` when there's an empty segment (like 533ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ``/path//path``); these segments are just ignored. 534ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 535ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Optional ``pattern`` argument is a regexp to match the return value 536ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik before returning. If there is no match, no changes are made to the 537ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik request and None is returned. 538ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 539ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik path = self.path_info 540ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not path: 541ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return None 542ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik slashes = '' 543ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik while path.startswith('/'): 544ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik slashes += '/' 545ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik path = path[1:] 546ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik idx = path.find('/') 547ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if idx == -1: 548ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik idx = len(path) 549ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r = path[:idx] 550ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if pattern is None or re.match(pattern, r): 551ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.script_name += slashes + r 552ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.path_info = path[idx:] 553ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return r 554ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 555ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def path_info_peek(self): 556ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 557ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Returns the next segment on PATH_INFO, or None if there is no 558ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik next segment. Doesn't modify the environment. 559ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 560ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik path = self.path_info 561ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not path: 562ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return None 563ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik path = path.lstrip('/') 564ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return path.split('/', 1)[0] 565ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 566ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _urlvars__get(self): 567ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 568ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Return any *named* variables matched in the URL. 569ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 570ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Takes values from ``environ['wsgiorg.routing_args']``. 571ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Systems like ``routes`` set this value. 572ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 573ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'paste.urlvars' in self.environ: 574ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.environ['paste.urlvars'] 575ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif 'wsgiorg.routing_args' in self.environ: 576ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.environ['wsgiorg.routing_args'][1] 577ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 578ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik result = {} 579ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.environ['wsgiorg.routing_args'] = ((), result) 580ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return result 581ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 582ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _urlvars__set(self, value): 583ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ = self.environ 584ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'wsgiorg.routing_args' in environ: 585ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ['wsgiorg.routing_args'] = ( 586ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ['wsgiorg.routing_args'][0], value) 587ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'paste.urlvars' in environ: 588ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik del environ['paste.urlvars'] 589ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif 'paste.urlvars' in environ: 590ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ['paste.urlvars'] = value 591ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 592ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ['wsgiorg.routing_args'] = ((), value) 593ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 594ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _urlvars__del(self): 595ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'paste.urlvars' in self.environ: 596ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik del self.environ['paste.urlvars'] 597ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'wsgiorg.routing_args' in self.environ: 598ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not self.environ['wsgiorg.routing_args'][0]: 599ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik del self.environ['wsgiorg.routing_args'] 600ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 601ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.environ['wsgiorg.routing_args'] = ( 602ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.environ['wsgiorg.routing_args'][0], {}) 603ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 604ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik urlvars = property(_urlvars__get, 605ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _urlvars__set, 606ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _urlvars__del, 607ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik doc=_urlvars__get.__doc__) 608ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 609ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _urlargs__get(self): 610ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 611ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Return any *positional* variables matched in the URL. 612ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 613ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Takes values from ``environ['wsgiorg.routing_args']``. 614ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Systems like ``routes`` set this value. 615ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 616ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'wsgiorg.routing_args' in self.environ: 617ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.environ['wsgiorg.routing_args'][0] 618ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 619ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # Since you can't update this value in-place, we don't need 620ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # to set the key in the environment 621ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return () 622ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 623ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _urlargs__set(self, value): 624ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ = self.environ 625ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'paste.urlvars' in environ: 626ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # Some overlap between this and wsgiorg.routing_args; we need 627ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # wsgiorg.routing_args to make this work 628ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik routing_args = (value, environ.pop('paste.urlvars')) 629ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif 'wsgiorg.routing_args' in environ: 630ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik routing_args = (value, environ['wsgiorg.routing_args'][1]) 631ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 632ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik routing_args = (value, {}) 633ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ['wsgiorg.routing_args'] = routing_args 634ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 635ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _urlargs__del(self): 636ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'wsgiorg.routing_args' in self.environ: 637ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not self.environ['wsgiorg.routing_args'][1]: 638ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik del self.environ['wsgiorg.routing_args'] 639ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 640ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.environ['wsgiorg.routing_args'] = ( 641ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik (), self.environ['wsgiorg.routing_args'][1]) 642ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 643ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik urlargs = property(_urlargs__get, 644ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _urlargs__set, 645ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _urlargs__del, 646ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _urlargs__get.__doc__) 647ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 648ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 649ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def is_xhr(self): 650ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """Is X-Requested-With header present and equal to ``XMLHttpRequest``? 651ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 652ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Note: this isn't set by every XMLHttpRequest request, it is 653ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik only set if you are using a Javascript library that sets it 654ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik (or you set the header yourself manually). Currently 655ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Prototype and jQuery are known to set this header.""" 656ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.environ.get('HTTP_X_REQUESTED_WITH', '') == 'XMLHttpRequest' 657ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 658ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _host__get(self): 659ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """Host name provided in HTTP_HOST, with fall-back to SERVER_NAME""" 660ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'HTTP_HOST' in self.environ: 661ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.environ['HTTP_HOST'] 662ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 663ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return '%(SERVER_NAME)s:%(SERVER_PORT)s' % self.environ 664ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _host__set(self, value): 665ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.environ['HTTP_HOST'] = value 666ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _host__del(self): 667ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'HTTP_HOST' in self.environ: 668ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik del self.environ['HTTP_HOST'] 669ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik host = property(_host__get, _host__set, _host__del, doc=_host__get.__doc__) 670ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 671ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 672ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def domain(self): 673ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ Returns the domain portion of the host value. Equivalent to: 674ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 675ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik .. code-block:: python 676ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 677ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik domain = request.host 678ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if ':' in domain: 679ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik domain = domain.split(':', 1)[0] 680ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 681ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik This will be equivalent to the domain portion of the ``HTTP_HOST`` 682ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik value in the environment if it exists, or the ``SERVER_NAME`` value in 683ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik the environment if it doesn't. For example, if the environment 684ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik contains an ``HTTP_HOST`` value of ``foo.example.com:8000``, 685ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ``request.domain`` will return ``foo.example.com``. 686ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 687ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Note that this value cannot be *set* on the request. To set the host 688ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik value use :meth:`webob.request.Request.host` instead. 689ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 690ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik domain = self.host 691ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if ':' in domain: 692ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik domain = domain.split(':', 1)[0] 693ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return domain 694ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 695ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _body__get(self): 696ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 697ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Return the content of the request body. 698ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 699ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not self.is_body_readable: 700ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return b'' 701ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.make_body_seekable() # we need this to have content_length 702ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r = self.body_file.read(self.content_length) 703ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body_file_raw.seek(0) 704ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return r 705ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _body__set(self, value): 706ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if value is None: 707ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik value = b'' 708ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not isinstance(value, bytes): 709ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise TypeError("You can only set Request.body to bytes (not %r)" 710ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik % type(value)) 711ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not http_method_probably_has_body.get(self.method, True): 712ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not value: 713ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.content_length = None 714ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body_file_raw = io.BytesIO() 715ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return 716ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.content_length = len(value) 717ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body_file_raw = io.BytesIO(value) 718ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.is_body_seekable = True 719ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _body__del(self): 720ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body = b'' 721ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik body = property(_body__get, _body__set, _body__del, doc=_body__get.__doc__) 722ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 723ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _json_body__get(self): 724ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """Access the body of the request as JSON""" 725ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return json.loads(self.body.decode(self.charset)) 726ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 727ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _json_body__set(self, value): 728ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body = json.dumps(value, separators=(',', ':')).encode(self.charset) 729ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 730ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _json_body__del(self): 731ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik del self.body 732ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 733ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik json = json_body = property(_json_body__get, _json_body__set, _json_body__del) 734ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 735ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _text__get(self): 736ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 737ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Get/set the text value of the body 738ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 739ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not self.charset: 740ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise AttributeError( 741ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "You cannot access Request.text unless charset is set") 742ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik body = self.body 743ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return body.decode(self.charset) 744ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 745ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _text__set(self, value): 746ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not self.charset: 747ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise AttributeError( 748ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "You cannot access Response.text unless charset is set") 749ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not isinstance(value, text_type): 750ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise TypeError( 751ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "You can only set Request.text to a unicode string " 752ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "(not %s)" % type(value)) 753ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body = value.encode(self.charset) 754ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 755ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _text__del(self): 756ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik del self.body 757ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 758ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik text = property(_text__get, _text__set, _text__del, doc=_text__get.__doc__) 759ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 760ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 761ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 762ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def POST(self): 763ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 764ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Return a MultiDict containing all the variables from a form 765ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik request. Returns an empty dict-like object for non-form requests. 766ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 767ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Form requests are typically POST requests, however PUT & PATCH requests 768ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik with an appropriate Content-Type are also supported. 769ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 770ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env = self.environ 771ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if self.method not in ('POST', 'PUT', 'PATCH'): 772ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return NoVars('Not a form request') 773ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'webob._parsed_post_vars' in env: 774ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik vars, body_file = env['webob._parsed_post_vars'] 775ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if body_file is self.body_file_raw: 776ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return vars 777ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_type = self.content_type 778ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if ((self.method == 'PUT' and not content_type) 779ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik or content_type not in 780ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ('', 781ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'application/x-www-form-urlencoded', 782ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'multipart/form-data') 783ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ): 784ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # Not an HTML form submission 785ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return NoVars('Not an HTML form submission (Content-Type: %s)' 786ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik % content_type) 787ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self._check_charset() 788ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 789ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.make_body_seekable() 790ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body_file_raw.seek(0) 791ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 792ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fs_environ = env.copy() 793ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # FieldStorage assumes a missing CONTENT_LENGTH, but a 794ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # default of 0 is better: 795ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fs_environ.setdefault('CONTENT_LENGTH', '0') 796ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fs_environ['QUERY_STRING'] = '' 797ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if PY3: # pragma: no cover 798ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fs = cgi_FieldStorage( 799ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fp=self.body_file, 800ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ=fs_environ, 801ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik keep_blank_values=True, 802ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik encoding='utf8') 803ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik vars = MultiDict.from_fieldstorage(fs) 804ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 805ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fs = cgi_FieldStorage( 806ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fp=self.body_file, 807ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ=fs_environ, 808ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik keep_blank_values=True) 809ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik vars = MultiDict.from_fieldstorage(fs) 810ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 811ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['webob._parsed_post_vars'] = (vars, self.body_file_raw) 812ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return vars 813ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 814ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 815ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def GET(self): 816ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 817ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Return a MultiDict containing all the variables from the 818ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik QUERY_STRING. 819ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 820ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env = self.environ 821ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik source = env.get('QUERY_STRING', '') 822ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'webob._parsed_query_vars' in env: 823ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik vars, qs = env['webob._parsed_query_vars'] 824ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if qs == source: 825ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return vars 826ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 827ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik data = [] 828ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if source: 829ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # this is disabled because we want to access req.GET 830ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # for text/plain; charset=ascii uploads for example 831ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik #self._check_charset() 832ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik data = parse_qsl_text(source) 833ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik #d = lambda b: b.decode('utf8') 834ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik #data = [(d(k), d(v)) for k,v in data] 835ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik vars = GetDict(data, env) 836ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['webob._parsed_query_vars'] = (vars, source) 837ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return vars 838ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 839ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _check_charset(self): 840ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if self.charset != 'UTF-8': 841ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise DeprecationWarning( 842ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "Requests are expected to be submitted in UTF-8, not %s. " 843ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "You can fix this by doing req = req.decode('%s')" % ( 844ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.charset, self.charset) 845ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 846ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 847ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 848ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def params(self): 849ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 850ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik A dictionary-like object containing both the parameters from 851ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik the query string and request body. 852ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 853ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik params = NestedMultiDict(self.GET, self.POST) 854ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return params 855ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 856ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 857ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @property 858ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def cookies(self): 859ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 860ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Return a dictionary of cookies as found in the request. 861ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 862ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return RequestCookies(self.environ) 863ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 864ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @cookies.setter 865ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def cookies(self, val): 866ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.environ.pop('HTTP_COOKIE', None) 867ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r = RequestCookies(self.environ) 868ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r.update(val) 869ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 870ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def copy(self): 871ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 872ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Copy the request and environment object. 873ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 874ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik This only does a shallow copy, except of wsgi.input 875ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 876ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.make_body_seekable() 877ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env = self.environ.copy() 878ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik new_req = self.__class__(env) 879ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik new_req.copy_body() 880ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return new_req 881ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 882ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def copy_get(self): 883ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 884ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Copies the request and environment object, but turning this request 885ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik into a GET along the way. If this was a POST request (or any other 886ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik verb) then it becomes GET, and the request body is thrown away. 887ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 888ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env = self.environ.copy() 889ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.__class__(env, method='GET', content_type=None, 890ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik body=b'') 891ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 892ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # webob.is_body_seekable marks input streams that are seekable 893ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # this way we can have seekable input without testing the .seek() method 894ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik is_body_seekable = environ_getter('webob.is_body_seekable', False) 895ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 896ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik #is_body_readable = environ_getter('webob.is_body_readable', False) 897ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 898ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _is_body_readable__get(self): 899ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 900ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik webob.is_body_readable is a flag that tells us 901ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik that we can read the input stream even though 902ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik CONTENT_LENGTH is missing. This allows FakeCGIBody 903ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik to work and can be used by servers to support 904ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik chunked encoding in requests. 905ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik For background see https://bitbucket.org/ianb/webob/issue/6 906ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 907ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if http_method_probably_has_body.get(self.method): 908ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # known HTTP method with body 909ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return True 910ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif self.content_length is not None: 911ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # unknown HTTP method, but the Content-Length 912ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # header is present 913ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return True 914ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 915ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # last resort -- rely on the special flag 916ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.environ.get('webob.is_body_readable', False) 917ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 918ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _is_body_readable__set(self, flag): 919ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.environ['webob.is_body_readable'] = bool(flag) 920ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 921ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik is_body_readable = property(_is_body_readable__get, _is_body_readable__set, 922ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik doc=_is_body_readable__get.__doc__ 923ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 924ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 925ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 926ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 927ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def make_body_seekable(self): 928ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 929ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik This forces ``environ['wsgi.input']`` to be seekable. 930ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik That means that, the content is copied into a BytesIO or temporary 931ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik file and flagged as seekable, so that it will not be unnecessarily 932ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik copied again. 933ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 934ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik After calling this method the .body_file is always seeked to the 935ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik start of file and .content_length is not None. 936ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 937ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik The choice to copy to BytesIO is made from 938ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ``self.request_body_tempfile_limit`` 939ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 940ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if self.is_body_seekable: 941ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body_file_raw.seek(0) 942ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 943ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.copy_body() 944ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 945ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 946ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def copy_body(self): 947ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 948ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Copies the body, in cases where it might be shared with 949ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik another request object and that is not desired. 950ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 951ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik This copies the body in-place, either into a BytesIO object 952ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik or a temporary file. 953ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 954ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not self.is_body_readable: 955ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # there's no body to copy 956ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body = b'' 957ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif self.content_length is None: 958ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # chunked body or FakeCGIBody 959ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body = self.body_file_raw.read() 960ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self._copy_body_tempfile() 961ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 962ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # try to read body into tempfile 963ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik did_copy = self._copy_body_tempfile() 964ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not did_copy: 965ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # it wasn't necessary, so just read it into memory 966ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body = self.body_file.read(self.content_length) 967ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 968ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _copy_body_tempfile(self): 969ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 970ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Copy wsgi.input to tempfile if necessary. Returns True if it did. 971ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 972ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik tempfile_limit = self.request_body_tempfile_limit 973ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik todo = self.content_length 974ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik assert isinstance(todo, integer_types), todo 975ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not tempfile_limit or todo <= tempfile_limit: 976ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return False 977ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fileobj = self.make_tempfile() 978ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik input = self.body_file 979ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik while todo > 0: 980ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik data = input.read(min(todo, 65536)) 981ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not data: 982ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # Normally this should not happen, because LimitedLengthFile 983ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # should have raised an exception by now. 984ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # It can happen if the is_body_seekable flag is incorrect. 985ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise DisconnectionError( 986ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "Client disconnected (%s more bytes were expected)" 987ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik % todo 988ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 989ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fileobj.write(data) 990ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik todo -= len(data) 991ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fileobj.seek(0) 992ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body_file_raw = fileobj 993ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.is_body_seekable = True 994ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return True 995ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 996ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def make_tempfile(self): 997ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 998ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Create a tempfile to store big request body. 999ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik This API is not stable yet. A 'size' argument might be added. 1000ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1001ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return tempfile.TemporaryFile() 1002ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1003ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1004ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def remove_conditional_headers(self, 1005ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik remove_encoding=True, 1006ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik remove_range=True, 1007ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik remove_match=True, 1008ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik remove_modified=True): 1009ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1010ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Remove headers that make the request conditional. 1011ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1012ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik These headers can cause the response to be 304 Not Modified, 1013ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik which in some cases you may not want to be possible. 1014ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1015ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik This does not remove headers like If-Match, which are used for 1016ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik conflict detection. 1017ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1018ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik check_keys = [] 1019ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if remove_range: 1020ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik check_keys += ['HTTP_IF_RANGE', 'HTTP_RANGE'] 1021ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if remove_match: 1022ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik check_keys.append('HTTP_IF_NONE_MATCH') 1023ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if remove_modified: 1024ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik check_keys.append('HTTP_IF_MODIFIED_SINCE') 1025ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if remove_encoding: 1026ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik check_keys.append('HTTP_ACCEPT_ENCODING') 1027ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1028ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik for key in check_keys: 1029ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if key in self.environ: 1030ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik del self.environ[key] 1031ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1032ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1033ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik accept = accept_property('Accept', '14.1', MIMEAccept, MIMENilAccept) 1034ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik accept_charset = accept_property('Accept-Charset', '14.2', AcceptCharset) 1035ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik accept_encoding = accept_property('Accept-Encoding', '14.3', 1036ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik NilClass=NoAccept) 1037ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik accept_language = accept_property('Accept-Language', '14.4', AcceptLanguage) 1038ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1039ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik authorization = converter( 1040ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ_getter('HTTP_AUTHORIZATION', None, '14.8'), 1041ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parse_auth, serialize_auth, 1042ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 1043ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1044ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1045ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _cache_control__get(self): 1046ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1047ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Get/set/modify the Cache-Control header (`HTTP spec section 14.9 1048ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9>`_) 1049ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1050ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env = self.environ 1051ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik value = env.get('HTTP_CACHE_CONTROL', '') 1052ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik cache_header, cache_obj = env.get('webob._cache_control', (None, None)) 1053ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if cache_obj is not None and cache_header == value: 1054ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return cache_obj 1055ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik cache_obj = CacheControl.parse(value, 1056ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik updates_to=self._update_cache_control, 1057ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik type='request') 1058ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['webob._cache_control'] = (value, cache_obj) 1059ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return cache_obj 1060ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1061ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _cache_control__set(self, value): 1062ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env = self.environ 1063ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik value = value or '' 1064ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if isinstance(value, dict): 1065ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik value = CacheControl(value, type='request') 1066ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if isinstance(value, CacheControl): 1067ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik str_value = str(value) 1068ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['HTTP_CACHE_CONTROL'] = str_value 1069ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['webob._cache_control'] = (str_value, value) 1070ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1071ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['HTTP_CACHE_CONTROL'] = str(value) 1072ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['webob._cache_control'] = (None, None) 1073ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1074ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _cache_control__del(self): 1075ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env = self.environ 1076ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'HTTP_CACHE_CONTROL' in env: 1077ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik del env['HTTP_CACHE_CONTROL'] 1078ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if 'webob._cache_control' in env: 1079ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik del env['webob._cache_control'] 1080ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1081ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def _update_cache_control(self, prop_dict): 1082ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.environ['HTTP_CACHE_CONTROL'] = serialize_cache_control(prop_dict) 1083ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1084ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik cache_control = property(_cache_control__get, 1085ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _cache_control__set, 1086ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _cache_control__del, 1087ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik doc=_cache_control__get.__doc__) 1088ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1089ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1090ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if_match = etag_property('HTTP_IF_MATCH', AnyETag, '14.24') 1091ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if_none_match = etag_property('HTTP_IF_NONE_MATCH', NoETag, '14.26', 1092ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik strong=False) 1093ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1094ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik date = converter_date(environ_getter('HTTP_DATE', None, '14.8')) 1095ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if_modified_since = converter_date( 1096ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ_getter('HTTP_IF_MODIFIED_SINCE', None, '14.25')) 1097ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if_unmodified_since = converter_date( 1098ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ_getter('HTTP_IF_UNMODIFIED_SINCE', None, '14.28')) 1099ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if_range = converter( 1100ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ_getter('HTTP_IF_RANGE', None, '14.27'), 1101ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik IfRange.parse, serialize_if_range, 'IfRange object') 1102ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1103ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1104ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik max_forwards = converter( 1105ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ_getter('HTTP_MAX_FORWARDS', None, '14.31'), 1106ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parse_int, serialize_int, 'int') 1107ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1108ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik pragma = environ_getter('HTTP_PRAGMA', None, '14.32') 1109ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1110ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik range = converter( 1111ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ_getter('HTTP_RANGE', None, '14.35'), 1112ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parse_range, serialize_range, 'Range object') 1113ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1114ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik referer = environ_getter('HTTP_REFERER', None, '14.36') 1115ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik referrer = referer 1116ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1117ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik user_agent = environ_getter('HTTP_USER_AGENT', None, '14.43') 1118ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1119ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def __repr__(self): 1120ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik try: 1121ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik name = '%s %s' % (self.method, self.url) 1122ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik except KeyError: 1123ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik name = '(invalid WSGI environ)' 1124ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik msg = '<%s at 0x%x %s>' % ( 1125ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.__class__.__name__, 1126ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik abs(id(self)), name) 1127ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return msg 1128ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1129ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def as_bytes(self, skip_body=False): 1130ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1131ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Return HTTP bytes representing this request. 1132ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik If skip_body is True, exclude the body. 1133ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik If skip_body is an integer larger than one, skip body 1134ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik only if its length is bigger than that number. 1135ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1136ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url = self.url 1137ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik host = self.host_url 1138ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik assert url.startswith(host) 1139ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik url = url[len(host):] 1140ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parts = [bytes_('%s %s %s' % (self.method, url, self.http_version))] 1141ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik #self.headers.setdefault('Host', self.host) 1142ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1143ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # acquire body before we handle headers so that 1144ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # content-length will be set 1145ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik body = None 1146ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if http_method_probably_has_body.get(self.method): 1147ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if skip_body > 1: 1148ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if len(self.body) > skip_body: 1149ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik body = bytes_('<body skipped (len=%s)>' % len(self.body)) 1150ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1151ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik skip_body = False 1152ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not skip_body: 1153ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik body = self.body 1154ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1155ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik for k, v in sorted(self.headers.items()): 1156ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik header = bytes_('%s: %s' % (k, v)) 1157ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parts.append(header) 1158ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1159ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if body: 1160ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik parts.extend([b'', body]) 1161ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # HTTP clearly specifies CRLF 1162ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return b'\r\n'.join(parts) 1163ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1164ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def as_string(self, skip_body=False): 1165ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # TODO: Remove in 1.4 1166ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik warn_deprecation( 1167ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "Please use req.as_bytes", 1168ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik '1.3', 1169ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self._setattr_stacklevel 1170ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 1171ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1172ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def as_text(self): 1173ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik bytes = self.as_bytes() 1174ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return bytes.decode(self.charset) 1175ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1176ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik __str__ = as_text 1177ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1178ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @classmethod 1179ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def from_bytes(cls, b): 1180ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1181ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Create a request from HTTP bytes data. If the bytes contain 1182ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik extra data after the request, raise a ValueError. 1183ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1184ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik f = io.BytesIO(b) 1185ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r = cls.from_file(f) 1186ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if f.tell() != len(b): 1187ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise ValueError("The string contains more data than expected") 1188ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return r 1189ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1190ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @classmethod 1191ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def from_string(cls, b): 1192ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # TODO: Remove in 1.4 1193ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik warn_deprecation( 1194ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "Please use req.from_bytes", 1195ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik '1.3', 1196ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik cls._setattr_stacklevel 1197ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 1198ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1199ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @classmethod 1200ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def from_text(cls, s): 1201ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik b = bytes_(s, 'utf-8') 1202ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return cls.from_bytes(b) 1203ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1204ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @classmethod 1205ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def from_file(cls, fp): 1206ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """Read a request from a file-like object (it must implement 1207ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ``.read(size)`` and ``.readline()``). 1208ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1209ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik It will read up to the end of the request, not the end of the 1210ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik file (unless the request is a POST or PUT and has no 1211ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Content-Length, in that case, the entire file is read). 1212ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1213ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik This reads the request as represented by ``str(req)``; it may 1214ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik not read every valid HTTP request properly. 1215ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1216ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik start_line = fp.readline() 1217ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik is_text = isinstance(start_line, text_type) 1218ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if is_text: 1219ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik crlf = '\r\n' 1220ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik colon = ':' 1221ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1222ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik crlf = b'\r\n' 1223ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik colon = b':' 1224ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik try: 1225ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik header = start_line.rstrip(crlf) 1226ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik method, resource, http_version = header.split(None, 2) 1227ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik method = native_(method, 'utf-8') 1228ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik resource = native_(resource, 'utf-8') 1229ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik http_version = native_(http_version, 'utf-8') 1230ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik except ValueError: 1231ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise ValueError('Bad HTTP request line: %r' % start_line) 1232ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r = cls(environ_from_url(resource), 1233ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik http_version=http_version, 1234ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik method=method.upper() 1235ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 1236ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik del r.environ['HTTP_HOST'] 1237ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik while 1: 1238ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik line = fp.readline() 1239ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not line.strip(): 1240ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # end of headers 1241ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik break 1242ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik hname, hval = line.split(colon, 1) 1243ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik hname = native_(hname, 'utf-8') 1244ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik hval = native_(hval, 'utf-8').strip() 1245ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if hname in r.headers: 1246ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik hval = r.headers[hname] + ', ' + hval 1247ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r.headers[hname] = hval 1248ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if http_method_probably_has_body.get(r.method): 1249ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik clen = r.content_length 1250ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if clen is None: 1251ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik body = fp.read() 1252ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1253ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik body = fp.read(clen) 1254ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if is_text: 1255ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik body = bytes_(body, 'utf-8') 1256ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik r.body = body 1257ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return r 1258ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1259ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def call_application(self, application, catch_exc_info=False): 1260ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1261ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Call the given WSGI application, returning ``(status_string, 1262ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik headerlist, app_iter)`` 1263ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1264ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Be sure to call ``app_iter.close()`` if it's there. 1265ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1266ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik If catch_exc_info is true, then returns ``(status_string, 1267ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik headerlist, app_iter, exc_info)``, where the fourth item may 1268ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik be None, but won't be if there was an exception. If you don't 1269ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik do this and there was an exception, the exception will be 1270ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raised directly. 1271ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1272ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if self.is_body_seekable: 1273ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.body_file_raw.seek(0) 1274ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik captured = [] 1275ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik output = [] 1276ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def start_response(status, headers, exc_info=None): 1277ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if exc_info is not None and not catch_exc_info: 1278ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik reraise(exc_info) 1279ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik captured[:] = [status, headers, exc_info] 1280ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return output.append 1281ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik app_iter = application(self.environ, start_response) 1282ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if output or not captured: 1283ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik try: 1284ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik output.extend(app_iter) 1285ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik finally: 1286ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if hasattr(app_iter, 'close'): 1287ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik app_iter.close() 1288ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik app_iter = output 1289ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if catch_exc_info: 1290ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return (captured[0], captured[1], app_iter, captured[2]) 1291ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1292ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return (captured[0], captured[1], app_iter) 1293ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1294ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # Will be filled in later: 1295ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ResponseClass = None 1296ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1297ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def send(self, application=None, catch_exc_info=False): 1298ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1299ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Like ``.call_application(application)``, except returns a 1300ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik response object with ``.status``, ``.headers``, and ``.body`` 1301ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik attributes. 1302ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1303ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik This will use ``self.ResponseClass`` to figure out the class 1304ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik of the response object to return. 1305ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1306ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik If ``application`` is not given, this will send the request to 1307ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ``self.make_default_send_app()`` 1308ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1309ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if application is None: 1310ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik application = self.make_default_send_app() 1311ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if catch_exc_info: 1312ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik status, headers, app_iter, exc_info = self.call_application( 1313ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik application, catch_exc_info=True) 1314ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik del exc_info 1315ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1316ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik status, headers, app_iter = self.call_application( 1317ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik application, catch_exc_info=False) 1318ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.ResponseClass( 1319ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik status=status, headerlist=list(headers), app_iter=app_iter) 1320ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1321ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik get_response = send 1322ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1323ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def make_default_send_app(self): 1324ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik global _client 1325ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik try: 1326ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik client = _client 1327ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik except NameError: 1328ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik from webob import client 1329ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _client = client 1330ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return client.send_request_app 1331ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1332ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @classmethod 1333ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def blank(cls, path, environ=None, base_url=None, 1334ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik headers=None, POST=None, **kw): 1335ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1336ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Create a blank request environ (and Request wrapper) with the 1337ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik given path (path should be urlencoded), and any keys from 1338ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ. 1339ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1340ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik The path will become path_info, with any query string split 1341ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik off and used. 1342ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1343ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik All necessary keys will be added to the environ, but the 1344ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik values you pass in will take precedence. If you pass in 1345ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik base_url then wsgi.url_scheme, HTTP_HOST, and SCRIPT_NAME will 1346ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik be filled in from that value. 1347ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1348ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Any extra keyword will be passed to ``__init__``. 1349ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1350ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env = environ_from_url(path) 1351ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if base_url: 1352ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik scheme, netloc, path, query, fragment = urlparse.urlsplit(base_url) 1353ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if query or fragment: 1354ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise ValueError( 1355ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "base_url (%r) cannot have a query or fragment" 1356ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik % base_url) 1357ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if scheme: 1358ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['wsgi.url_scheme'] = scheme 1359ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if netloc: 1360ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if ':' not in netloc: 1361ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if scheme == 'http': 1362ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik netloc += ':80' 1363ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif scheme == 'https': 1364ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik netloc += ':443' 1365ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1366ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise ValueError( 1367ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "Unknown scheme: %r" % scheme) 1368ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik host, port = netloc.split(':', 1) 1369ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['SERVER_PORT'] = port 1370ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['SERVER_NAME'] = host 1371ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['HTTP_HOST'] = netloc 1372ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if path: 1373ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['SCRIPT_NAME'] = url_unquote(path) 1374ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if environ: 1375ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env.update(environ) 1376ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_type = kw.get('content_type', env.get('CONTENT_TYPE')) 1377ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if headers and 'Content-Type' in headers: 1378ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_type = headers['Content-Type'] 1379ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if content_type is not None: 1380ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik kw['content_type'] = content_type 1381ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik environ_add_POST(env, POST, content_type=content_type) 1382ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik obj = cls(env, **kw) 1383ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if headers is not None: 1384ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik obj.headers.update(headers) 1385ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return obj 1386ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1387ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikclass LegacyRequest(BaseRequest): 1388ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik uscript_name = upath_property('SCRIPT_NAME') 1389ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik upath_info = upath_property('PATH_INFO') 1390ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1391ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def encget(self, key, default=NoDefault, encattr=None): 1392ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik val = self.environ.get(key, default) 1393ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if val is NoDefault: 1394ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise KeyError(key) 1395ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if val is default: 1396ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return default 1397ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return val 1398ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1399ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikclass AdhocAttrMixin(object): 1400ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _setattr_stacklevel = 3 1401ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1402ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def __setattr__(self, attr, value, DEFAULT=object()): 1403ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if (getattr(self.__class__, attr, DEFAULT) is not DEFAULT or 1404ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik attr.startswith('_')): 1405ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik object.__setattr__(self, attr, value) 1406ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1407ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.environ.setdefault('webob.adhoc_attrs', {})[attr] = value 1408ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1409ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def __getattr__(self, attr, DEFAULT=object()): 1410ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik try: 1411ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.environ['webob.adhoc_attrs'][attr] 1412ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik except KeyError: 1413ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise AttributeError(attr) 1414ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1415ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def __delattr__(self, attr, DEFAULT=object()): 1416ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if getattr(self.__class__, attr, DEFAULT) is not DEFAULT: 1417ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return object.__delattr__(self, attr) 1418ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik try: 1419ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik del self.environ['webob.adhoc_attrs'][attr] 1420ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik except KeyError: 1421ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise AttributeError(attr) 1422ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1423ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikclass Request(AdhocAttrMixin, BaseRequest): 1424ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ The default request implementation """ 1425ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1426ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikdef environ_from_url(path): 1427ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if SCHEME_RE.search(path): 1428ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik scheme, netloc, path, qs, fragment = urlparse.urlsplit(path) 1429ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if fragment: 1430ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise TypeError("Path cannot contain a fragment (%r)" % fragment) 1431ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if qs: 1432ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik path += '?' + qs 1433ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if ':' not in netloc: 1434ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if scheme == 'http': 1435ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik netloc += ':80' 1436ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif scheme == 'https': 1437ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik netloc += ':443' 1438ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1439ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise TypeError("Unknown scheme: %r" % scheme) 1440ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1441ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik scheme = 'http' 1442ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik netloc = 'localhost:80' 1443ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if path and '?' in path: 1444ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik path_info, query_string = path.split('?', 1) 1445ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik path_info = url_unquote(path_info) 1446ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1447ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik path_info = url_unquote(path) 1448ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik query_string = '' 1449ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env = { 1450ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'REQUEST_METHOD': 'GET', 1451ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'SCRIPT_NAME': '', 1452ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'PATH_INFO': path_info or '', 1453ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'QUERY_STRING': query_string, 1454ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'SERVER_NAME': netloc.split(':')[0], 1455ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'SERVER_PORT': netloc.split(':')[1], 1456ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'HTTP_HOST': netloc, 1457ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'SERVER_PROTOCOL': 'HTTP/1.0', 1458ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'wsgi.version': (1, 0), 1459ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'wsgi.url_scheme': scheme, 1460ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'wsgi.input': io.BytesIO(), 1461ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'wsgi.errors': sys.stderr, 1462ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'wsgi.multithread': False, 1463ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'wsgi.multiprocess': False, 1464ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'wsgi.run_once': False, 1465ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik #'webob.is_body_seekable': True, 1466ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik } 1467ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return env 1468ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1469ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1470ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikdef environ_add_POST(env, data, content_type=None): 1471ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if data is None: 1472ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return 1473ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif isinstance(data, text_type): # pragma: no cover 1474ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik data = data.encode('ascii') 1475ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if env['REQUEST_METHOD'] not in ('POST', 'PUT'): 1476ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['REQUEST_METHOD'] = 'POST' 1477ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik has_files = False 1478ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if hasattr(data, 'items'): 1479ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik data = list(data.items()) 1480ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik for k, v in data: 1481ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if isinstance(v, (tuple, list)): 1482ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik has_files = True 1483ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik break 1484ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if content_type is None: 1485ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if has_files: 1486ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_type = 'multipart/form-data' 1487ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1488ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_type = 'application/x-www-form-urlencoded' 1489ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if content_type.startswith('multipart/form-data'): 1490ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not isinstance(data, bytes): 1491ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_type, data = _encode_multipart(data, content_type) 1492ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif content_type.startswith('application/x-www-form-urlencoded'): 1493ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if has_files: 1494ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise ValueError('Submiting files is not allowed for' 1495ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ' content type `%s`' % content_type) 1496ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not isinstance(data, bytes): 1497ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik data = url_encode(data) 1498ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1499ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not isinstance(data, bytes): 1500ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise ValueError('Please provide `POST` data as string' 1501ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ' for content type `%s`' % content_type) 1502ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik data = bytes_(data, 'utf8') 1503ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['wsgi.input'] = io.BytesIO(data) 1504ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['webob.is_body_seekable'] = True 1505ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['CONTENT_LENGTH'] = str(len(data)) 1506ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik env['CONTENT_TYPE'] = content_type 1507ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1508ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1509ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1510ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik######################### 1511ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik## Helper classes and monkeypatching 1512ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik######################### 1513ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1514ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikclass DisconnectionError(IOError): 1515ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik pass 1516ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1517ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1518ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikclass LimitedLengthFile(io.RawIOBase): 1519ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def __init__(self, file, maxlen): 1520ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.file = file 1521ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.maxlen = maxlen 1522ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.remaining = maxlen 1523ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1524ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def __repr__(self): 1525ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return '<%s(%r, maxlen=%s)>' % ( 1526ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.__class__.__name__, 1527ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.file, 1528ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.maxlen 1529ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 1530ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1531ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def fileno(self): 1532ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.file.fileno() 1533ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1534ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @staticmethod 1535ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def readable(): 1536ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return True 1537ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1538ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def readinto(self, buff): 1539ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not self.remaining: 1540ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return 0 1541ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik sz0 = min(len(buff), self.remaining) 1542ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik data = self.file.read(sz0) 1543ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik sz = len(data) 1544ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.remaining -= sz 1545ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik #if not data: 1546ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if sz < sz0 and self.remaining: 1547ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise DisconnectionError( 1548ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "The client disconnected while sending the POST/PUT body " 1549ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik + "(%d more bytes were expected)" % self.remaining 1550ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 1551ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik buff[:sz] = data 1552ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return sz 1553ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1554ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1555ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikdef _cgi_FieldStorage__repr__patch(self): 1556ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ monkey patch for FieldStorage.__repr__ 1557ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1558ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik Unbelievably, the default __repr__ on FieldStorage reads 1559ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik the entire file content instead of being sane about it. 1560ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik This is a simple replacement that doesn't do that 1561ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """ 1562ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if self.file: 1563ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return "FieldStorage(%r, %r)" % (self.name, self.filename) 1564ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return "FieldStorage(%r, %r, %r)" % (self.name, self.filename, self.value) 1565ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1566ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikcgi_FieldStorage.__repr__ = _cgi_FieldStorage__repr__patch 1567ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1568ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikclass FakeCGIBody(io.RawIOBase): 1569ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def __init__(self, vars, content_type): 1570ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if content_type.startswith('multipart/form-data'): 1571ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not _get_multipart_boundary(content_type): 1572ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik raise ValueError('Content-type: %r does not contain boundary' 1573ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik % content_type) 1574ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.vars = vars 1575ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.content_type = content_type 1576ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.file = None 1577ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1578ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def __repr__(self): 1579ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik inner = repr(self.vars) 1580ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if len(inner) > 20: 1581ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik inner = inner[:15] + '...' + inner[-5:] 1582ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return '<%s at 0x%x viewing %s>' % ( 1583ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.__class__.__name__, 1584ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik abs(id(self)), inner) 1585ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1586ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def fileno(self): 1587ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return None 1588ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1589ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik @staticmethod 1590ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def readable(): 1591ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return True 1592ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1593ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def readinto(self, buff): 1594ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if self.file is None: 1595ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if self.content_type.startswith( 1596ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 'application/x-www-form-urlencoded'): 1597ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik data = '&'.join( 1598ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik '%s=%s' % (quote_plus(bytes_(k, 'utf8')), quote_plus(bytes_(v, 'utf8'))) 1599ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik for k,v in self.vars.items() 1600ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 1601ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.file = io.BytesIO(bytes_(data)) 1602ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif self.content_type.startswith('multipart/form-data'): 1603ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.file = _encode_multipart( 1604ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.vars.items(), 1605ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.content_type, 1606ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fout=io.BytesIO() 1607ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik )[1] 1608ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.file.seek(0) 1609ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1610ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik assert 0, ('Bad content type: %r' % self.content_type) 1611ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return self.file.readinto(buff) 1612ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1613ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1614ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikdef _get_multipart_boundary(ctype): 1615ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik m = re.search(r'boundary=([^ ]+)', ctype, re.I) 1616ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if m: 1617ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return native_(m.group(1).strip('"')) 1618ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1619ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1620ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikdef _encode_multipart(vars, content_type, fout=None): 1621ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik """Encode a multipart request body into a string""" 1622ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik f = fout or io.BytesIO() 1623ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik w = f.write 1624ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik wt = lambda t: f.write(t.encode('utf8')) 1625ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik CRLF = b'\r\n' 1626ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik boundary = _get_multipart_boundary(content_type) 1627ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not boundary: 1628ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik boundary = native_(binascii.hexlify(os.urandom(10))) 1629ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_type += ('; boundary=%s' % boundary) 1630ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik for name, value in vars: 1631ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik w(b'--') 1632ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik wt(boundary) 1633ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik w(CRLF) 1634ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik assert name is not None, 'Value associated with no name: %r' % value 1635ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik wt('Content-Disposition: form-data; name="%s"' % name) 1636ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik filename = None 1637ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if getattr(value, 'filename', None): 1638ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik filename = value.filename 1639ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif isinstance(value, (list, tuple)): 1640ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik filename, value = value 1641ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if hasattr(value, 'read'): 1642ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik value = value.read() 1643ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1644ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if filename is not None: 1645ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik wt('; filename="%s"' % filename) 1646ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik mime_type = mimetypes.guess_type(filename)[0] 1647ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1648ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik mime_type = None 1649ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1650ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik w(CRLF) 1651ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1652ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # TODO: should handle value.disposition_options 1653ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if getattr(value, 'type', None): 1654ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik wt('Content-type: %s' % value.type) 1655ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if value.type_options: 1656ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik for ct_name, ct_value in sorted(value.type_options.items()): 1657ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik wt('; %s="%s"' % (ct_name, ct_value)) 1658ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik w(CRLF) 1659ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik elif mime_type: 1660ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik wt('Content-type: %s' % mime_type) 1661ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik w(CRLF) 1662ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik w(CRLF) 1663ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if hasattr(value, 'value'): 1664ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik value = value.value 1665ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if isinstance(value, bytes): 1666ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik w(value) 1667ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1668ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik wt(value) 1669ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik w(CRLF) 1670ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik wt('--%s--' % boundary) 1671ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if fout: 1672ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return content_type, fout 1673ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1674ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return content_type, f.getvalue() 1675ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1676ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikdef detect_charset(ctype): 1677ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik m = CHARSET_RE.search(ctype) 1678ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if m: 1679ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return m.group(1).strip('"').strip() 1680ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1681ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikdef _is_utf8(charset): 1682ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if not charset: 1683ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return True 1684ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1685ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return charset.lower().replace('-', '') == 'utf8' 1686ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1687ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1688ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikclass Transcoder(object): 1689ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def __init__(self, charset, errors='strict'): 1690ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.charset = charset # source charset 1691ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self.errors = errors # unicode errors 1692ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik self._trans = lambda b: b.decode(charset, errors).encode('utf8') 1693ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1694ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def transcode_query(self, q): 1695ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if PY3: # pragma: no cover 1696ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik q_orig = q 1697ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if '=' not in q: 1698ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # this doesn't look like a form submission 1699ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return q_orig 1700ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik q = list(parse_qsl_text(q, self.charset)) 1701ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return url_encode(q) 1702ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1703ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik q_orig = q 1704ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if '=' not in q: 1705ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # this doesn't look like a form submission 1706ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return q_orig 1707ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik q = urlparse.parse_qsl(q, self.charset) 1708ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik t = self._trans 1709ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik q = [(t(k), t(v)) for k,v in q] 1710ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return url_encode(q) 1711ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1712ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik def transcode_fs(self, fs, content_type): 1713ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # transcode FieldStorage 1714ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if PY3: # pragma: no cover 1715ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik decode = lambda b: b 1716ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1717ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik decode = lambda b: b.decode(self.charset, self.errors) 1718ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik data = [] 1719ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik for field in fs.list or (): 1720ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik field.name = decode(field.name) 1721ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik if field.filename: 1722ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik field.filename = decode(field.filename) 1723ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik data.append((field.name, field)) 1724ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik else: 1725ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik data.append((field.name, decode(field.value))) 1726ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1727ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik # TODO: transcode big requests to temp file 1728ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_type, fout = _encode_multipart( 1729ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik data, 1730ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik content_type, 1731ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik fout=io.BytesIO() 1732ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik ) 1733ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik return fout 1734ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik 1735ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik# TODO: remove in 1.4 1736ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craikfor _name in 'GET POST params cookies'.split(): 1737ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _str_name = 'str_'+_name 1738ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik _prop = deprecated_property( 1739ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik None, _str_name, 1740ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik "disabled starting WebOb 1.2, use %s instead" % _name, '1.2') 1741ced05db70069f9d84c4b0dd9b3b26b94e3482336Chris Craik setattr(BaseRequest, _str_name, _prop) 1742