def __init__(self, code=200, body='', headers=None): """Takes an int, a string, and a dict (or list of tuples). - code an HTTP response code, e.g., 404 - body the message body as a string - headers a Headers instance - cookie a Cookie.SimpleCookie instance Code is first because when you're raising your own Responses, they're usually error conditions. Body is second because one more often wants to specify a body without headers, than a header without a body. """ if not isinstance(code, int): raise TypeError("'code' must be an integer") elif not isinstance(body, basestring): raise TypeError("'body' must be a string") elif headers is not None and not isinstance(headers, (dict, list)): raise TypeError("'headers' must be a dictionary or a list of " + "2-tuples") Exception.__init__(self) self.code = code self.body = body self.headers = Headers('') if headers: if isinstance(headers, dict): headers = headers.items() for k, v in headers: self.headers.add(k, v) self.cookie = SimpleCookie() try: self.cookie.load(self.headers.one('Cookie', '')) except CookieError: pass
def hydrate(self): """Populate a number of attributes on self based on primitives. """ self.body = WwwForm(self.raw_body) self.headers = Headers(self.raw_headers) self.cookie = SimpleCookie() try: self.cookie.load(self.headers.one('Cookie', '')) except CookieError: pass urlparts = urlparse.urlparse(self.raw_url) self.path = Path(urlparts[2]) # populated by Website self.raw_querystring = urlparts[4] self.qs = WwwForm(self.raw_querystring) self.url = self.rebuild_url() # needs things above self.urlparts = urlparse.urlparse(self.url) self.transport = None # set by Website for *.sock files self.session_id = None # set by Website for *.sock files self.root = '' # set by Website self.fs = '' # set by Website self.namespace = {} # populated by user in inbound hooks
def test_headers_one_is_case_insensitive(): headers = Headers("Foo: Bar") expected = "Bar" actual = headers.one('foo') assert actual == expected, actual
def test_headers_one_gets_first_value(): headers = Headers("Foo: Bar\r\nFoo: Baz") expected = "Bar" actual = headers.one('Foo') assert actual == expected, actual
class Response(Exception): """Represent an HTTP Response message. """ def __init__(self, code=200, body='', headers=None): """Takes an int, a string, and a dict (or list of tuples). - code an HTTP response code, e.g., 404 - body the message body as a string - headers a Headers instance - cookie a Cookie.SimpleCookie instance Code is first because when you're raising your own Responses, they're usually error conditions. Body is second because one more often wants to specify a body without headers, than a header without a body. """ if not isinstance(code, int): raise TypeError("'code' must be an integer") elif not isinstance(body, basestring): raise TypeError("'body' must be a string") elif headers is not None and not isinstance(headers, (dict, list)): raise TypeError("'headers' must be a dictionary or a list of " + "2-tuples") Exception.__init__(self) self.code = code self.body = body self.headers = Headers('') if headers: if isinstance(headers, dict): headers = headers.items() for k, v in headers: self.headers.add(k, v) self.cookie = SimpleCookie() try: self.cookie.load(self.headers.one('Cookie', '')) except CookieError: pass def __call__(self, environ, start_response): wsgi_status = str(self) for morsel in self.cookie.values(): self.headers.add('Set-Cookie', morsel.OutputString()) wsgi_headers = [] for k, vals in self.headers._dict.items(): for v in vals: wsgi_headers.append((k, v)) start_response(wsgi_status, wsgi_headers) return [self.body] def __repr__(self): return "<Response: %s>" % str(self) def __str__(self): return "%d %s" % (self.code, self._status()) def _status(self): return status_strings.get(self.code, 'Unknown HTTP status') def _to_http(self, version): """Given a version string like 1.1, return an HTTP message, a string. """ status_line = "HTTP/%s" % version headers = self.headers.to_http() body = self.body if self.headers.one('Content-Type', '').startswith('text/'): body = body.replace('\n', '\r\n') body = body.replace('\r\r', '\r') return '\r\n'.join([status_line, headers, '', body])
class Request(object): """Represent an HTTP Request message. Attributes: http://sync.in/aspen-request """ def hydrate(self): """Populate a number of attributes on self based on primitives. """ self.body = WwwForm(self.raw_body) self.headers = Headers(self.raw_headers) self.cookie = SimpleCookie() try: self.cookie.load(self.headers.one('Cookie', '')) except CookieError: pass urlparts = urlparse.urlparse(self.raw_url) self.path = Path(urlparts[2]) # populated by Website self.raw_querystring = urlparts[4] self.qs = WwwForm(self.raw_querystring) self.url = self.rebuild_url() # needs things above self.urlparts = urlparse.urlparse(self.url) self.transport = None # set by Website for *.sock files self.session_id = None # set by Website for *.sock files self.root = '' # set by Website self.fs = '' # set by Website self.namespace = {} # populated by user in inbound hooks @classmethod def from_diesel(cls, request): """Set primitives from a diesel request. """ self = cls() self._diesel_request = request self.method = request.method self.version = request.version self.raw_headers = request.headers and request.headers.format() or '' self.raw_body = request.body and request.body or '' self.remote_addr = request.remote_addr and request.remote_addr or '' self.raw_url = request.url self.hydrate() return self def __str__(self): return "<%s %s>" % (self.method, self.path) __repr__ = __str__ def allow(self, *methods): """Given a list of methods, raise 405 if we don't meet the requirement. """ methods = [x.upper() for x in methods] if self.method not in methods: raise Response(405, headers={'Allow': ', '.join(methods)}) def rebuild_url(self): """Return a full URL for this request, per PEP 333: http://www.python.org/dev/peps/pep-0333/#url-reconstruction This function is kind of naive. """ # http://docs.python.org/library/wsgiref.html#wsgiref.util.guess_scheme scheme = self.headers.one('HTTPS') and 'https' or 'http' url = scheme url += '://' if 'X-Forwarded-Host' in self.headers: url += self.headers.one('X-Forwarded-Host') elif 'Host' in self.headers: url += self.headers.one('Host') else: # per spec, return 400 if no Host header given raise Response(400) url += urllib.quote(self.path.raw) # screw params, fragment? if self.raw_querystring: url += '?' + self.raw_querystring return url
class Response(Exception): """Represent an HTTP Response message. """ def __init__(self, code=200, body='', headers=None): """Takes an int, a string, and a dict (or list of tuples). - code an HTTP response code, e.g., 404 - body the message body as a string - headers a Headers instance - cookie a Cookie.SimpleCookie instance Code is first because when you're raising your own Responses, they're usually error conditions. Body is second because one more often wants to specify a body without headers, than a header without a body. """ if not isinstance(code, int): raise TypeError("'code' must be an integer") elif not isinstance(body, basestring): raise TypeError("'body' must be a string") elif headers is not None and not isinstance(headers, (dict, list)): raise TypeError("'headers' must be a dictionary or a list of " + "2-tuples") Exception.__init__(self) self.code = code self.body = body self.headers = Headers('') if headers: if isinstance(headers, dict): headers = headers.items() for k, v in headers: self.headers.add(k, v) self.cookie = SimpleCookie() try: self.cookie.load(self.headers.one('Cookie', '')) except CookieError: pass def __repr__(self): return "<Response: %s>" % str(self) def __str__(self): return "%d %s" % (self.code, self._status()) def _status(self): return status_strings.get(self.code, ('???','Unknown HTTP status')) def _to_diesel(self, _diesel_request): """This actually sends bits over the wire(!). """ for morsel in self.cookie.values(): self.headers.add('Set-Cookie', morsel.OutputString()) self.headers._diesel_headers._headers = self.headers._dict return DieselResponse( _diesel_request , self.code , self.headers._diesel_headers , self.body ) def _to_http(self, version): status_line = "HTTP/%s" % version headers = self.headers.to_http() body = self.body if self.headers.one('Content-Type', '').startswith('text/'): body = body.replace('\n', '\r\n') body = body.replace('\r\r', '\r') return '\r\n'.join([status_line, headers, '', body])
def test_headers_are_case_insensitive(): headers = Headers('Foo: bar') expected = 'bar' actual = headers.one('foo') assert actual == expected, actual
class Request(object): """Represent an HTTP Request message. Attributes: http://sync.in/aspen-request """ def hydrate(self): """Populate a number of attributes on self based on primitives. """ self.body = WwwForm(self.raw_body) self.headers = Headers(self.raw_headers) self.cookie = SimpleCookie() try: self.cookie.load(self.headers.one('Cookie', '')) except CookieError: pass urlparts = urlparse.urlparse(self.raw_url) self.path = Path(urlparts[2]) # populated by Website self.raw_querystring = urlparts[4] self.qs = WwwForm(self.raw_querystring) self.url = self.rebuild_url() # needs things above self.urlparts = urlparse.urlparse(self.url) self.transport = None # set by Website for *.sock files self.session_id = None # set by Website for *.sock files self.root = '' # set by Website self.fs = '' # set by Website self.namespace = {} # populated by user in inbound hooks @classmethod def from_wsgi(cls, environ): """Set primitives from a WSGI environ. """ self = cls() self.method = environ['REQUEST_METHOD'] self.version = environ['SERVER_PROTOCOL'] self.remote_addr = environ.get('REMOTE_ADDR', None) # relaxed for Pants self.raw_url = environ['PATH_INFO'] # Headers # ======= raw_headers = [] for k, v in environ.items(): if k.startswith('HTTP_'): k = k[len('HTTP_'):] raw_headers.append(': '.join([k, v])) raw_headers = '\r\n'.join(raw_headers) self.raw_headers = raw_headers # Body # ==== if not environ.get('SERVER_SOFTWARE', '').startswith('Rocket'): # normal case self.raw_body = environ['wsgi.input'].read() else: # rocket engine # Email from Rocket guy: While HTTP stipulates that you shouldn't # read a socket unless you are specifically expecting data to be # there, WSGI allows (but doesn't require) for that # (http://www.python.org/dev/peps/pep-3333/#id25). I've started # support for this (if you feel like reading the code, its in the # connection class) but this feature is not yet production ready # (it works but it way too slow on cPython). # # The hacky solution is to grab the socket from the stream and # manually set the timeout to 0 and set it back when you get your # data (or not). # # If you're curious, those HTTP conditions are (it's better to do # this anyway to avoid unnecessary and slow OS calls): # - You can assume that there will be content in the body if the # request type is "POST" or "PUT" # - You can figure how much to read by the "CONTENT_LENGTH" header # existence with a valid integer value # - alternatively "CONTENT_TYPE" can be set with no length and # you can read based on the body content ("content-encoding" = # "chunked" is a good example). # # Here's the "hacky solution": _tmp = environ['wsgi.input']._sock.timeout environ['wsgi.input']._sock.settimeout(0) # equiv. to non-blocking try: self.raw_body = environ['wsgi.input'].read() except Exception, exc: if exc.errno != 35: raise self.raw_body = "" environ['wsgi.input']._sock.settimeout(_tmp) self.hydrate() return self
def test_headers_one_gets_a_value(): headers = Headers("Foo: Bar") expected = "Bar" actual = headers.one("Foo") assert actual == expected, actual