def __call__(self, env, start_response): """WSGI "app" method Makes instances of API callable by any WSGI server. See also PEP 333. Args: env: A WSGI environment dictionary start_response: A WSGI helper method for setting status and headers on a response. """ req = Request(env) resp = Response() responder, params, na_responder = self._get_responder( req.path, req.method) try: responder(req, resp, **params) except HTTPError as ex: resp.status = ex.status if ex.headers is not None: resp.set_headers(ex.headers) if req.client_accepts('application/json'): resp.body = ex.json() # # Set status and headers # use_body = not helpers.should_ignore_body(resp.status, req.method) if use_body: helpers.set_content_length(resp) body = helpers.get_body(resp) else: # Default: return an empty body body = [] # Set content type if needed use_content_type = (body or req.method == 'HEAD' or resp.status == HTTP_416) if use_content_type: media_type = self._media_type else: media_type = None headers = resp._wsgi_headers(media_type) # Return the response per the WSGI spec start_response(resp.status, headers) return body
def test_default_request_context(self): env = testing.create_environ() req = Request(env) req.context.hello = 'World' assert req.context.hello == 'World' assert req.context['hello'] == 'World' req.context['note'] = 'Default Request.context_type used to be dict.' assert 'note' in req.context assert hasattr(req.context, 'note') assert req.context.get('note') == req.context['note']
def before(self): self.qs = 'marker=deadbeef&limit=10' self.headers = { 'Host': 'falcon.example.com', 'Content-Type': 'text/plain', 'Content-Length': '4829', 'Authorization': '' } self.app = '/test' self.path = '/hello' self.relative_uri = self.path + '?' + self.qs self.uri = 'http://falcon.example.com' + self.app + self.relative_uri self.uri_noqs = 'http://falcon.example.com' + self.app + self.path self.req = Request(testing.create_environ( app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) self.req_noqs = Request(testing.create_environ( app=self.app, path='/hello', headers=self.headers))
class TestReqVars(helpers.TestSuite): def prepare(self): qs = '?marker=deadbeef&limit=10' headers = { 'Content-Type': 'text/plain', 'Content-Length': '4829' } self.req = Request(helpers.create_environ(script='/test', path='/hello', query_string=qs, headers=headers)) def test_reconstruct_url(self): req = self.req scheme = req.protocol host = req.get_header('host') app = req.app path = req.path query_string = req.query_string expected_url = 'http://falconer/test/hello?marker=deadbeef&limit=10' actual_url = ''.join([scheme, '://', host, app, path, query_string]) self.assertEquals(actual_url, expected_url) def test_method(self): self.assertEquals(self.req.method, 'GET') self.req = Request(helpers.create_environ(path='', method='HEAD')) self.assertEquals(self.req.method, 'HEAD') def test_empty_path(self): self.req = Request(helpers.create_environ(path='')) self.assertEquals(self.req.path, '/') def test_content_type(self): self.assertEquals(self.req.get_header('content-type'), 'text/plain') def test_content_length(self): self.assertEquals(self.req.get_header('content-length'), '4829')
def prepare(self): qs = '?marker=deadbeef&limit=10' headers = { 'Content-Type': 'text/plain', 'Content-Length': '4829' } self.req = Request(helpers.create_environ(script='/test', path='/hello', query_string=qs, headers=headers))
def prepare(self): qs = '?marker=deadbeef&limit=10' headers = { 'Content-Type': 'text/plain', 'Content-Length': '4829', } self.url = 'http://falconer/test/hello?marker=deadbeef&limit=10' self.req = Request(testing.create_environ(script='/test', path='/hello', query_string=qs, headers=headers))
def test_client_prefers(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xml']) self.assertEqual(preferred_type, 'application/xml') headers = {'Accept': '*/*'} preferred_type = req.client_prefers(('application/xml', 'application/json')) # NOTE(kgriffs): If client doesn't care, "prefer" the first one self.assertEqual(preferred_type, 'application/xml') headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) self.assertEqual(preferred_type, 'application/xhtml+xml') headers = {'Accept': '3p12845j;;;asfd;'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) self.assertEqual(preferred_type, None)
def setup_method(self, method): self.qs = 'marker=deadbeef&limit=10' self.headers = { 'Content-Type': 'text/plain', 'Content-Length': '4829', 'Authorization': '' } self.app = '/test' self.path = '/hello' self.relative_uri = self.path + '?' + self.qs self.req = Request(testing.create_environ( app=self.app, port=8080, path='/hello', query_string=self.qs, headers=self.headers)) self.req_noqs = Request(testing.create_environ( app=self.app, path='/hello', headers=self.headers))
def test_method(self): assert self.req.method == 'GET' self.req = Request(testing.create_environ(path='', method='HEAD')) assert self.req.method == 'HEAD'
class TestRequestAttributes: def setup_method(self, method): self.qs = 'marker=deadbeef&limit=10' self.headers = { 'Content-Type': 'text/plain', 'Content-Length': '4829', 'Authorization': '' } self.app = '/test' self.path = '/hello' self.relative_uri = self.path + '?' + self.qs self.req = Request(testing.create_environ( app=self.app, port=8080, path='/hello', query_string=self.qs, headers=self.headers)) self.req_noqs = Request(testing.create_environ( app=self.app, path='/hello', headers=self.headers)) def test_missing_qs(self): env = testing.create_environ() if 'QUERY_STRING' in env: del env['QUERY_STRING'] # Should not cause an exception when Request is instantiated Request(env) def test_empty(self): assert self.req.auth is None def test_host(self): assert self.req.host == testing.DEFAULT_HOST def test_subdomain(self): req = Request(testing.create_environ( host='com', path='/hello', headers=self.headers)) assert req.subdomain is None req = Request(testing.create_environ( host='example.com', path='/hello', headers=self.headers)) assert req.subdomain == 'example' req = Request(testing.create_environ( host='highwire.example.com', path='/hello', headers=self.headers)) assert req.subdomain == 'highwire' req = Request(testing.create_environ( host='lb01.dfw01.example.com', port=8080, path='/hello', headers=self.headers)) assert req.subdomain == 'lb01' # NOTE(kgriffs): Behavior for IP addresses is undefined, # so just make sure it doesn't blow up. req = Request(testing.create_environ( host='127.0.0.1', path='/hello', headers=self.headers)) assert type(req.subdomain) == str # NOTE(kgriffs): Test fallback to SERVER_NAME by using # HTTP 1.0, which will cause .create_environ to not set # HTTP_HOST. req = Request(testing.create_environ( protocol='HTTP/1.0', host='example.com', path='/hello', headers=self.headers)) assert req.subdomain == 'example' def test_reconstruct_url(self): req = self.req scheme = req.scheme host = req.get_header('host') app = req.app path = req.path query_string = req.query_string expected_prefix = ''.join([scheme, '://', host, app]) expected_uri = ''.join([expected_prefix, path, '?', query_string]) assert req.uri == expected_uri assert req.prefix == expected_prefix assert req.prefix == expected_prefix # Check cached value @pytest.mark.parametrize('test_path', [ '/hello_\u043f\u0440\u0438\u0432\u0435\u0442', '/test/%E5%BB%B6%E5%AE%89', '/test/%C3%A4%C3%B6%C3%BC%C3%9F%E2%82%AC', ]) def test_nonlatin_path(self, test_path): # NOTE(kgriffs): When a request comes in, web servers decode # the path. The decoded path may contain UTF-8 characters, # but according to the WSGI spec, no strings can contain chars # outside ISO-8859-1. Therefore, to reconcile the URI # encoding standard that allows UTF-8 with the WSGI spec # that does not, WSGI servers tunnel the string via # ISO-8859-1. falcon.testing.create_environ() mimics this # behavior, e.g.: # # tunnelled_path = path.encode('utf-8').decode('iso-8859-1') # # falcon.Request does the following to reverse the process: # # path = tunnelled_path.encode('iso-8859-1').decode('utf-8', 'replace') # req = Request(testing.create_environ( host='com', path=test_path, headers=self.headers)) assert req.path == falcon.uri.decode(test_path) def test_uri(self): prefix = 'http://' + testing.DEFAULT_HOST + ':8080' + self.app uri = prefix + self.relative_uri assert self.req.url == uri assert self.req.prefix == prefix # NOTE(kgriffs): Call twice to check caching works assert self.req.uri == uri assert self.req.uri == uri uri_noqs = ('http://' + testing.DEFAULT_HOST + self.app + self.path) assert self.req_noqs.uri == uri_noqs def test_uri_https(self): # ======================================================= # Default port, implicit # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https')) uri = ('https://' + testing.DEFAULT_HOST + '/hello') assert req.uri == uri # ======================================================= # Default port, explicit # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https', port=443)) uri = ('https://' + testing.DEFAULT_HOST + '/hello') assert req.uri == uri # ======================================================= # Non-default port # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https', port=22)) uri = ('https://' + testing.DEFAULT_HOST + ':22/hello') assert req.uri == uri def test_uri_http_1_0(self): # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', app=self.app, port=80, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('http://' + testing.DEFAULT_HOST + self.app + self.relative_uri) assert req.uri == uri # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', app=self.app, port=8080, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('http://' + testing.DEFAULT_HOST + ':8080' + self.app + self.relative_uri) assert req.uri == uri # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', scheme='https', app=self.app, port=443, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('https://' + testing.DEFAULT_HOST + self.app + self.relative_uri) assert req.uri == uri # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', scheme='https', app=self.app, port=22, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('https://' + testing.DEFAULT_HOST + ':22' + self.app + self.relative_uri) assert req.uri == uri def test_relative_uri(self): assert self.req.relative_uri == self.app + self.relative_uri assert self.req_noqs.relative_uri == self.app + self.path req_noapp = Request(testing.create_environ( path='/hello', query_string=self.qs, headers=self.headers)) assert req_noapp.relative_uri == self.relative_uri req_noapp = Request(testing.create_environ( path='/hello/', query_string=self.qs, headers=self.headers)) relative_trailing_uri = self.path + '/?' + self.qs # NOTE(kgriffs): Call twice to check caching works assert req_noapp.relative_uri == relative_trailing_uri assert req_noapp.relative_uri == relative_trailing_uri options = RequestOptions() options.strip_url_path_trailing_slash = False req_noapp = Request(testing.create_environ( path='/hello/', query_string=self.qs, headers=self.headers), options=options) assert req_noapp.relative_uri == '/hello/' + '?' + self.qs def test_client_accepts(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/xml') headers = {'Accept': '*/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/xml') assert req.client_accepts('application/json') assert req.client_accepts('application/x-msgpack') headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts('application/xml') assert not req.client_accepts('application/json') assert req.client_accepts('application/x-msgpack') headers = {} # NOTE(kgriffs): Equivalent to '*/*' per RFC req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/xml') headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts('application/xml') headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/x-msgpack') headers = {'Accept': 'application/xm'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts('application/xml') headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/json') assert req.client_accepts('application/xml') assert req.client_accepts('application/x-msgpack') headers = {'Accept': 'text/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('text/csv') assert not req.client_accepts('application/xhtml+xml') headers = {'Accept': 'text/*, application/xhtml+xml; q=0.0'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('text/csv') assert not req.client_accepts('application/xhtml+xml') headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('application/xhtml+xml') headers = {'Accept': 'text/*, application/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('application/xml') assert req.client_accepts('application/json') assert req.client_accepts('application/x-msgpack') headers = {'Accept': 'text/*,application/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('application/xml') assert req.client_accepts('application/json') assert req.client_accepts('application/x-msgpack') def test_client_accepts_bogus(self): headers = {'Accept': '~'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts('text/plain') assert not req.client_accepts('application/json') def test_client_accepts_props(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts_xml assert not req.client_accepts_json assert not req.client_accepts_msgpack headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts_xml assert req.client_accepts_json assert req.client_accepts_msgpack headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts_xml assert req.client_accepts_json assert not req.client_accepts_msgpack headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts_xml assert not req.client_accepts_json assert req.client_accepts_msgpack headers = {'Accept': 'application/msgpack'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts_xml assert not req.client_accepts_json assert req.client_accepts_msgpack headers = { 'Accept': 'application/json,application/xml,application/x-msgpack' } req = Request(testing.create_environ(headers=headers)) assert req.client_accepts_xml assert req.client_accepts_json assert req.client_accepts_msgpack def test_client_prefers(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xml']) assert preferred_type == 'application/xml' headers = {'Accept': '*/*'} preferred_type = req.client_prefers(('application/xml', 'application/json')) # NOTE(kgriffs): If client doesn't care, "prefer" the first one assert preferred_type == 'application/xml' headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) assert preferred_type == 'application/xhtml+xml' headers = {'Accept': '3p12845j;;;asfd;'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) assert preferred_type is None def test_range(self): headers = {'Range': 'bytes=10-'} req = Request(testing.create_environ(headers=headers)) assert req.range == (10, -1) headers = {'Range': 'bytes=10-20'} req = Request(testing.create_environ(headers=headers)) assert req.range == (10, 20) headers = {'Range': 'bytes=-10240'} req = Request(testing.create_environ(headers=headers)) assert req.range == (-10240, -1) headers = {'Range': 'bytes=0-2'} req = Request(testing.create_environ(headers=headers)) assert req.range == (0, 2) headers = {'Range': ''} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPInvalidHeader): req.range req = Request(testing.create_environ()) assert req.range is None def test_range_unit(self): headers = {'Range': 'bytes=10-'} req = Request(testing.create_environ(headers=headers)) assert req.range == (10, -1) assert req.range_unit == 'bytes' headers = {'Range': 'items=10-'} req = Request(testing.create_environ(headers=headers)) assert req.range == (10, -1) assert req.range_unit == 'items' headers = {'Range': ''} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPInvalidHeader): req.range_unit req = Request(testing.create_environ()) assert req.range_unit is None def test_range_invalid(self): headers = {'Range': 'bytes=10240'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=-'} expected_desc = ('The value provided for the Range header is ' 'invalid. The range offsets are missing.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': 'bytes=--'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=-3-'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=-3-4'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=3-3-4'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=3-3-'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=3-3- '} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=fizbit'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=a-'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=a-3'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=-b'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=3-b'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=x-y'} expected_desc = ('The value provided for the Range header is ' 'invalid. It must be a range formatted ' 'according to RFC 7233.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': 'bytes=0-0,-1'} expected_desc = ('The value provided for the Range ' 'header is invalid. The value must be a ' 'continuous range.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': '10-'} expected_desc = ('The value provided for the Range ' 'header is invalid. The value must be ' "prefixed with a range unit, e.g. 'bytes='") self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) def test_missing_attribute_header(self): req = Request(testing.create_environ()) assert req.range is None req = Request(testing.create_environ()) assert req.content_length is None def test_content_length(self): headers = {'content-length': '5656'} req = Request(testing.create_environ(headers=headers)) assert req.content_length == 5656 headers = {'content-length': ''} req = Request(testing.create_environ(headers=headers)) assert req.content_length is None def test_bogus_content_length_nan(self): headers = {'content-length': 'fuzzy-bunnies'} expected_desc = ('The value provided for the ' 'Content-Length header is invalid. The value ' 'of the header must be a number.') self._test_error_details(headers, 'content_length', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) def test_bogus_content_length_neg(self): headers = {'content-length': '-1'} expected_desc = ('The value provided for the Content-Length ' 'header is invalid. The value of the header ' 'must be a positive number.') self._test_error_details(headers, 'content_length', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) @pytest.mark.parametrize('header,attr', [ ('Date', 'date'), ('If-Modified-Since', 'if_modified_since'), ('If-Unmodified-Since', 'if_unmodified_since'), ]) def test_date(self, header, attr): date = datetime.datetime(2013, 4, 4, 5, 19, 18) date_str = 'Thu, 04 Apr 2013 05:19:18 GMT' self._test_header_expected_value(header, date_str, attr, date) @pytest.mark.parametrize('header,attr', [ ('Date', 'date'), ('If-Modified-Since', 'if_modified_since'), ('If-Unmodified-Since', 'if_unmodified_since'), ]) def test_date_invalid(self, header, attr): # Date formats don't conform to RFC 1123 headers = {header: 'Thu, 04 Apr 2013'} expected_desc = ('The value provided for the {} ' 'header is invalid. It must be formatted ' 'according to RFC 7231, Section 7.1.1.1') self._test_error_details(headers, attr, falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc.format(header)) headers = {header: ''} self._test_error_details(headers, attr, falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc.format(header)) @pytest.mark.parametrize('attr', ('date', 'if_modified_since', 'if_unmodified_since')) def test_date_missing(self, attr): req = Request(testing.create_environ()) assert getattr(req, attr) is None def test_attribute_headers(self): date = 'Wed, 21 Oct 2015 07:28:00 GMT' auth = 'HMAC_SHA1 c590afa9bb59191ffab30f223791e82d3fd3e3af' agent = 'testing/1.0.1' default_agent = 'curl/7.24.0 (x86_64-apple-darwin12.0)' referer = 'https://www.google.com/' self._test_attribute_header('Accept', 'x-falcon', 'accept', default='*/*') self._test_attribute_header('Authorization', auth, 'auth') self._test_attribute_header('Content-Type', 'text/plain', 'content_type') self._test_attribute_header('Expect', '100-continue', 'expect') self._test_attribute_header('If-Range', date, 'if_range') self._test_attribute_header('User-Agent', agent, 'user_agent', default=default_agent) self._test_attribute_header('Referer', referer, 'referer') def test_method(self): assert self.req.method == 'GET' self.req = Request(testing.create_environ(path='', method='HEAD')) assert self.req.method == 'HEAD' def test_empty_path(self): self.req = Request(testing.create_environ(path='')) assert self.req.path == '/' def test_content_type_method(self): assert self.req.get_header('content-type') == 'text/plain' def test_content_length_method(self): assert self.req.get_header('content-length') == '4829' # TODO(kgriffs): Migrate to pytest and parametrized fixtures # to DRY things up a bit. @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_port_explicit(self, protocol): port = 9000 req = Request(testing.create_environ( protocol=protocol, port=port, app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) assert req.port == port @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_scheme_https(self, protocol): scheme = 'https' req = Request(testing.create_environ( protocol=protocol, scheme=scheme, app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) assert req.scheme == scheme assert req.port == 443 @pytest.mark.parametrize( 'protocol, set_forwarded_proto', list(itertools.product(_PROTOCOLS, [True, False])) ) def test_scheme_http(self, protocol, set_forwarded_proto): scheme = 'http' forwarded_scheme = 'HttPs' headers = dict(self.headers) if set_forwarded_proto: headers['X-Forwarded-Proto'] = forwarded_scheme req = Request(testing.create_environ( protocol=protocol, scheme=scheme, app=self.app, path='/hello', query_string=self.qs, headers=headers)) assert req.scheme == scheme assert req.port == 80 if set_forwarded_proto: assert req.forwarded_scheme == forwarded_scheme.lower() else: assert req.forwarded_scheme == scheme @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_netloc_default_port(self, protocol): req = Request(testing.create_environ( protocol=protocol, app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) assert req.netloc == 'falconframework.org' @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_netloc_nondefault_port(self, protocol): req = Request(testing.create_environ( protocol=protocol, port='8080', app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) assert req.netloc == 'falconframework.org:8080' @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_netloc_from_env(self, protocol): port = 9000 host = 'example.org' env = testing.create_environ( protocol=protocol, host=host, port=port, app=self.app, path='/hello', query_string=self.qs, headers=self.headers) req = Request(env) assert req.port == port assert req.netloc == '{}:{}'.format(host, port) def test_app_present(self): req = Request(testing.create_environ(app='/moving-pictures')) assert req.app == '/moving-pictures' def test_app_blank(self): req = Request(testing.create_environ(app='')) assert req.app == '' def test_app_missing(self): env = testing.create_environ() del env['SCRIPT_NAME'] req = Request(env) assert req.app == '' @pytest.mark.parametrize('etag,expected', [ ('', None), (' ', None), (' ', None), ('\t', None), (' \t', None), (',', None), (',,', None), (',, ', None), (', , ', None), ('*', ['*']), ( 'W/"67ab43"', [_make_etag('67ab43', is_weak=True)] ), ( 'w/"67ab43"', [_make_etag('67ab43', is_weak=True)] ), ( ' w/"67ab43"', [_make_etag('67ab43', is_weak=True)] ), ( 'w/"67ab43" ', [_make_etag('67ab43', is_weak=True)] ), ( 'w/"67ab43 " ', [_make_etag('67ab43 ', is_weak=True)] ), ( '"67ab43"', [_make_etag('67ab43')] ), ( ' "67ab43"', [_make_etag('67ab43')] ), ( ' "67ab43" ', [_make_etag('67ab43')] ), ( '"67ab43" ', [_make_etag('67ab43')] ), ( '" 67ab43" ', [_make_etag(' 67ab43')] ), ( '67ab43"', [_make_etag('67ab43"')] ), ( '"67ab43', [_make_etag('"67ab43')] ), ( '67ab43', [_make_etag('67ab43')] ), ( '67ab43 ', [_make_etag('67ab43')] ), ( ' 67ab43 ', [_make_etag('67ab43')] ), ( ' 67ab43', [_make_etag('67ab43')] ), ( # NOTE(kgriffs): To simplify parsing and improve performance, we # do not attempt to handle unquoted entity-tags when there is # a list; it is non-standard anyway, and has been since 1999. 'W/"67ab43", "54ed21", junk"F9,22", junk "41, 7F", unquoted, w/"22, 41, 7F", "", W/""', [ _make_etag('67ab43', is_weak=True), _make_etag('54ed21'), # NOTE(kgriffs): Test that the ETag initializer defaults to # is_weak == False ETag('F9,22'), _make_etag('41, 7F'), _make_etag('22, 41, 7F', is_weak=True), # NOTE(kgriffs): According to the grammar in RFC 7232, zero # etagc's is acceptable. _make_etag(''), _make_etag('', is_weak=True), ] ), ]) def test_etag(self, etag, expected): self._test_header_etag('If-Match', etag, 'if_match', expected) self._test_header_etag('If-None-Match', etag, 'if_none_match', expected) def test_etag_is_missing(self): # NOTE(kgriffs): Loop in order to test caching for __ in range(3): assert self.req.if_match is None assert self.req.if_none_match is None @pytest.mark.parametrize('header_value', ['', ' ', ' ']) def test_etag_parsing_helper(self, header_value): # NOTE(kgriffs): Test a couple of cases that are not directly covered # elsewhere (but that we want the helper to still support # for the sake of avoiding suprises if they are ever called without # preflighting the header value). assert _parse_etags(header_value) is None # ------------------------------------------------------------------------- # Helpers # ------------------------------------------------------------------------- def _test_attribute_header(self, name, value, attr, default=None): headers = {name: value} req = Request(testing.create_environ(headers=headers)) assert getattr(req, attr) == value req = Request(testing.create_environ()) assert getattr(req, attr) == default def _test_header_expected_value(self, name, value, attr, expected_value): headers = {name: value} req = Request(testing.create_environ(headers=headers)) assert getattr(req, attr) == expected_value def _test_header_etag(self, name, value, attr, expected_value): headers = {name: value} req = Request(testing.create_environ(headers=headers)) # NOTE(kgriffs): Loop in order to test caching for __ in range(3): value = getattr(req, attr) if expected_value is None: assert value is None return assert value is not None for element, expected_element in zip(value, expected_value): assert element == expected_element if isinstance(expected_element, ETag): assert element.is_weak == expected_element.is_weak def _test_error_details(self, headers, attr_name, error_type, title, description): req = Request(testing.create_environ(headers=headers)) try: getattr(req, attr_name) pytest.fail('{} not raised'.format(error_type.__name__)) except error_type as ex: assert ex.title == title assert ex.description == description
def _test_header_expected_value(self, name, value, attr, expected_value): headers = {name: value} req = Request(testing.create_environ(headers=headers)) self.assertEqual(getattr(req, attr), expected_value)
def test_method(self): self.assertEqual(self.req.method, 'GET') self.req = Request(testing.create_environ(path='', method='HEAD')) self.assertEqual(self.req.method, 'HEAD')
def test_missing_attribute_header(self): req = Request(testing.create_environ()) self.assertEqual(req.range, None) req = Request(testing.create_environ()) self.assertEqual(req.content_length, None)
def test_client_accepts_bogus(self): headers = {'Accept': '~'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('text/plain')) self.assertFalse(req.client_accepts('application/json'))
def __call__(self, env, start_response): """WSGI "app" method Makes instances of API callable by any WSGI server. See also PEP 333. Args: env: A WSGI environment dictionary start_response: A WSGI helper method for setting status and headers on a response. """ req = Request(env) resp = Response() responder, params = self._get_responder( req.path, req.method) try: # NOTE(kgriffs): Using an inner try..except in order to # address the case when err_handler raises HTTPError. # # NOTE(kgriffs): Coverage is giving false negatives, # so disabled on relevant lines. All paths are tested # afaict. try: responder(req, resp, **params) # pragma: no cover except Exception as ex: for err_type, err_handler in self._error_handlers: if isinstance(ex, err_type): err_handler(ex, req, resp, params) break # pragma: no cover else: # PERF(kgriffs): This will propagate HTTPError to # the handler below. It makes handling HTTPError # less efficient, but that is OK since error cases # don't need to be as fast as the happy path, and # indeed, should perhaps be slower to create # backpressure on clients that are issuing bad # requests. raise except HTTPError as ex: resp.status = ex.status if ex.headers is not None: resp.set_headers(ex.headers) if req.client_accepts('application/json'): resp.body = ex.json() # # Set status and headers # use_body = not helpers.should_ignore_body(resp.status, req.method) if use_body: helpers.set_content_length(resp) body = helpers.get_body(resp) else: # Default: return an empty body body = [] # Set content type if needed use_content_type = (body or req.method == 'HEAD' or resp.status == HTTP_416) if use_content_type: media_type = self._media_type else: media_type = None headers = resp._wsgi_headers(media_type) # Return the response per the WSGI spec start_response(resp.status, headers) return body
def test_app_blank(self): req = Request(testing.create_environ(app='')) assert req.app == ''
def test_app_present(self): req = Request(testing.create_environ(app='/moving-pictures')) assert req.app == '/moving-pictures'
def test_empty_path(self): self.req = Request(testing.create_environ(path='')) assert self.req.path == '/'
def test_date_missing(self, attr): req = Request(testing.create_environ()) assert getattr(req, attr) is None
def test_missing_attribute_header(self): req = Request(testing.create_environ()) assert req.range is None req = Request(testing.create_environ()) assert req.content_length is None
class TestReqVars(testing.TestBase): def before(self): self.qs = 'marker=deadbeef&limit=10' self.headers = { 'Host': 'falcon.example.com', 'Content-Type': 'text/plain', 'Content-Length': '4829', 'Authorization': '' } self.app = '/test' self.path = '/hello' self.relative_uri = self.path + '?' + self.qs self.uri = 'http://falcon.example.com' + self.app + self.relative_uri self.uri_noqs = 'http://falcon.example.com' + self.app + self.path self.req = Request(testing.create_environ( app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) self.req_noqs = Request(testing.create_environ( app=self.app, path='/hello', headers=self.headers)) def test_missing_qs(self): env = testing.create_environ() if 'QUERY_STRING' in env: del env['QUERY_STRING'] # Should not cause an exception when Request is instantiated Request(env) def test_empty(self): self.assertIs(self.req.auth, None) def test_reconstruct_url(self): req = self.req scheme = req.protocol host = req.get_header('host') app = req.app path = req.path query_string = req.query_string actual_url = ''.join([scheme, '://', host, app, path, '?', query_string]) self.assertEquals(actual_url, self.uri) def test_uri(self): self.assertEquals(self.req.url, self.uri) self.assertEquals(self.req.uri, self.uri) self.assertEquals(self.req_noqs.uri, self.uri_noqs) def test_relative_uri(self): self.assertEqual(self.req.relative_uri, self.app + self.relative_uri) self.assertEqual( self.req_noqs.relative_uri, self.app + self.path) req_noapp = Request(testing.create_environ( path='/hello', query_string=self.qs, headers=self.headers)) self.assertEqual(req_noapp.relative_uri, self.relative_uri) req_noapp = Request(testing.create_environ( path='/hello/', query_string=self.qs, headers=self.headers)) self.assertEqual(req_noapp.relative_uri, self.relative_uri) def test_accept_xml(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts_xml) headers = {'Accept': '*/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts_xml) headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts_xml) headers = {'Accept': 'application/xm'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts_xml) def test_range(self): headers = {'Range': '10-'} req = Request(testing.create_environ(headers=headers)) self.assertEquals(req.range, (10, -1)) headers = {'Range': '10-20'} req = Request(testing.create_environ(headers=headers)) self.assertEquals(req.range, (10, 20)) headers = {'Range': '-10240'} req = Request(testing.create_environ(headers=headers)) self.assertEquals(req.range, (-10240, -1)) headers = {'Range': ''} req = Request(testing.create_environ(headers=headers)) self.assertIs(req.range, None) headers = {'Range': None} req = Request(testing.create_environ(headers=headers)) self.assertIs(req.range, None) def test_range_invalid(self): headers = {'Range': '10240'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '--'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '-3-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '3-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '3-3-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '3-3- '} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'fizbit'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'a-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'a-3'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '-b'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '3-b'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'x-y'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=0-0,-1'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) def test_missing_attribute_header(self): req = Request(testing.create_environ()) self.assertEquals(req.range, None) req = Request(testing.create_environ()) self.assertEquals(req.content_length, None) def test_content_length(self): headers = {'content-length': '5656'} req = Request(testing.create_environ(headers=headers)) self.assertEquals(req.content_length, 5656) headers = {'content-length': ''} req = Request(testing.create_environ(headers=headers)) self.assertEquals(req.content_length, None) def test_bogus_content_length_nan(self): headers = {'content-length': 'fuzzy-bunnies'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.content_length) def test_bogus_content_length_neg(self): headers = {'content-length': '-1'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.content_length) def test_date(self): date = datetime.datetime(2013, 4, 4, 5, 19, 18) headers = {'date': 'Thu, 04 Apr 2013 05:19:18 GMT'} req = Request(testing.create_environ(headers=headers)) self.assertEquals(req.date, date) def test_date_invalid(self): headers = {'date': 'Thu, 04 Apr 2013'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.date) def test_attribute_headers(self): date = testing.httpnow() hash = 'fa0d1a60ef6616bb28038515c8ea4cb2' auth = 'HMAC_SHA1 c590afa9bb59191ffab30f223791e82d3fd3e3af' agent = 'curl/7.24.0 (x86_64-apple-darwin12.0)' self._test_attribute_header('Accept', 'x-falcon', 'accept') self._test_attribute_header('Authorization', auth, 'auth') self._test_attribute_header('Content-Type', 'text/plain', 'content_type') self._test_attribute_header('Expect', '100-continue', 'expect') self._test_attribute_header('If-Match', hash, 'if_match') self._test_attribute_header('If-Modified-Since', date, 'if_modified_since') self._test_attribute_header('If-None-Match', hash, 'if_none_match') self._test_attribute_header('If-Range', hash, 'if_range') self._test_attribute_header('If-Unmodified-Since', date, 'if_unmodified_since') self._test_attribute_header('User-Agent', agent, 'user_agent') def test_method(self): self.assertEquals(self.req.method, 'GET') self.req = Request(testing.create_environ(path='', method='HEAD')) self.assertEquals(self.req.method, 'HEAD') def test_empty_path(self): self.req = Request(testing.create_environ(path='')) self.assertEquals(self.req.path, '/') def test_content_type_method(self): self.assertEquals(self.req.get_header('content-type'), 'text/plain') def test_content_length_method(self): self.assertEquals(self.req.get_header('content-length'), '4829') # ------------------------------------------------------------------------- # Helpers # ------------------------------------------------------------------------- def _test_attribute_header(self, name, value, attr): headers = {name: value} req = Request(testing.create_environ(headers=headers)) self.assertEquals(getattr(req, attr), value) headers = {name: None} req = Request(testing.create_environ(headers=headers)) self.assertEqual(getattr(req, attr), None)
class TestReqVars(testing.TestSuite): def prepare(self): qs = '?marker=deadbeef&limit=10' headers = { 'Content-Type': 'text/plain', 'Content-Length': '4829', } self.url = 'http://falconer/test/hello?marker=deadbeef&limit=10' self.req = Request(testing.create_environ(script='/test', path='/hello', query_string=qs, headers=headers)) def test_reconstruct_url(self): req = self.req scheme = req.protocol host = req.get_header('host') app = req.app path = req.path query_string = req.query_string actual_url = ''.join([scheme, '://', host, app, path, query_string]) self.assertEquals(self.url, actual_url) def test_url(self): self.assertEquals(self.url, self.req.url) def test_range(self): headers = {'Range': '10-'} req = Request(testing.create_environ(headers=headers)) self.assertEquals((10, -1), req.range) headers = {'Range': '10-20'} req = Request(testing.create_environ(headers=headers)) self.assertEquals((10, 20), req.range) headers = {'Range': '-10240'} req = Request(testing.create_environ(headers=headers)) self.assertEquals((-10240, -1), req.range) headers = {'Range': '10240'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(None, req.range) headers = {'Range': ''} req = Request(testing.create_environ(headers=headers)) self.assertEqual(None, req.range) headers = {'Range': '-'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(None, req.range) headers = {'Range': '--'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(None, req.range) headers = {'Range': '-3-'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(None, req.range) headers = {'Range': '-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(None, req.range) headers = {'Range': '3-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(None, req.range) headers = {'Range': '3-3-'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(None, req.range) headers = {'Range': None} req = Request(testing.create_environ(headers=headers)) self.assertEqual(None, req.range) def test_attribute_headers(self): date = testing.httpnow() hash = 'fa0d1a60ef6616bb28038515c8ea4cb2' auth = 'HMAC_SHA1 c590afa9bb59191ffab30f223791e82d3fd3e3af' agent = 'curl/7.24.0 (x86_64-apple-darwin12.0)' self._test_attribute_header('Accept', 'x-falcon', 'accept') self._test_attribute_header('Authorization', auth, 'auth') self._test_attribute_header('Content-Length', '4829', 'content_length') self._test_attribute_header('Content-Type', 'text/plain', 'content_type') self._test_attribute_header('Expect', '100-continue', 'expect') self._test_attribute_header('Date', date, 'date') self._test_attribute_header('If-Match', hash, 'if_match') self._test_attribute_header('If-Modified-Since', date, 'if_modified_since') self._test_attribute_header('If-None-Match', hash, 'if_none_match') self._test_attribute_header('If-Range', hash, 'if_range') self._test_attribute_header('If-Unmodified-Since', date, 'if_unmodified_since') self._test_attribute_header('User-Agent', agent, 'user_agent') def test_method(self): self.assertEquals(self.req.method, 'GET') self.req = Request(testing.create_environ(path='', method='HEAD')) self.assertEquals(self.req.method, 'HEAD') def test_empty_path(self): self.req = Request(testing.create_environ(path='')) self.assertEquals(self.req.path, '/') def test_content_type_method(self): self.assertEquals(self.req.get_header('content-type'), 'text/plain') def test_content_length_method(self): self.assertEquals(self.req.get_header('content-length'), '4829') # ------------------------------------------------------------------------- # Helpers # ------------------------------------------------------------------------- def _test_attribute_header(self, name, value, attr): headers = {name: value} req = Request(testing.create_environ(headers=headers)) self.assertEquals(value, getattr(req, attr)) headers = {name: None} req = Request(testing.create_environ(headers=headers)) self.assertEqual(None, getattr(req, attr))
def test_x_real_ip(self): req = Request( testing.create_environ(host='example.com', path='/access_route', headers={'X-Real-IP': '2001:db8:cafe::17'})) self.assertEqual(req.access_route, ['2001:db8:cafe::17'])
class TestRequestAttributes(object): def setup_method(self, method): self.qs = 'marker=deadbeef&limit=10' self.headers = { 'Content-Type': 'text/plain', 'Content-Length': '4829', 'Authorization': '' } self.app = '/test' self.path = '/hello' self.relative_uri = self.path + '?' + self.qs self.req = Request(testing.create_environ( app=self.app, port=8080, path='/hello', query_string=self.qs, headers=self.headers)) self.req_noqs = Request(testing.create_environ( app=self.app, path='/hello', headers=self.headers)) def test_missing_qs(self): env = testing.create_environ() if 'QUERY_STRING' in env: del env['QUERY_STRING'] # Should not cause an exception when Request is instantiated Request(env) def test_empty(self): assert self.req.auth is None def test_host(self): assert self.req.host == testing.DEFAULT_HOST def test_subdomain(self): req = Request(testing.create_environ( host='com', path='/hello', headers=self.headers)) assert req.subdomain is None req = Request(testing.create_environ( host='example.com', path='/hello', headers=self.headers)) assert req.subdomain == 'example' req = Request(testing.create_environ( host='highwire.example.com', path='/hello', headers=self.headers)) assert req.subdomain == 'highwire' req = Request(testing.create_environ( host='lb01.dfw01.example.com', port=8080, path='/hello', headers=self.headers)) assert req.subdomain == 'lb01' # NOTE(kgriffs): Behavior for IP addresses is undefined, # so just make sure it doesn't blow up. req = Request(testing.create_environ( host='127.0.0.1', path='/hello', headers=self.headers)) assert type(req.subdomain) == str # NOTE(kgriffs): Test fallback to SERVER_NAME by using # HTTP 1.0, which will cause .create_environ to not set # HTTP_HOST. req = Request(testing.create_environ( protocol='HTTP/1.0', host='example.com', path='/hello', headers=self.headers)) assert req.subdomain == 'example' def test_reconstruct_url(self): req = self.req scheme = req.scheme host = req.get_header('host') app = req.app path = req.path query_string = req.query_string expected_prefix = ''.join([scheme, '://', host, app]) expected_uri = ''.join([expected_prefix, path, '?', query_string]) assert req.uri == expected_uri assert req.prefix == expected_prefix assert req.prefix == expected_prefix # Check cached value @pytest.mark.skipif(not compat.PY3, reason='Test only applies to Python 3') @pytest.mark.parametrize('test_path', [ u'/hello_\u043f\u0440\u0438\u0432\u0435\u0442', u'/test/%E5%BB%B6%E5%AE%89', u'/test/%C3%A4%C3%B6%C3%BC%C3%9F%E2%82%AC', ]) def test_nonlatin_path(self, test_path): # NOTE(kgriffs): When a request comes in, web servers decode # the path. The decoded path may contain UTF-8 characters, # but according to the WSGI spec, no strings can contain chars # outside ISO-8859-1. Therefore, to reconcile the URI # encoding standard that allows UTF-8 with the WSGI spec # that does not, WSGI servers tunnel the string via # ISO-8859-1. falcon.testing.create_environ() mimics this # behavior, e.g.: # # tunnelled_path = path.encode('utf-8').decode('iso-8859-1') # # falcon.Request does the following to reverse the process: # # path = tunnelled_path.encode('iso-8859-1').decode('utf-8', 'replace') # req = Request(testing.create_environ( host='com', path=test_path, headers=self.headers)) assert req.path == falcon.uri.decode(test_path) def test_uri(self): prefix = 'http://' + testing.DEFAULT_HOST + ':8080' + self.app uri = prefix + self.relative_uri assert self.req.url == uri assert self.req.prefix == prefix # NOTE(kgriffs): Call twice to check caching works assert self.req.uri == uri assert self.req.uri == uri uri_noqs = ('http://' + testing.DEFAULT_HOST + self.app + self.path) assert self.req_noqs.uri == uri_noqs def test_uri_https(self): # ======================================================= # Default port, implicit # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https')) uri = ('https://' + testing.DEFAULT_HOST + '/hello') assert req.uri == uri # ======================================================= # Default port, explicit # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https', port=443)) uri = ('https://' + testing.DEFAULT_HOST + '/hello') assert req.uri == uri # ======================================================= # Non-default port # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https', port=22)) uri = ('https://' + testing.DEFAULT_HOST + ':22/hello') assert req.uri == uri def test_uri_http_1_0(self): # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', app=self.app, port=80, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('http://' + testing.DEFAULT_HOST + self.app + self.relative_uri) assert req.uri == uri # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', app=self.app, port=8080, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('http://' + testing.DEFAULT_HOST + ':8080' + self.app + self.relative_uri) assert req.uri == uri # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', scheme='https', app=self.app, port=443, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('https://' + testing.DEFAULT_HOST + self.app + self.relative_uri) assert req.uri == uri # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', scheme='https', app=self.app, port=22, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('https://' + testing.DEFAULT_HOST + ':22' + self.app + self.relative_uri) assert req.uri == uri def test_relative_uri(self): assert self.req.relative_uri == self.app + self.relative_uri assert self.req_noqs.relative_uri == self.app + self.path req_noapp = Request(testing.create_environ( path='/hello', query_string=self.qs, headers=self.headers)) assert req_noapp.relative_uri == self.relative_uri req_noapp = Request(testing.create_environ( path='/hello/', query_string=self.qs, headers=self.headers)) relative_trailing_uri = self.path + '/?' + self.qs # NOTE(kgriffs): Call twice to check caching works assert req_noapp.relative_uri == relative_trailing_uri assert req_noapp.relative_uri == relative_trailing_uri options = RequestOptions() options.strip_url_path_trailing_slash = False req_noapp = Request(testing.create_environ( path='/hello/', query_string=self.qs, headers=self.headers), options=options) assert req_noapp.relative_uri == '/hello/' + '?' + self.qs def test_client_accepts(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/xml') headers = {'Accept': '*/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/xml') assert req.client_accepts('application/json') assert req.client_accepts('application/x-msgpack') headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts('application/xml') assert not req.client_accepts('application/json') assert req.client_accepts('application/x-msgpack') headers = {} # NOTE(kgriffs): Equivalent to '*/*' per RFC req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/xml') headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts('application/xml') headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/x-msgpack') headers = {'Accept': 'application/xm'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts('application/xml') headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/json') assert req.client_accepts('application/xml') assert req.client_accepts('application/x-msgpack') headers = {'Accept': 'text/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('text/csv') assert not req.client_accepts('application/xhtml+xml') headers = {'Accept': 'text/*, application/xhtml+xml; q=0.0'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('text/csv') assert not req.client_accepts('application/xhtml+xml') headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('application/xhtml+xml') headers = {'Accept': 'text/*, application/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('application/xml') assert req.client_accepts('application/json') assert req.client_accepts('application/x-msgpack') headers = {'Accept': 'text/*,application/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('application/xml') assert req.client_accepts('application/json') assert req.client_accepts('application/x-msgpack') def test_client_accepts_bogus(self): headers = {'Accept': '~'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts('text/plain') assert not req.client_accepts('application/json') def test_client_accepts_props(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts_xml assert not req.client_accepts_json assert not req.client_accepts_msgpack headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts_xml assert req.client_accepts_json assert req.client_accepts_msgpack headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts_xml assert req.client_accepts_json assert not req.client_accepts_msgpack headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts_xml assert not req.client_accepts_json assert req.client_accepts_msgpack headers = {'Accept': 'application/msgpack'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts_xml assert not req.client_accepts_json assert req.client_accepts_msgpack headers = { 'Accept': 'application/json,application/xml,application/x-msgpack' } req = Request(testing.create_environ(headers=headers)) assert req.client_accepts_xml assert req.client_accepts_json assert req.client_accepts_msgpack def test_client_prefers(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xml']) assert preferred_type == 'application/xml' headers = {'Accept': '*/*'} preferred_type = req.client_prefers(('application/xml', 'application/json')) # NOTE(kgriffs): If client doesn't care, "prefer" the first one assert preferred_type == 'application/xml' headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) assert preferred_type == 'application/xhtml+xml' headers = {'Accept': '3p12845j;;;asfd;'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) assert preferred_type is None def test_range(self): headers = {'Range': 'bytes=10-'} req = Request(testing.create_environ(headers=headers)) assert req.range == (10, -1) headers = {'Range': 'bytes=10-20'} req = Request(testing.create_environ(headers=headers)) assert req.range == (10, 20) headers = {'Range': 'bytes=-10240'} req = Request(testing.create_environ(headers=headers)) assert req.range == (-10240, -1) headers = {'Range': 'bytes=0-2'} req = Request(testing.create_environ(headers=headers)) assert req.range == (0, 2) headers = {'Range': ''} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPInvalidHeader): req.range req = Request(testing.create_environ()) assert req.range is None def test_range_unit(self): headers = {'Range': 'bytes=10-'} req = Request(testing.create_environ(headers=headers)) assert req.range == (10, -1) assert req.range_unit == 'bytes' headers = {'Range': 'items=10-'} req = Request(testing.create_environ(headers=headers)) assert req.range == (10, -1) assert req.range_unit == 'items' headers = {'Range': ''} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPInvalidHeader): req.range_unit req = Request(testing.create_environ()) assert req.range_unit is None def test_range_invalid(self): headers = {'Range': 'bytes=10240'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=-'} expected_desc = ('The value provided for the Range header is ' 'invalid. The range offsets are missing.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': 'bytes=--'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=-3-'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=-3-4'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=3-3-4'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=3-3-'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=3-3- '} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=fizbit'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=a-'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=a-3'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=-b'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=3-b'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=x-y'} expected_desc = ('The value provided for the Range header is ' 'invalid. It must be a range formatted ' 'according to RFC 7233.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': 'bytes=0-0,-1'} expected_desc = ('The value provided for the Range ' 'header is invalid. The value must be a ' 'continuous range.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': '10-'} expected_desc = ('The value provided for the Range ' 'header is invalid. The value must be ' "prefixed with a range unit, e.g. 'bytes='") self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) def test_missing_attribute_header(self): req = Request(testing.create_environ()) assert req.range is None req = Request(testing.create_environ()) assert req.content_length is None def test_content_length(self): headers = {'content-length': '5656'} req = Request(testing.create_environ(headers=headers)) assert req.content_length == 5656 headers = {'content-length': ''} req = Request(testing.create_environ(headers=headers)) assert req.content_length is None def test_bogus_content_length_nan(self): headers = {'content-length': 'fuzzy-bunnies'} expected_desc = ('The value provided for the ' 'Content-Length header is invalid. The value ' 'of the header must be a number.') self._test_error_details(headers, 'content_length', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) def test_bogus_content_length_neg(self): headers = {'content-length': '-1'} expected_desc = ('The value provided for the Content-Length ' 'header is invalid. The value of the header ' 'must be a positive number.') self._test_error_details(headers, 'content_length', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) @pytest.mark.parametrize('header,attr', [ ('Date', 'date'), ('If-Modified-Since', 'if_modified_since'), ('If-Unmodified-Since', 'if_unmodified_since'), ]) def test_date(self, header, attr): date = datetime.datetime(2013, 4, 4, 5, 19, 18) date_str = 'Thu, 04 Apr 2013 05:19:18 GMT' self._test_header_expected_value(header, date_str, attr, date) @pytest.mark.parametrize('header,attr', [ ('Date', 'date'), ('If-Modified-Since', 'if_modified_since'), ('If-Unmodified-Since', 'if_unmodified_since'), ]) def test_date_invalid(self, header, attr): # Date formats don't conform to RFC 1123 headers = {header: 'Thu, 04 Apr 2013'} expected_desc = ('The value provided for the {} ' 'header is invalid. It must be formatted ' 'according to RFC 7231, Section 7.1.1.1') self._test_error_details(headers, attr, falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc.format(header)) headers = {header: ''} self._test_error_details(headers, attr, falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc.format(header)) @pytest.mark.parametrize('attr', ('date', 'if_modified_since', 'if_unmodified_since')) def test_date_missing(self, attr): req = Request(testing.create_environ()) assert getattr(req, attr) is None def test_attribute_headers(self): hash = 'fa0d1a60ef6616bb28038515c8ea4cb2' auth = 'HMAC_SHA1 c590afa9bb59191ffab30f223791e82d3fd3e3af' agent = 'testing/1.0.1' default_agent = 'curl/7.24.0 (x86_64-apple-darwin12.0)' referer = 'https://www.google.com/' self._test_attribute_header('Accept', 'x-falcon', 'accept', default='*/*') self._test_attribute_header('Authorization', auth, 'auth') self._test_attribute_header('Content-Type', 'text/plain', 'content_type') self._test_attribute_header('Expect', '100-continue', 'expect') self._test_attribute_header('If-Match', hash, 'if_match') self._test_attribute_header('If-None-Match', hash, 'if_none_match') self._test_attribute_header('If-Range', hash, 'if_range') self._test_attribute_header('User-Agent', agent, 'user_agent', default=default_agent) self._test_attribute_header('Referer', referer, 'referer') def test_method(self): assert self.req.method == 'GET' self.req = Request(testing.create_environ(path='', method='HEAD')) assert self.req.method == 'HEAD' def test_empty_path(self): self.req = Request(testing.create_environ(path='')) assert self.req.path == '/' def test_content_type_method(self): assert self.req.get_header('content-type') == 'text/plain' def test_content_length_method(self): assert self.req.get_header('content-length') == '4829' # TODO(kgriffs): Migrate to pytest and parametrized fixtures # to DRY things up a bit. @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_port_explicit(self, protocol): port = 9000 req = Request(testing.create_environ( protocol=protocol, port=port, app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) assert req.port == port @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_scheme_https(self, protocol): scheme = 'https' req = Request(testing.create_environ( protocol=protocol, scheme=scheme, app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) assert req.scheme == scheme assert req.port == 443 @pytest.mark.parametrize( 'protocol, set_forwarded_proto', list(itertools.product(_PROTOCOLS, [True, False])) ) def test_scheme_http(self, protocol, set_forwarded_proto): scheme = 'http' forwarded_scheme = 'HttPs' headers = dict(self.headers) if set_forwarded_proto: headers['X-Forwarded-Proto'] = forwarded_scheme req = Request(testing.create_environ( protocol=protocol, scheme=scheme, app=self.app, path='/hello', query_string=self.qs, headers=headers)) assert req.scheme == scheme assert req.port == 80 if set_forwarded_proto: assert req.forwarded_scheme == forwarded_scheme.lower() else: assert req.forwarded_scheme == scheme @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_netloc_default_port(self, protocol): req = Request(testing.create_environ( protocol=protocol, app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) assert req.netloc == 'falconframework.org' @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_netloc_nondefault_port(self, protocol): req = Request(testing.create_environ( protocol=protocol, port='8080', app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) assert req.netloc == 'falconframework.org:8080' @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_netloc_from_env(self, protocol): port = 9000 host = 'example.org' env = testing.create_environ( protocol=protocol, host=host, port=port, app=self.app, path='/hello', query_string=self.qs, headers=self.headers) req = Request(env) assert req.port == port assert req.netloc == '{}:{}'.format(host, port) def test_app_present(self): req = Request(testing.create_environ(app='/moving-pictures')) assert req.app == '/moving-pictures' def test_app_blank(self): req = Request(testing.create_environ(app='')) assert req.app == '' def test_app_missing(self): env = testing.create_environ() del env['SCRIPT_NAME'] req = Request(env) assert req.app == '' # ------------------------------------------------------------------------- # Helpers # ------------------------------------------------------------------------- def _test_attribute_header(self, name, value, attr, default=None): headers = {name: value} req = Request(testing.create_environ(headers=headers)) assert getattr(req, attr) == value req = Request(testing.create_environ()) assert getattr(req, attr) == default def _test_header_expected_value(self, name, value, attr, expected_value): headers = {name: value} req = Request(testing.create_environ(headers=headers)) assert getattr(req, attr) == expected_value def _test_error_details(self, headers, attr_name, error_type, title, description): req = Request(testing.create_environ(headers=headers)) try: getattr(req, attr_name) pytest.fail('{} not raised'.format(error_type.__name__)) except error_type as ex: assert ex.title == title assert ex.description == description
def test_remote_addr(self): req = Request( testing.create_environ(host='example.com', path='/access_route')) self.assertEqual(req.access_route, ['127.0.0.1'])
def test_range_invalid(self): headers = {'Range': 'bytes=10240'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=-'} expected_desc = ('The value provided for the Range header is ' 'invalid. The byte offsets are missing.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': 'bytes=--'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=-3-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=3-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=3-3-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=3-3- '} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=fizbit'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=a-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=a-3'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=-b'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=3-b'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=x-y'} expected_desc = ('The value provided for the Range header is ' 'invalid. It must be a byte range formatted ' 'according to RFC 2616.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': 'bytes=0-0,-1'} expected_desc = ('The value provided for the Range ' 'header is invalid. The value must be a ' 'continuous byte range.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': '10-'} expected_desc = ("The value provided for the Range " "header is invalid. The value must be " "prefixed with 'bytes='") self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc)
def test_remote_addr_missing(self): env = testing.create_environ(host='example.com', path='/access_route') del env['REMOTE_ADDR'] req = Request(env) self.assertEqual(req.access_route, [])
def test_date_missing(self, attr): req = Request(testing.create_environ()) self.assertIs(getattr(req, attr), None)
def test_default_request_context(self): env = testing.create_environ() req = Request(env) self.assertIsInstance(req.context, dict)
def test_empty_path(self): self.req = Request(testing.create_environ(path='')) self.assertEqual(self.req.path, '/')
def test_app_missing(): env = testing.create_environ() del env['SCRIPT_NAME'] req = Request(env) assert req.app == ''
def test_date_invalid(self): headers = {'date': 'Thu, 04 Apr 2013'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.date)
def test_empty_path(self): self.req = Request(helpers.create_environ(path='')) self.assertEquals(self.req.path, '/')
class TestReqVars(testing.TestBase): def before(self): self.qs = 'marker=deadbeef&limit=10' self.headers = { 'Host': 'falcon.example.com', 'Content-Type': 'text/plain', 'Content-Length': '4829', 'Authorization': '' } self.app = '/test' self.path = '/hello' self.relative_uri = self.path + '?' + self.qs self.uri = 'http://falcon.example.com' + self.app + self.relative_uri self.uri_noqs = 'http://falcon.example.com' + self.app + self.path self.req = Request( testing.create_environ(app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) self.req_noqs = Request( testing.create_environ(app=self.app, path='/hello', headers=self.headers)) def test_missing_qs(self): env = testing.create_environ() if 'QUERY_STRING' in env: del env['QUERY_STRING'] # Should not cause an exception when Request is instantiated Request(env) def test_empty(self): self.assertIs(self.req.auth, None) def test_reconstruct_url(self): req = self.req scheme = req.protocol host = req.get_header('host') app = req.app path = req.path query_string = req.query_string actual_url = ''.join( [scheme, '://', host, app, path, '?', query_string]) self.assertEquals(actual_url, self.uri) def test_uri(self): self.assertEquals(self.req.url, self.uri) self.assertEquals(self.req.uri, self.uri) self.assertEquals(self.req_noqs.uri, self.uri_noqs) def test_relative_uri(self): self.assertEqual(self.req.relative_uri, self.app + self.relative_uri) self.assertEqual(self.req_noqs.relative_uri, self.app + self.path) req_noapp = Request( testing.create_environ(path='/hello', query_string=self.qs, headers=self.headers)) self.assertEqual(req_noapp.relative_uri, self.relative_uri) req_noapp = Request( testing.create_environ(path='/hello/', query_string=self.qs, headers=self.headers)) self.assertEqual(req_noapp.relative_uri, self.relative_uri) def test_client_accepts(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) headers = {'Accept': '*/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) headers = {} # NOTE(kgriffs): Equivalent to '*/*' per RFC req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) headers = {'Accept': 'application/xm'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/xml')) headers = {'Accept': 'text/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('text/csv')) self.assertFalse(req.client_accepts('application/xhtml+xml')) headers = {'Accept': 'text/*, application/xhtml+xml; q=0.0'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('text/csv')) self.assertTrue(req.client_accepts('application/xhtml+xml')) headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) headers = {'Accept': 'text/*, application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('application/json')) headers = {'Accept': 'text/*,application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('application/json')) def test_client_accepts_props(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts_xml) self.assertFalse(req.client_accepts_json) headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts_xml) headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts_xml) self.assertTrue(req.client_accepts_json) headers = {'Accept': 'application/json, application/xml'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts_xml) self.assertTrue(req.client_accepts_json) def test_client_prefers(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xml']) self.assertEquals(preferred_type, 'application/xml') headers = {'Accept': '*/*'} preferred_type = req.client_prefers( ('application/xml', 'application/json')) # NOTE(kgriffs): If client doesn't care, "preferr" the first one self.assertEquals(preferred_type, 'application/xml') headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) self.assertEquals(preferred_type, 'application/xhtml+xml') headers = {'Accept': '3p12845j;;;asfd;'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) self.assertEquals(preferred_type, None) def test_range(self): headers = {'Range': '10-'} req = Request(testing.create_environ(headers=headers)) self.assertEquals(req.range, (10, -1)) headers = {'Range': '10-20'} req = Request(testing.create_environ(headers=headers)) self.assertEquals(req.range, (10, 20)) headers = {'Range': '-10240'} req = Request(testing.create_environ(headers=headers)) self.assertEquals(req.range, (-10240, -1)) headers = {'Range': ''} req = Request(testing.create_environ(headers=headers)) self.assertIs(req.range, None) req = Request(testing.create_environ()) self.assertIs(req.range, None) def test_range_invalid(self): headers = {'Range': '10240'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '--'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '-3-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '3-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '3-3-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '3-3- '} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'fizbit'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'a-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'a-3'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '-b'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '3-b'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'x-y'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=0-0,-1'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) def test_missing_attribute_header(self): req = Request(testing.create_environ()) self.assertEquals(req.range, None) req = Request(testing.create_environ()) self.assertEquals(req.content_length, None) def test_content_length(self): headers = {'content-length': '5656'} req = Request(testing.create_environ(headers=headers)) self.assertEquals(req.content_length, 5656) headers = {'content-length': ''} req = Request(testing.create_environ(headers=headers)) self.assertEquals(req.content_length, None) def test_bogus_content_length_nan(self): headers = {'content-length': 'fuzzy-bunnies'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.content_length) def test_bogus_content_length_neg(self): headers = {'content-length': '-1'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.content_length) def test_date(self): date = datetime.datetime(2013, 4, 4, 5, 19, 18) headers = {'date': 'Thu, 04 Apr 2013 05:19:18 GMT'} req = Request(testing.create_environ(headers=headers)) self.assertEquals(req.date, date) def test_date_invalid(self): headers = {'date': 'Thu, 04 Apr 2013'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.date) def test_attribute_headers(self): date = testing.httpnow() hash = 'fa0d1a60ef6616bb28038515c8ea4cb2' auth = 'HMAC_SHA1 c590afa9bb59191ffab30f223791e82d3fd3e3af' agent = 'testing/1.0.1' default_agent = 'curl/7.24.0 (x86_64-apple-darwin12.0)' self._test_attribute_header('Accept', 'x-falcon', 'accept', default='*/*') self._test_attribute_header('Authorization', auth, 'auth') self._test_attribute_header('Content-Type', 'text/plain', 'content_type') self._test_attribute_header('Expect', '100-continue', 'expect') self._test_attribute_header('If-Match', hash, 'if_match') self._test_attribute_header('If-Modified-Since', date, 'if_modified_since') self._test_attribute_header('If-None-Match', hash, 'if_none_match') self._test_attribute_header('If-Range', hash, 'if_range') self._test_attribute_header('If-Unmodified-Since', date, 'if_unmodified_since') self._test_attribute_header('User-Agent', agent, 'user_agent', default=default_agent) def test_method(self): self.assertEquals(self.req.method, 'GET') self.req = Request(testing.create_environ(path='', method='HEAD')) self.assertEquals(self.req.method, 'HEAD') def test_empty_path(self): self.req = Request(testing.create_environ(path='')) self.assertEquals(self.req.path, '/') def test_content_type_method(self): self.assertEquals(self.req.get_header('content-type'), 'text/plain') def test_content_length_method(self): self.assertEquals(self.req.get_header('content-length'), '4829') # ------------------------------------------------------------------------- # Helpers # ------------------------------------------------------------------------- def _test_attribute_header(self, name, value, attr, default=None): headers = {name: value} req = Request(testing.create_environ(headers=headers)) self.assertEquals(getattr(req, attr), value) req = Request(testing.create_environ()) self.assertEqual(getattr(req, attr), default)
class TestReqVars(testing.TestBase): def before(self): self.qs = 'marker=deadbeef&limit=10' self.headers = { 'Content-Type': 'text/plain', 'Content-Length': '4829', 'Authorization': '' } self.app = '/test' self.path = '/hello' self.relative_uri = self.path + '?' + self.qs self.req = Request(testing.create_environ( app=self.app, port=8080, path='/hello', query_string=self.qs, headers=self.headers)) self.req_noqs = Request(testing.create_environ( app=self.app, path='/hello', headers=self.headers)) def test_missing_qs(self): env = testing.create_environ() if 'QUERY_STRING' in env: del env['QUERY_STRING'] # Should not cause an exception when Request is instantiated Request(env) def test_empty(self): self.assertIs(self.req.auth, None) def test_host(self): self.assertEqual(self.req.host, testing.DEFAULT_HOST) def test_subdomain(self): req = Request(testing.create_environ( host='com', path='/hello', headers=self.headers)) self.assertIs(req.subdomain, None) req = Request(testing.create_environ( host='example.com', path='/hello', headers=self.headers)) self.assertEqual(req.subdomain, 'example') req = Request(testing.create_environ( host='highwire.example.com', path='/hello', headers=self.headers)) self.assertEqual(req.subdomain, 'highwire') req = Request(testing.create_environ( host='lb01.dfw01.example.com', port=8080, path='/hello', headers=self.headers)) self.assertEqual(req.subdomain, 'lb01') # NOTE(kgriffs): Behavior for IP addresses is undefined, # so just make sure it doesn't blow up. req = Request(testing.create_environ( host='127.0.0.1', path='/hello', headers=self.headers)) self.assertEqual(type(req.subdomain), str) # NOTE(kgriffs): Test fallback to SERVER_NAME by using # HTTP 1.0, which will cause .create_environ to not set # HTTP_HOST. req = Request(testing.create_environ( protocol='HTTP/1.0', host='example.com', path='/hello', headers=self.headers)) self.assertEqual(req.subdomain, 'example') def test_reconstruct_url(self): req = self.req scheme = req.protocol host = req.get_header('host') app = req.app path = req.path query_string = req.query_string expected_uri = ''.join([scheme, '://', host, app, path, '?', query_string]) self.assertEqual(expected_uri, req.uri) @testtools.skipUnless(six.PY3, 'Test only applies to Python 3') def test_nonlatin_path(self): cyrillic_path = u'/hello_\u043f\u0440\u0438\u0432\u0435\u0442' cyrillic_path_decoded = cyrillic_path.encode('utf-8').decode('latin1') req = Request(testing.create_environ( host='com', path=cyrillic_path_decoded, headers=self.headers)) self.assertEqual(req.path, cyrillic_path) def test_uri(self): uri = ('http://' + testing.DEFAULT_HOST + ':8080' + self.app + self.relative_uri) self.assertEqual(self.req.url, uri) # NOTE(kgriffs): Call twice to check caching works self.assertEqual(self.req.uri, uri) self.assertEqual(self.req.uri, uri) uri_noqs = ('http://' + testing.DEFAULT_HOST + self.app + self.path) self.assertEqual(self.req_noqs.uri, uri_noqs) def test_uri_https(self): # ======================================================= # Default port, implicit # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https')) uri = ('https://' + testing.DEFAULT_HOST + '/hello') self.assertEqual(req.uri, uri) # ======================================================= # Default port, explicit # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https', port=443)) uri = ('https://' + testing.DEFAULT_HOST + '/hello') self.assertEqual(req.uri, uri) # ======================================================= # Non-default port # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https', port=22)) uri = ('https://' + testing.DEFAULT_HOST + ':22/hello') self.assertEqual(req.uri, uri) def test_uri_http_1_0(self): # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', app=self.app, port=80, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('http://' + testing.DEFAULT_HOST + self.app + self.relative_uri) self.assertEqual(req.uri, uri) # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', app=self.app, port=8080, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('http://' + testing.DEFAULT_HOST + ':8080' + self.app + self.relative_uri) self.assertEqual(req.uri, uri) # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', scheme='https', app=self.app, port=443, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('https://' + testing.DEFAULT_HOST + self.app + self.relative_uri) self.assertEqual(req.uri, uri) # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', scheme='https', app=self.app, port=22, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('https://' + testing.DEFAULT_HOST + ':22' + self.app + self.relative_uri) self.assertEqual(req.uri, uri) def test_relative_uri(self): self.assertEqual(self.req.relative_uri, self.app + self.relative_uri) self.assertEqual( self.req_noqs.relative_uri, self.app + self.path) req_noapp = Request(testing.create_environ( path='/hello', query_string=self.qs, headers=self.headers)) self.assertEqual(req_noapp.relative_uri, self.relative_uri) req_noapp = Request(testing.create_environ( path='/hello/', query_string=self.qs, headers=self.headers)) # NOTE(kgriffs): Call twice to check caching works self.assertEqual(req_noapp.relative_uri, self.relative_uri) self.assertEqual(req_noapp.relative_uri, self.relative_uri) def test_client_accepts(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) headers = {'Accept': '*/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) self.assertFalse(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {} # NOTE(kgriffs): Equivalent to '*/*' per RFC req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'application/xm'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'text/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('text/csv')) self.assertFalse(req.client_accepts('application/xhtml+xml')) headers = {'Accept': 'text/*, application/xhtml+xml; q=0.0'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('text/csv')) self.assertFalse(req.client_accepts('application/xhtml+xml')) headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('application/xhtml+xml')) headers = {'Accept': 'text/*, application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'text/*,application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) def test_client_accepts_bogus(self): headers = {'Accept': '~'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('text/plain')) self.assertFalse(req.client_accepts('application/json')) def test_client_accepts_props(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts_xml) self.assertFalse(req.client_accepts_json) self.assertFalse(req.client_accepts_msgpack) headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts_xml) self.assertTrue(req.client_accepts_json) self.assertTrue(req.client_accepts_msgpack) headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts_xml) self.assertTrue(req.client_accepts_json) self.assertFalse(req.client_accepts_msgpack) headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts_xml) self.assertFalse(req.client_accepts_json) self.assertTrue(req.client_accepts_msgpack) headers = {'Accept': 'application/msgpack'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts_xml) self.assertFalse(req.client_accepts_json) self.assertTrue(req.client_accepts_msgpack) headers = { 'Accept': 'application/json,application/xml,application/x-msgpack' } req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts_xml) self.assertTrue(req.client_accepts_json) self.assertTrue(req.client_accepts_msgpack) def test_client_prefers(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xml']) self.assertEqual(preferred_type, 'application/xml') headers = {'Accept': '*/*'} preferred_type = req.client_prefers(('application/xml', 'application/json')) # NOTE(kgriffs): If client doesn't care, "prefer" the first one self.assertEqual(preferred_type, 'application/xml') headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) self.assertEqual(preferred_type, 'application/xhtml+xml') headers = {'Accept': '3p12845j;;;asfd;'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) self.assertEqual(preferred_type, None) def test_range(self): headers = {'Range': 'bytes=10-'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.range, (10, -1)) headers = {'Range': 'bytes=10-20'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.range, (10, 20)) headers = {'Range': 'bytes=-10240'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.range, (-10240, -1)) headers = {'Range': 'bytes=0-2'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.range, (0, 2)) headers = {'Range': ''} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPInvalidHeader, lambda: req.range) req = Request(testing.create_environ()) self.assertIs(req.range, None) def test_range_unit(self): headers = {'Range': 'bytes=10-'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.range, (10, -1)) self.assertEqual(req.range_unit, 'bytes') headers = {'Range': 'items=10-'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.range, (10, -1)) self.assertEqual(req.range_unit, 'items') headers = {'Range': ''} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPInvalidHeader, lambda: req.range_unit) req = Request(testing.create_environ()) self.assertIs(req.range_unit, None) def test_range_invalid(self): headers = {'Range': 'bytes=10240'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=-'} expected_desc = ('The value provided for the Range header is ' 'invalid. The range offsets are missing.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': 'bytes=--'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=-3-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=3-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=3-3-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=3-3- '} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=fizbit'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=a-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=a-3'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=-b'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=3-b'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=x-y'} expected_desc = ('The value provided for the Range header is ' 'invalid. It must be a range formatted ' 'according to RFC 7233.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': 'bytes=0-0,-1'} expected_desc = ('The value provided for the Range ' 'header is invalid. The value must be a ' 'continuous range.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': '10-'} expected_desc = ("The value provided for the Range " "header is invalid. The value must be " "prefixed with a range unit, e.g. 'bytes='") self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) def test_missing_attribute_header(self): req = Request(testing.create_environ()) self.assertEqual(req.range, None) req = Request(testing.create_environ()) self.assertEqual(req.content_length, None) def test_content_length(self): headers = {'content-length': '5656'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.content_length, 5656) headers = {'content-length': ''} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.content_length, None) def test_bogus_content_length_nan(self): headers = {'content-length': 'fuzzy-bunnies'} expected_desc = ('The value provided for the ' 'Content-Length header is invalid. The value ' 'of the header must be a number.') self._test_error_details(headers, 'content_length', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) def test_bogus_content_length_neg(self): headers = {'content-length': '-1'} expected_desc = ('The value provided for the Content-Length ' 'header is invalid. The value of the header ' 'must be a positive number.') self._test_error_details(headers, 'content_length', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) @ddt.data(('Date', 'date'), ('If-Modified-since', 'if_modified_since'), ('If-Unmodified-since', 'if_unmodified_since'), ) @ddt.unpack def test_date(self, header, attr): date = datetime.datetime(2013, 4, 4, 5, 19, 18) date_str = 'Thu, 04 Apr 2013 05:19:18 GMT' self._test_header_expected_value(header, date_str, attr, date) @ddt.data(('Date', 'date'), ('If-Modified-Since', 'if_modified_since'), ('If-Unmodified-Since', 'if_unmodified_since'), ) @ddt.unpack def test_date_invalid(self, header, attr): # Date formats don't conform to RFC 1123 headers = {header: 'Thu, 04 Apr 2013'} expected_desc = ('The value provided for the {0} ' 'header is invalid. It must be formatted ' 'according to RFC 7231, Section 7.1.1.1') self._test_error_details(headers, attr, falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc.format(header)) headers = {header: ''} self._test_error_details(headers, attr, falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc.format(header)) @ddt.data('date', 'if_modified_since', 'if_unmodified_since') def test_date_missing(self, attr): req = Request(testing.create_environ()) self.assertIs(getattr(req, attr), None) def test_attribute_headers(self): hash = 'fa0d1a60ef6616bb28038515c8ea4cb2' auth = 'HMAC_SHA1 c590afa9bb59191ffab30f223791e82d3fd3e3af' agent = 'testing/1.0.1' default_agent = 'curl/7.24.0 (x86_64-apple-darwin12.0)' self._test_attribute_header('Accept', 'x-falcon', 'accept', default='*/*') self._test_attribute_header('Authorization', auth, 'auth') self._test_attribute_header('Content-Type', 'text/plain', 'content_type') self._test_attribute_header('Expect', '100-continue', 'expect') self._test_attribute_header('If-Match', hash, 'if_match') self._test_attribute_header('If-None-Match', hash, 'if_none_match') self._test_attribute_header('If-Range', hash, 'if_range') self._test_attribute_header('User-Agent', agent, 'user_agent', default=default_agent) def test_method(self): self.assertEqual(self.req.method, 'GET') self.req = Request(testing.create_environ(path='', method='HEAD')) self.assertEqual(self.req.method, 'HEAD') def test_empty_path(self): self.req = Request(testing.create_environ(path='')) self.assertEqual(self.req.path, '/') def test_content_type_method(self): self.assertEqual(self.req.get_header('content-type'), 'text/plain') def test_content_length_method(self): self.assertEqual(self.req.get_header('content-length'), '4829') # ------------------------------------------------------------------------- # Helpers # ------------------------------------------------------------------------- def _test_attribute_header(self, name, value, attr, default=None): headers = {name: value} req = Request(testing.create_environ(headers=headers)) self.assertEqual(getattr(req, attr), value) req = Request(testing.create_environ()) self.assertEqual(getattr(req, attr), default) def _test_header_expected_value(self, name, value, attr, expected_value): headers = {name: value} req = Request(testing.create_environ(headers=headers)) self.assertEqual(getattr(req, attr), expected_value) def _test_error_details(self, headers, attr_name, error_type, title, description): req = Request(testing.create_environ(headers=headers)) try: getattr(req, attr_name) self.fail('{0} not raised'.format(error_type.__name__)) except error_type as ex: self.assertEqual(ex.title, title) self.assertEqual(ex.description, description)
def test_date(self): date = datetime.datetime(2013, 4, 4, 5, 19, 18) headers = {'date': 'Thu, 04 Apr 2013 05:19:18 GMT'} req = Request(testing.create_environ(headers=headers)) self.assertEquals(req.date, date)
def test_bogus_content_length_neg(self): headers = {'content-length': '-1'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.content_length)
def test_range_invalid(self): headers = {'Range': '10240'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '--'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '-3-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '3-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '3-3-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '3-3- '} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'fizbit'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'a-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'a-3'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '-b'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': '3-b'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'x-y'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=0-0,-1'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range)
def __call__(self, env, start_response): """WSGI "app" method Makes instances of API callable by any WSGI server. See also PEP 333. Args: env: A WSGI environment dictionary start_response: A WSGI helper method for setting status and headers on a response. """ req = Request(env) resp = Response() responder, params, na_responder = self._get_responder( req.path, req.method) try: responder(req, resp, **params) except HTTPError as ex: resp.status = ex.status if ex.headers is not None: resp.set_headers(ex.headers) if req.client_accepts('application/json'): resp.body = ex.json() except TypeError as ex: # NOTE(kgriffs): Get the stack trace up here since we can just # use this convenience function which graps the last raised # exception context. stack_trace = traceback.format_exc() # See if the method doesn't support the given route's params, to # support assigning multiple routes to the same resource. try: argspec = responder.wrapped_argspec except AttributeError: argspec = inspect.getargspec(responder) # First three args should be (self, req, resp) if argspec.args[0] == 'self': offset = 3 else: offset = 2 args_needed = set(argspec.args[offset:]) args_given = set(params.keys()) # Reset the response resp = Response() # Does the responder require more or fewer args than given? if args_needed != args_given: req.log_error('A responder method could not be found with the ' 'correct arguments.') na_responder(req, resp) else: # Error caused by something else req.log_error('A responder method (on_*) raised TypeError. %s' % stack_trace) falcon.responders.internal_server_error(req, resp) # # Set status and headers # use_body = not helpers.should_ignore_body(resp.status, req.method) if use_body: helpers.set_content_length(resp) body = helpers.get_body(resp) else: # Default: return an empty body body = [] # Set content type if needed use_content_type = (body or req.method == 'HEAD' or resp.status == HTTP_416) if use_content_type: media_type = self._media_type else: media_type = None headers = resp._wsgi_headers(media_type) # Return the response per the WSGI spec start_response(resp.status, headers) return body
class TestReqVars(object): def setup_method(self, method): self.qs = 'marker=deadbeef&limit=10' self.headers = { 'Content-Type': 'text/plain', 'Content-Length': '4829', 'Authorization': '' } self.app = '/test' self.path = '/hello' self.relative_uri = self.path + '?' + self.qs self.req = Request(testing.create_environ( app=self.app, port=8080, path='/hello', query_string=self.qs, headers=self.headers)) self.req_noqs = Request(testing.create_environ( app=self.app, path='/hello', headers=self.headers)) def test_missing_qs(self): env = testing.create_environ() if 'QUERY_STRING' in env: del env['QUERY_STRING'] # Should not cause an exception when Request is instantiated Request(env) def test_empty(self): assert self.req.auth is None def test_host(self): assert self.req.host == testing.DEFAULT_HOST def test_subdomain(self): req = Request(testing.create_environ( host='com', path='/hello', headers=self.headers)) assert req.subdomain is None req = Request(testing.create_environ( host='example.com', path='/hello', headers=self.headers)) assert req.subdomain == 'example' req = Request(testing.create_environ( host='highwire.example.com', path='/hello', headers=self.headers)) assert req.subdomain == 'highwire' req = Request(testing.create_environ( host='lb01.dfw01.example.com', port=8080, path='/hello', headers=self.headers)) assert req.subdomain == 'lb01' # NOTE(kgriffs): Behavior for IP addresses is undefined, # so just make sure it doesn't blow up. req = Request(testing.create_environ( host='127.0.0.1', path='/hello', headers=self.headers)) assert type(req.subdomain) == str # NOTE(kgriffs): Test fallback to SERVER_NAME by using # HTTP 1.0, which will cause .create_environ to not set # HTTP_HOST. req = Request(testing.create_environ( protocol='HTTP/1.0', host='example.com', path='/hello', headers=self.headers)) assert req.subdomain == 'example' def test_reconstruct_url(self): req = self.req scheme = req.protocol host = req.get_header('host') app = req.app path = req.path query_string = req.query_string expected_uri = ''.join([scheme, '://', host, app, path, '?', query_string]) assert expected_uri == req.uri @pytest.mark.skipif(not six.PY3, reason='Test only applies to Python 3') @pytest.mark.parametrize('test_path', [ u'/hello_\u043f\u0440\u0438\u0432\u0435\u0442', u'/test/%E5%BB%B6%E5%AE%89', u'/test/%C3%A4%C3%B6%C3%BC%C3%9F%E2%82%AC', ]) def test_nonlatin_path(self, test_path): # NOTE(kgriffs): When a request comes in, web servers decode # the path. The decoded path may contain UTF-8 characters, # but according to the WSGI spec, no strings can contain chars # outside ISO-8859-1. Therefore, to reconcile the URI # encoding standard that allows UTF-8 with the WSGI spec # that does not, WSGI servers tunnel the string via # ISO-8859-1. falcon.testing.create_environ() mimics this # behavior, e.g.: # # tunnelled_path = path.encode('utf-8').decode('iso-8859-1') # # falcon.Request does the following to reverse the process: # # path = tunnelled_path.encode('iso-8859-1').decode('utf-8', 'replace') # req = Request(testing.create_environ( host='com', path=test_path, headers=self.headers)) assert req.path == falcon.uri.decode(test_path) def test_uri(self): uri = ('http://' + testing.DEFAULT_HOST + ':8080' + self.app + self.relative_uri) assert self.req.url == uri # NOTE(kgriffs): Call twice to check caching works assert self.req.uri == uri assert self.req.uri == uri uri_noqs = ('http://' + testing.DEFAULT_HOST + self.app + self.path) assert self.req_noqs.uri == uri_noqs def test_uri_https(self): # ======================================================= # Default port, implicit # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https')) uri = ('https://' + testing.DEFAULT_HOST + '/hello') assert req.uri == uri # ======================================================= # Default port, explicit # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https', port=443)) uri = ('https://' + testing.DEFAULT_HOST + '/hello') assert req.uri == uri # ======================================================= # Non-default port # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https', port=22)) uri = ('https://' + testing.DEFAULT_HOST + ':22/hello') assert req.uri == uri def test_uri_http_1_0(self): # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', app=self.app, port=80, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('http://' + testing.DEFAULT_HOST + self.app + self.relative_uri) assert req.uri == uri # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', app=self.app, port=8080, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('http://' + testing.DEFAULT_HOST + ':8080' + self.app + self.relative_uri) assert req.uri == uri # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', scheme='https', app=self.app, port=443, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('https://' + testing.DEFAULT_HOST + self.app + self.relative_uri) assert req.uri == uri # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', scheme='https', app=self.app, port=22, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('https://' + testing.DEFAULT_HOST + ':22' + self.app + self.relative_uri) assert req.uri == uri def test_relative_uri(self): assert self.req.relative_uri == self.app + self.relative_uri assert self.req_noqs.relative_uri == self.app + self.path req_noapp = Request(testing.create_environ( path='/hello', query_string=self.qs, headers=self.headers)) assert req_noapp.relative_uri == self.relative_uri req_noapp = Request(testing.create_environ( path='/hello/', query_string=self.qs, headers=self.headers)) # NOTE(kgriffs): Call twice to check caching works assert req_noapp.relative_uri == self.relative_uri assert req_noapp.relative_uri == self.relative_uri options = RequestOptions() options.strip_url_path_trailing_slash = False req_noapp = Request(testing.create_environ( path='/hello/', query_string=self.qs, headers=self.headers), options=options) assert req_noapp.relative_uri == '/hello/' + '?' + self.qs def test_client_accepts(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/xml') headers = {'Accept': '*/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/xml') assert req.client_accepts('application/json') assert req.client_accepts('application/x-msgpack') headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts('application/xml') assert not req.client_accepts('application/json') assert req.client_accepts('application/x-msgpack') headers = {} # NOTE(kgriffs): Equivalent to '*/*' per RFC req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/xml') headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts('application/xml') headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/x-msgpack') headers = {'Accept': 'application/xm'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts('application/xml') headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('application/json') assert req.client_accepts('application/xml') assert req.client_accepts('application/x-msgpack') headers = {'Accept': 'text/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('text/csv') assert not req.client_accepts('application/xhtml+xml') headers = {'Accept': 'text/*, application/xhtml+xml; q=0.0'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('text/csv') assert not req.client_accepts('application/xhtml+xml') headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('application/xhtml+xml') headers = {'Accept': 'text/*, application/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('application/xml') assert req.client_accepts('application/json') assert req.client_accepts('application/x-msgpack') headers = {'Accept': 'text/*,application/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts('text/plain') assert req.client_accepts('application/xml') assert req.client_accepts('application/json') assert req.client_accepts('application/x-msgpack') def test_client_accepts_bogus(self): headers = {'Accept': '~'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts('text/plain') assert not req.client_accepts('application/json') def test_client_accepts_props(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts_xml assert not req.client_accepts_json assert not req.client_accepts_msgpack headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) assert req.client_accepts_xml assert req.client_accepts_json assert req.client_accepts_msgpack headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts_xml assert req.client_accepts_json assert not req.client_accepts_msgpack headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts_xml assert not req.client_accepts_json assert req.client_accepts_msgpack headers = {'Accept': 'application/msgpack'} req = Request(testing.create_environ(headers=headers)) assert not req.client_accepts_xml assert not req.client_accepts_json assert req.client_accepts_msgpack headers = { 'Accept': 'application/json,application/xml,application/x-msgpack' } req = Request(testing.create_environ(headers=headers)) assert req.client_accepts_xml assert req.client_accepts_json assert req.client_accepts_msgpack def test_client_prefers(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xml']) assert preferred_type == 'application/xml' headers = {'Accept': '*/*'} preferred_type = req.client_prefers(('application/xml', 'application/json')) # NOTE(kgriffs): If client doesn't care, "prefer" the first one assert preferred_type == 'application/xml' headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) assert preferred_type == 'application/xhtml+xml' headers = {'Accept': '3p12845j;;;asfd;'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) assert preferred_type is None def test_range(self): headers = {'Range': 'bytes=10-'} req = Request(testing.create_environ(headers=headers)) assert req.range == (10, -1) headers = {'Range': 'bytes=10-20'} req = Request(testing.create_environ(headers=headers)) assert req.range == (10, 20) headers = {'Range': 'bytes=-10240'} req = Request(testing.create_environ(headers=headers)) assert req.range == (-10240, -1) headers = {'Range': 'bytes=0-2'} req = Request(testing.create_environ(headers=headers)) assert req.range == (0, 2) headers = {'Range': ''} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPInvalidHeader): req.range req = Request(testing.create_environ()) assert req.range is None def test_range_unit(self): headers = {'Range': 'bytes=10-'} req = Request(testing.create_environ(headers=headers)) assert req.range == (10, -1) assert req.range_unit == 'bytes' headers = {'Range': 'items=10-'} req = Request(testing.create_environ(headers=headers)) assert req.range == (10, -1) assert req.range_unit == 'items' headers = {'Range': ''} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPInvalidHeader): req.range_unit req = Request(testing.create_environ()) assert req.range_unit is None def test_range_invalid(self): headers = {'Range': 'bytes=10240'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=-'} expected_desc = ('The value provided for the Range header is ' 'invalid. The range offsets are missing.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': 'bytes=--'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=-3-'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=-3-4'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=3-3-4'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=3-3-'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=3-3- '} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=fizbit'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=a-'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=a-3'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=-b'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=3-b'} req = Request(testing.create_environ(headers=headers)) with pytest.raises(falcon.HTTPBadRequest): req.range headers = {'Range': 'bytes=x-y'} expected_desc = ('The value provided for the Range header is ' 'invalid. It must be a range formatted ' 'according to RFC 7233.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': 'bytes=0-0,-1'} expected_desc = ('The value provided for the Range ' 'header is invalid. The value must be a ' 'continuous range.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': '10-'} expected_desc = ('The value provided for the Range ' 'header is invalid. The value must be ' "prefixed with a range unit, e.g. 'bytes='") self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) def test_missing_attribute_header(self): req = Request(testing.create_environ()) assert req.range is None req = Request(testing.create_environ()) assert req.content_length is None def test_content_length(self): headers = {'content-length': '5656'} req = Request(testing.create_environ(headers=headers)) assert req.content_length == 5656 headers = {'content-length': ''} req = Request(testing.create_environ(headers=headers)) assert req.content_length is None def test_bogus_content_length_nan(self): headers = {'content-length': 'fuzzy-bunnies'} expected_desc = ('The value provided for the ' 'Content-Length header is invalid. The value ' 'of the header must be a number.') self._test_error_details(headers, 'content_length', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) def test_bogus_content_length_neg(self): headers = {'content-length': '-1'} expected_desc = ('The value provided for the Content-Length ' 'header is invalid. The value of the header ' 'must be a positive number.') self._test_error_details(headers, 'content_length', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) @pytest.mark.parametrize('header,attr', [ ('Date', 'date'), ('If-Modified-Since', 'if_modified_since'), ('If-Unmodified-Since', 'if_unmodified_since'), ]) def test_date(self, header, attr): date = datetime.datetime(2013, 4, 4, 5, 19, 18) date_str = 'Thu, 04 Apr 2013 05:19:18 GMT' self._test_header_expected_value(header, date_str, attr, date) @pytest.mark.parametrize('header,attr', [ ('Date', 'date'), ('If-Modified-Since', 'if_modified_since'), ('If-Unmodified-Since', 'if_unmodified_since'), ]) def test_date_invalid(self, header, attr): # Date formats don't conform to RFC 1123 headers = {header: 'Thu, 04 Apr 2013'} expected_desc = ('The value provided for the {0} ' 'header is invalid. It must be formatted ' 'according to RFC 7231, Section 7.1.1.1') self._test_error_details(headers, attr, falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc.format(header)) headers = {header: ''} self._test_error_details(headers, attr, falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc.format(header)) @pytest.mark.parametrize('attr', ('date', 'if_modified_since', 'if_unmodified_since')) def test_date_missing(self, attr): req = Request(testing.create_environ()) assert getattr(req, attr) is None def test_attribute_headers(self): hash = 'fa0d1a60ef6616bb28038515c8ea4cb2' auth = 'HMAC_SHA1 c590afa9bb59191ffab30f223791e82d3fd3e3af' agent = 'testing/1.0.1' default_agent = 'curl/7.24.0 (x86_64-apple-darwin12.0)' referer = 'https://www.google.com/' self._test_attribute_header('Accept', 'x-falcon', 'accept', default='*/*') self._test_attribute_header('Authorization', auth, 'auth') self._test_attribute_header('Content-Type', 'text/plain', 'content_type') self._test_attribute_header('Expect', '100-continue', 'expect') self._test_attribute_header('If-Match', hash, 'if_match') self._test_attribute_header('If-None-Match', hash, 'if_none_match') self._test_attribute_header('If-Range', hash, 'if_range') self._test_attribute_header('User-Agent', agent, 'user_agent', default=default_agent) self._test_attribute_header('Referer', referer, 'referer') def test_method(self): assert self.req.method == 'GET' self.req = Request(testing.create_environ(path='', method='HEAD')) assert self.req.method == 'HEAD' def test_empty_path(self): self.req = Request(testing.create_environ(path='')) assert self.req.path == '/' def test_content_type_method(self): assert self.req.get_header('content-type') == 'text/plain' def test_content_length_method(self): assert self.req.get_header('content-length') == '4829' # TODO(kgriffs): Migrate to pytest and parametrized fixtures # to DRY things up a bit. @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_port_explicit(self, protocol): port = 9000 req = Request(testing.create_environ( protocol=protocol, port=port, app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) assert req.port == port @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_scheme_https(self, protocol): scheme = 'https' req = Request(testing.create_environ( protocol=protocol, scheme=scheme, app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) assert req.scheme == scheme assert req.port == 443 @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_scheme_http(self, protocol): scheme = 'http' req = Request(testing.create_environ( protocol=protocol, scheme=scheme, app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) assert req.scheme == scheme assert req.port == 80 @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_netloc_default_port(self, protocol): req = Request(testing.create_environ( protocol=protocol, app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) assert req.netloc == 'falconframework.org' @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_netloc_nondefault_port(self, protocol): req = Request(testing.create_environ( protocol=protocol, port='8080', app=self.app, path='/hello', query_string=self.qs, headers=self.headers)) assert req.netloc == 'falconframework.org:8080' @pytest.mark.parametrize('protocol', _PROTOCOLS) def test_netloc_from_env(self, protocol): port = 9000 host = 'example.org' env = testing.create_environ( protocol=protocol, host=host, port=port, app=self.app, path='/hello', query_string=self.qs, headers=self.headers) req = Request(env) assert req.port == port assert req.netloc == '{0}:{1}'.format(host, port) # ------------------------------------------------------------------------- # Helpers # ------------------------------------------------------------------------- def _test_attribute_header(self, name, value, attr, default=None): headers = {name: value} req = Request(testing.create_environ(headers=headers)) assert getattr(req, attr) == value req = Request(testing.create_environ()) assert getattr(req, attr) == default def _test_header_expected_value(self, name, value, attr, expected_value): headers = {name: value} req = Request(testing.create_environ(headers=headers)) assert getattr(req, attr) == expected_value def _test_error_details(self, headers, attr_name, error_type, title, description): req = Request(testing.create_environ(headers=headers)) try: getattr(req, attr_name) pytest.fail('{0} not raised'.format(error_type.__name__)) except error_type as ex: assert ex.title == title assert ex.description == description
def test_client_accepts(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) headers = {'Accept': '*/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) self.assertFalse(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {} # NOTE(kgriffs): Equivalent to '*/*' per RFC req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'application/xm'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'text/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('text/csv')) self.assertFalse(req.client_accepts('application/xhtml+xml')) headers = {'Accept': 'text/*, application/xhtml+xml; q=0.0'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('text/csv')) self.assertFalse(req.client_accepts('application/xhtml+xml')) headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('application/xhtml+xml')) headers = {'Accept': 'text/*, application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'text/*,application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack'))
class TestReqVars(testing.TestBase): def before(self): self.qs = 'marker=deadbeef&limit=10' self.headers = { 'Content-Type': 'text/plain', 'Content-Length': '4829', 'Authorization': '' } self.app = '/test' self.path = '/hello' self.relative_uri = self.path + '?' + self.qs self.req = Request(testing.create_environ( app=self.app, port=8080, path='/hello', query_string=self.qs, headers=self.headers)) self.req_noqs = Request(testing.create_environ( app=self.app, path='/hello', headers=self.headers)) def test_missing_qs(self): env = testing.create_environ() if 'QUERY_STRING' in env: del env['QUERY_STRING'] # Should not cause an exception when Request is instantiated Request(env) def test_empty(self): self.assertIs(self.req.auth, None) def test_host(self): self.assertEqual(self.req.host, testing.DEFAULT_HOST) def test_subdomain(self): req = Request(testing.create_environ( host='com', path='/hello', headers=self.headers)) self.assertIs(req.subdomain, None) req = Request(testing.create_environ( host='example.com', path='/hello', headers=self.headers)) self.assertEqual(req.subdomain, 'example') req = Request(testing.create_environ( host='highwire.example.com', path='/hello', headers=self.headers)) self.assertEqual(req.subdomain, 'highwire') req = Request(testing.create_environ( host='lb01.dfw01.example.com', port=8080, path='/hello', headers=self.headers)) self.assertEqual(req.subdomain, 'lb01') # NOTE(kgriffs): Behavior for IP addresses is undefined, # so just make sure it doesn't blow up. req = Request(testing.create_environ( host='127.0.0.1', path='/hello', headers=self.headers)) self.assertEqual(type(req.subdomain), str) # NOTE(kgriffs): Test fallback to SERVER_NAME by using # HTTP 1.0, which will cause .create_environ to not set # HTTP_HOST. req = Request(testing.create_environ( protocol='HTTP/1.0', host='example.com', path='/hello', headers=self.headers)) self.assertEqual(req.subdomain, 'example') def test_reconstruct_url(self): req = self.req scheme = req.protocol host = req.get_header('host') app = req.app path = req.path query_string = req.query_string expected_uri = ''.join([scheme, '://', host, app, path, '?', query_string]) self.assertEqual(expected_uri, req.uri) @testtools.skipUnless(six.PY3, 'Test only applies to Python 3') def test_nonlatin_path(self): cyrillic_path = u'/hello_\u043f\u0440\u0438\u0432\u0435\u0442' cyrillic_path_decoded = cyrillic_path.encode('utf-8').decode('latin1') req = Request(testing.create_environ( host='com', path=cyrillic_path_decoded, headers=self.headers)) self.assertEqual(req.path, cyrillic_path) def test_uri(self): uri = ('http://' + testing.DEFAULT_HOST + ':8080' + self.app + self.relative_uri) self.assertEqual(self.req.url, uri) # NOTE(kgriffs): Call twice to check caching works self.assertEqual(self.req.uri, uri) self.assertEqual(self.req.uri, uri) uri_noqs = ('http://' + testing.DEFAULT_HOST + self.app + self.path) self.assertEqual(self.req_noqs.uri, uri_noqs) def test_uri_https(self): # ======================================================= # Default port, implicit # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https')) uri = ('https://' + testing.DEFAULT_HOST + '/hello') self.assertEqual(req.uri, uri) # ======================================================= # Default port, explicit # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https', port=443)) uri = ('https://' + testing.DEFAULT_HOST + '/hello') self.assertEqual(req.uri, uri) # ======================================================= # Non-default port # ======================================================= req = Request(testing.create_environ( path='/hello', scheme='https', port=22)) uri = ('https://' + testing.DEFAULT_HOST + ':22/hello') self.assertEqual(req.uri, uri) def test_uri_http_1_0(self): # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', app=self.app, port=80, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('http://' + testing.DEFAULT_HOST + self.app + self.relative_uri) self.assertEqual(req.uri, uri) # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', app=self.app, port=8080, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('http://' + testing.DEFAULT_HOST + ':8080' + self.app + self.relative_uri) self.assertEqual(req.uri, uri) # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', scheme='https', app=self.app, port=443, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('https://' + testing.DEFAULT_HOST + self.app + self.relative_uri) self.assertEqual(req.uri, uri) # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', scheme='https', app=self.app, port=22, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('https://' + testing.DEFAULT_HOST + ':22' + self.app + self.relative_uri) self.assertEqual(req.uri, uri) def test_relative_uri(self): self.assertEqual(self.req.relative_uri, self.app + self.relative_uri) self.assertEqual( self.req_noqs.relative_uri, self.app + self.path) req_noapp = Request(testing.create_environ( path='/hello', query_string=self.qs, headers=self.headers)) self.assertEqual(req_noapp.relative_uri, self.relative_uri) req_noapp = Request(testing.create_environ( path='/hello/', query_string=self.qs, headers=self.headers)) # NOTE(kgriffs): Call twice to check caching works self.assertEqual(req_noapp.relative_uri, self.relative_uri) self.assertEqual(req_noapp.relative_uri, self.relative_uri) def test_client_accepts(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) headers = {'Accept': '*/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) self.assertFalse(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {} # NOTE(kgriffs): Equivalent to '*/*' per RFC req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'application/xm'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'text/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('text/csv')) self.assertFalse(req.client_accepts('application/xhtml+xml')) headers = {'Accept': 'text/*, application/xhtml+xml; q=0.0'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('text/csv')) self.assertTrue(req.client_accepts('application/xhtml+xml')) headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) headers = {'Accept': 'text/*, application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'text/*,application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) def test_client_accepts_bogus(self): headers = {'Accept': '~'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('text/plain')) self.assertFalse(req.client_accepts('application/json')) def test_client_accepts_props(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts_xml) self.assertFalse(req.client_accepts_json) self.assertFalse(req.client_accepts_msgpack) headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts_xml) self.assertTrue(req.client_accepts_json) self.assertTrue(req.client_accepts_msgpack) headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts_xml) self.assertTrue(req.client_accepts_json) self.assertFalse(req.client_accepts_msgpack) headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts_xml) self.assertFalse(req.client_accepts_json) self.assertTrue(req.client_accepts_msgpack) headers = { 'Accept': 'application/json,application/xml,application/x-msgpack' } req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts_xml) self.assertTrue(req.client_accepts_json) self.assertTrue(req.client_accepts_msgpack) def test_client_prefers(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xml']) self.assertEqual(preferred_type, 'application/xml') headers = {'Accept': '*/*'} preferred_type = req.client_prefers(('application/xml', 'application/json')) # NOTE(kgriffs): If client doesn't care, "preferr" the first one self.assertEqual(preferred_type, 'application/xml') headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) self.assertEqual(preferred_type, 'application/xhtml+xml') headers = {'Accept': '3p12845j;;;asfd;'} req = Request(testing.create_environ(headers=headers)) preferred_type = req.client_prefers(['application/xhtml+xml']) self.assertEqual(preferred_type, None) def test_range(self): headers = {'Range': 'bytes=10-'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.range, (10, -1)) headers = {'Range': 'bytes=10-20'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.range, (10, 20)) headers = {'Range': 'bytes=-10240'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.range, (-10240, -1)) headers = {'Range': 'bytes=0-2'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.range, (0, 2)) headers = {'Range': ''} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPInvalidHeader, lambda: req.range) req = Request(testing.create_environ()) self.assertIs(req.range, None) def test_range_invalid(self): headers = {'Range': 'bytes=10240'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=-'} expected_desc = ('The value provided for the Range header is ' 'invalid. The byte offsets are missing.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': 'bytes=--'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=-3-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=3-3-4'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=3-3-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=3-3- '} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=fizbit'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=a-'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=a-3'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=-b'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=3-b'} req = Request(testing.create_environ(headers=headers)) self.assertRaises(falcon.HTTPBadRequest, lambda: req.range) headers = {'Range': 'bytes=x-y'} expected_desc = ('The value provided for the Range header is ' 'invalid. It must be a byte range formatted ' 'according to RFC 2616.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': 'bytes=0-0,-1'} expected_desc = ('The value provided for the Range ' 'header is invalid. The value must be a ' 'continuous byte range.') self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) headers = {'Range': '10-'} expected_desc = ("The value provided for the Range " "header is invalid. The value must be " "prefixed with 'bytes='") self._test_error_details(headers, 'range', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) def test_missing_attribute_header(self): req = Request(testing.create_environ()) self.assertEqual(req.range, None) req = Request(testing.create_environ()) self.assertEqual(req.content_length, None) def test_content_length(self): headers = {'content-length': '5656'} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.content_length, 5656) headers = {'content-length': ''} req = Request(testing.create_environ(headers=headers)) self.assertEqual(req.content_length, None) def test_bogus_content_length_nan(self): headers = {'content-length': 'fuzzy-bunnies'} expected_desc = ('The value provided for the ' 'Content-Length header is invalid. The value ' 'of the header must be a number.') self._test_error_details(headers, 'content_length', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) def test_bogus_content_length_neg(self): headers = {'content-length': '-1'} expected_desc = ('The value provided for the Content-Length ' 'header is invalid. The value of the header ' 'must be a positive number.') self._test_error_details(headers, 'content_length', falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc) @ddt.data(('Date', 'date'), ('If-Modified-since', 'if_modified_since'), ('If-Unmodified-since', 'if_unmodified_since'), ) @ddt.unpack def test_date(self, header, attr): date = datetime.datetime(2013, 4, 4, 5, 19, 18) date_str = 'Thu, 04 Apr 2013 05:19:18 GMT' self._test_header_expected_value(header, date_str, attr, date) @ddt.data(('Date', 'date'), ('If-Modified-Since', 'if_modified_since'), ('If-Unmodified-Since', 'if_unmodified_since'), ) @ddt.unpack def test_date_invalid(self, header, attr): # Date formats don't conform to RFC 1123 headers = {header: 'Thu, 04 Apr 2013'} expected_desc = ('The value provided for the {0} ' 'header is invalid. It must be formatted ' 'according to RFC 7231, Section 7.1.1.1') self._test_error_details(headers, attr, falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc.format(header)) headers = {header: ''} self._test_error_details(headers, attr, falcon.HTTPInvalidHeader, 'Invalid header value', expected_desc.format(header)) @ddt.data('date', 'if_modified_since', 'if_unmodified_since') def test_date_missing(self, attr): req = Request(testing.create_environ()) self.assertIs(getattr(req, attr), None) def test_attribute_headers(self): hash = 'fa0d1a60ef6616bb28038515c8ea4cb2' auth = 'HMAC_SHA1 c590afa9bb59191ffab30f223791e82d3fd3e3af' agent = 'testing/1.0.1' default_agent = 'curl/7.24.0 (x86_64-apple-darwin12.0)' self._test_attribute_header('Accept', 'x-falcon', 'accept', default='*/*') self._test_attribute_header('Authorization', auth, 'auth') self._test_attribute_header('Content-Type', 'text/plain', 'content_type') self._test_attribute_header('Expect', '100-continue', 'expect') self._test_attribute_header('If-Match', hash, 'if_match') self._test_attribute_header('If-None-Match', hash, 'if_none_match') self._test_attribute_header('If-Range', hash, 'if_range') self._test_attribute_header('User-Agent', agent, 'user_agent', default=default_agent) def test_method(self): self.assertEqual(self.req.method, 'GET') self.req = Request(testing.create_environ(path='', method='HEAD')) self.assertEqual(self.req.method, 'HEAD') def test_empty_path(self): self.req = Request(testing.create_environ(path='')) self.assertEqual(self.req.path, '/') def test_content_type_method(self): self.assertEqual(self.req.get_header('content-type'), 'text/plain') def test_content_length_method(self): self.assertEqual(self.req.get_header('content-length'), '4829') # ------------------------------------------------------------------------- # Helpers # ------------------------------------------------------------------------- def _test_attribute_header(self, name, value, attr, default=None): headers = {name: value} req = Request(testing.create_environ(headers=headers)) self.assertEqual(getattr(req, attr), value) req = Request(testing.create_environ()) self.assertEqual(getattr(req, attr), default) def _test_header_expected_value(self, name, value, attr, expected_value): headers = {name: value} req = Request(testing.create_environ(headers=headers)) self.assertEqual(getattr(req, attr), expected_value) def _test_error_details(self, headers, attr_name, error_type, title, description): req = Request(testing.create_environ(headers=headers)) try: getattr(req, attr_name) self.fail('{0} not raised'.format(error_type.__name__)) except error_type as ex: self.assertEqual(ex.title, title) self.assertEqual(ex.description, description)
def test_uri_http_1_0(self): # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', app=self.app, port=80, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('http://' + testing.DEFAULT_HOST + self.app + self.relative_uri) self.assertEqual(req.uri, uri) # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', app=self.app, port=8080, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('http://' + testing.DEFAULT_HOST + ':8080' + self.app + self.relative_uri) self.assertEqual(req.uri, uri) # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', scheme='https', app=self.app, port=443, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('https://' + testing.DEFAULT_HOST + self.app + self.relative_uri) self.assertEqual(req.uri, uri) # ======================================================= # HTTP, 80 # ======================================================= req = Request(testing.create_environ( protocol='HTTP/1.0', scheme='https', app=self.app, port=22, path='/hello', query_string=self.qs, headers=self.headers)) uri = ('https://' + testing.DEFAULT_HOST + ':22' + self.app + self.relative_uri) self.assertEqual(req.uri, uri)
def test_client_accepts(self): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) headers = {'Accept': '*/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) self.assertFalse(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {} # NOTE(kgriffs): Equivalent to '*/*' per RFC req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) headers = {'Accept': 'application/x-msgpack'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'application/xm'} req = Request(testing.create_environ(headers=headers)) self.assertFalse(req.client_accepts('application/xml')) headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'text/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('text/csv')) self.assertFalse(req.client_accepts('application/xhtml+xml')) headers = {'Accept': 'text/*, application/xhtml+xml; q=0.0'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('text/csv')) self.assertTrue(req.client_accepts('application/xhtml+xml')) headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) headers = {'Accept': 'text/*, application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack')) headers = {'Accept': 'text/*,application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('application/xml')) self.assertTrue(req.client_accepts('application/json')) self.assertTrue(req.client_accepts('application/x-msgpack'))
def __call__(self, env, start_response): """WSGI "app" method Makes instances of API callable by any WSGI server. See also PEP 333. Args: env: A WSGI environment dictionary start_response: A WSGI helper method for setting status and headers on a response. """ req = Request(env) resp = Response() responder, params = self._get_responder(req.path, req.method) try: # NOTE(kgriffs): Using an inner try..except in order to # address the case when err_handler raises HTTPError. # # NOTE(kgriffs): Coverage is giving false negatives, # so disabled on relevant lines. All paths are tested # afaict. try: responder(req, resp, **params) # pragma: no cover except Exception as ex: for err_type, err_handler in self._error_handlers: if isinstance(ex, err_type): err_handler(ex, req, resp, params) break # pragma: no cover else: # PERF(kgriffs): This will propagate HTTPError to # the handler below. It makes handling HTTPError # less efficient, but that is OK since error cases # don't need to be as fast as the happy path, and # indeed, should perhaps be slower to create # backpressure on clients that are issuing bad # requests. raise except HTTPError as ex: resp.status = ex.status if ex.headers is not None: resp.set_headers(ex.headers) if req.client_accepts('application/json'): resp.body = ex.json() # # Set status and headers # use_body = not helpers.should_ignore_body(resp.status, req.method) if use_body: helpers.set_content_length(resp) body = helpers.get_body(resp) else: # Default: return an empty body body = [] # Set content type if needed use_content_type = (body or req.method == 'HEAD' or resp.status == HTTP_416) if use_content_type: media_type = self._media_type else: media_type = None headers = resp._wsgi_headers(media_type) # Return the response per the WSGI spec start_response(resp.status, headers) return body