def testRegularCORSHeaders(self): """ Validates that the headers are handled correctly for a CORS request that doesn't use the OPTIONS verb """ # The origin to use in the tests dummyOrigin = 'http://foo.com' expectedHeaders = [ 'X-FluidDB-Error-Class', 'X-FluidDB-Path', 'X-FluidDB-Message', 'X-FluidDB-ObjectId', 'X-FluidDB-Name', 'X-FluidDB-Category', 'X-FluidDB-Action', 'X-FluidDB-Rangetype', 'X-FluidDB-Fieldname', 'X-FluidDB-Argument', 'X-FluidDB-Access-Token', 'X-FluidDB-Username', 'X-FluidDB-New-User' ] payload = { 'description': 'A namespace for tags that I add to people', 'name': 'people' } content = json.dumps(payload) contentLength = len(content) request = http.Request(DummyChannel(), False) request.method = 'POST' request.requestHeaders.setRawHeaders('Content-Length', [str(contentLength)]) request.requestHeaders.setRawHeaders('Content-Type', ['application/json']) request.requestHeaders.setRawHeaders('Host', ['fluiddb.fluidinfo.com']) request.requestHeaders.setRawHeaders('Origin', [dummyOrigin]) request._fluidDB_reqid = 'xxx' request.args = dict() request.postpath = [] request.content = StringIO(content) resource = NamespacesResource(FakeFacadeClient(), FakeSession()) resource.render(request) # 201 Created self.assertEqual(request.code, http.CREATED) # check we have the required headers headers = request.responseHeaders self.assertTrue(headers.hasHeader('Access-Control-Allow-Origin')) self.assertTrue(headers.hasHeader('Access-Control-Allow-Credentials')) self.assertTrue(headers.hasHeader('Access-Control-Expose-Headers')) # check the values of the required headers self.assertEqual( dummyOrigin, headers.getRawHeaders('Access-Control-Allow-Origin')[0]) self.assertEqual( 'true', headers.getRawHeaders('Access-Control-Allow-Credentials')[0]) accessControlExposeHeaders = headers.getRawHeaders( 'Access-Control-Expose-Headers')[0] # Make sure we haven't accidentally turned the header value into a # Python tuple by including a comma in its definition. self.assertTrue(isinstance(accessControlExposeHeaders, str)) # Make sure we haven't accidentally left a comma space in the last # element of the Python header definition. self.assertFalse(accessControlExposeHeaders.endswith(', ')) actualHeaders = accessControlExposeHeaders.split(', ') for header in expectedHeaders: self.assertTrue(header.startswith('X-FluidDB-')) self.assertTrue(header in actualHeaders)
def testInvalidCORSPreFlightRequestNoACRM(self): """ The request has an Origin header but no Access-Control-Request-Method so check it returns a regular OPTIONS response. """ # The origin to use in the tests dummyOrigin = 'http://foo.com' request = http.Request(DummyChannel(), False) request.method = 'OPTIONS' request.requestHeaders.setRawHeaders('Origin', [dummyOrigin]) request._fluidDB_reqid = 'xxx' request.args = dict() request.postpath = [] request.content = NoContent() resource = NamespacesResource(FakeFacadeClient(), FakeSession()) resource._handleOptions(request, dummyOrigin) # 200 OK self.assertEqual(request.code, http.OK) # check we have the required headers headers = request.responseHeaders self.assertTrue(headers.hasHeader('Allow')) # Check the allowed methods (including and in addition to those # allowed for CORS) allowedMethods = headers.getRawHeaders('Allow')[0].split(', ') self.assertEqual(len(resource.allowedMethods), len(allowedMethods)) for item in resource.allowedMethods: self.assertTrue(item in allowedMethods) # There are *NO* CORS related headers self.assertFalse(headers.hasHeader('Access-Control-Allow-Origin')) self.assertFalse(headers.hasHeader('Access-Control-Max-Age')) self.assertFalse(headers.hasHeader('Access-Control-Allow-Credentials')) self.assertFalse(headers.hasHeader('Access-Control-Allow-Methods'))
def _reset(self): # not very random at all self.c = CookieInstaller( secureRandom=lambda nbytes: 'x' * nbytes, insecureName='__', secureName='_s') self.request = http.Request(DummyChannel(), None)
def testUnauthorizedOptionsCall(self): """ Validates that a regular non-CORS OPTIONS call returns an appropriate response """ request = http.Request(DummyChannel(), False) request.method = 'OPTIONS' request._fluidDB_reqid = 'xxx' request.args = dict() request.postpath = [] request.content = NoContent() request.requestHeaders.setRawHeaders('Origin', ['*']) resource = WSFEUnauthorizedResource( [BasicCredentialFactory('example.com')]) resource.render(request) self.assertEqual(http.OK, request.code) headers = request.responseHeaders self.assertEqual('DELETE, GET, HEAD, POST, PUT', headers.getRawHeaders('Allow')[0]) self.assertEqual( 'Accept, Authorization, Content-Type, X-FluidDB-Access-Token', headers.getRawHeaders('Access-Control-Allow-Headers')[0]) self.assertEqual( 'DELETE, GET, HEAD, POST, PUT', headers.getRawHeaders('Access-Control-Allow-Methods')[0])
def test_setHeader(self): """ L{http.Request.setHeader} sets the value of the given response header. """ req = http.Request(DummyChannel(), None) req.setHeader("test", "lemur") self.assertEquals(req.responseHeaders.getRawHeaders("test"), ["lemur"])
def testContentMD5OK(self): """ Checks that an incoming requests whose payload matches the Content-MD5 header. """ payload = {"description": "A namespace for tags that I'm using to" " add to people", "name": "people"} content = json.dumps(payload) contentLength = len(content) md5Content = base64.standard_b64encode(md5(content).digest()) request = http.Request(DummyChannel(), False) request.method = 'POST' request.requestHeaders.setRawHeaders("Content-MD5", [md5Content]) request.requestHeaders.setRawHeaders("Content-Length", [str(contentLength)]) request.requestHeaders.setRawHeaders("Content-Type", ["application/json"]) request.requestHeaders.setRawHeaders("Host", ["fluiddb.fluidinfo.com"]) request._fluidDB_reqid = 'xxx' request.args = dict() request.postpath = [] request.content = StringIO(content) resource = NamespacesResource(FakeFacadeClient(), FakeSession()) resource.render(request) self.assertEqual(request.code, http.CREATED)
def test_getHeaderNotFound(self): """ L{http.Request.getHeader} returns C{None} when asked for the value of a request header which is not present. """ req = http.Request(DummyChannel(), None) self.assertEquals(req.getHeader("test"), None)
def test_getAllHeadersNoHeaders(self): """ L{http.Request.getAllHeaders} returns an empty C{dict} if there are no request headers. """ req = http.Request(DummyChannel(), None) self.assertEquals(req.getAllHeaders(), {})
def testContentMD5DoesNotMatch(self): """ Checks that an incoming requests whose payload doesn't match the Content-MD5 header and returns a PRECONDITION FAILED (412). """ payload = {"description": "A namespace for tags that I'm using to" "add to people", "name": "people"} content = json.dumps(payload) contentLength = len(content) request = http.Request(DummyChannel(), False) request.method = 'POST' request.requestHeaders.setRawHeaders("Content-MD5", ["bad-md5"]) request.requestHeaders.setRawHeaders("Content-Length", [str(contentLength)]) request.requestHeaders.setRawHeaders("Content-Type", ["application/json"]) request._fluidDB_reqid = 'xxx' request.args = dict() request.postpath = [] request.content = StringIO(content) resource = NamespacesResource(FakeFacadeClient(), FakeSession()) resource.render(request) self.assertEqual(request.code, http.PRECONDITION_FAILED)
def testCorsHeadersDoNotAppearForRegularRequests(self): """ Make sure CORS related headers do NOT appear in regular non-CORS requests """ payload = { 'description': 'A namespace for tags I add to people', 'name': 'people' } content = json.dumps(payload) contentLength = len(content) request = http.Request(DummyChannel(), False) request.method = 'POST' request.requestHeaders.setRawHeaders('Content-Length', [str(contentLength)]) request.requestHeaders.setRawHeaders('Content-Type', ['application/json']) request.requestHeaders.setRawHeaders('Host', ['fluiddb.fluidinfo.com']) request._fluidDB_reqid = 'xxx' request.args = dict() request.postpath = [] request.content = StringIO(content) resource = NamespacesResource(FakeFacadeClient(), FakeSession()) resource.render(request) # 201 Created self.assertEqual(request.code, http.CREATED) # check we don't have the CORS related headers headers = request.responseHeaders self.assertFalse(headers.hasHeader('Access-Control-Allow-Origin')) self.assertFalse(headers.hasHeader('Access-Control-Allow-Credentials')) self.assertFalse(headers.hasHeader('Access-Control-Expose-Headers'))
def testRegularOptionsCall(self): """ Validates that a regular non-CORS OPTIONS call returns an appropriate response """ request = http.Request(DummyChannel(), False) request.method = 'OPTIONS' request._fluidDB_reqid = 'xxx' request.args = dict() request.postpath = [] request.content = NoContent() resource = NamespacesResource(FakeFacadeClient(), FakeSession()) resource._handleOptions(request, None) # 200 OK self.assertEqual(request.code, http.OK) # check we have the required headers headers = request.responseHeaders self.assertTrue(headers.hasHeader('Allow')) # Check the allowed methods (including and in addition to those # allowed for CORS) allowedMethods = headers.getRawHeaders('Allow')[0].split(', ') self.assertEqual(len(resource.allowedMethods), len(allowedMethods)) for item in resource.allowedMethods: self.assertTrue(item in allowedMethods) # There are *NO* CORS related headers self.assertFalse(headers.hasHeader('Access-Control-Allow-Origin')) self.assertFalse(headers.hasHeader('Access-Control-Max-Age')) self.assertFalse(headers.hasHeader('Access-Control-Allow-Credentials')) self.assertFalse(headers.hasHeader('Access-Control-Allow-Methods'))
def _prequest(**headers): """ Make a request with the given request headers for the persistence tests. """ request = http.Request(DummyChannel(), None) for k, v in headers.iteritems(): request.requestHeaders.setRawHeaders(k, v) return request
def test_getAllHeadersMultipleHeaders(self): """ When there are multiple values for a single request header, L{http.Request.getAllHeaders} returns only the last value. """ req = http.Request(DummyChannel(), None) req.requestHeaders.setRawHeaders("test", ["lemur", "panda"]) self.assertEquals(req.getAllHeaders(), {"test": "panda"})
def test_setHost(self): """ L{http.Request.setHost} sets the value of the host request header. """ req = http.Request(DummyChannel(), None) req.setHost("example.com", 443) self.assertEqual( req.requestHeaders.getRawHeaders("host"), ["example.com"])
def test_getAllHeaders(self): """ L{http.Request.getAllheaders} returns a C{dict} mapping all request header names to their corresponding values. """ req = http.Request(DummyChannel(), None) req.requestHeaders.setRawHeaders("test", ["lemur"]) self.assertEquals(req.getAllHeaders(), {"test": "lemur"})
def test_getHeaderReceivedMultiples(self): """ When there are multiple values for a single request header, L{http.Request.getHeader} returns the last value. """ req = http.Request(DummyChannel(), None) req.requestHeaders.setRawHeaders("test", ["lemur", "panda"]) self.assertEquals(req.getHeader("test"), "panda")
def test_getHeader(self): """ L{http.Request.getHeader} returns the value of the named request header. """ req = http.Request(DummyChannel(), None) req.requestHeaders.setRawHeaders("test", ["lemur"]) self.assertEquals(req.getHeader("test"), "lemur")
def test_registerProducerTwiceFails(self): """ Calling L{Request.registerProducer} when a producer is already registered raises ValueError. """ req = http.Request(DummyChannel(), None) req.registerProducer(DummyProducer(), True) self.assertRaises(ValueError, req.registerProducer, DummyProducer(), True)
def test_registerProducerWhenQueuedPausesPushProducer(self): """ Calling L{Request.registerProducer} with an IPushProducer when the request is queued pauses the producer. """ req = http.Request(DummyChannel(), True) producer = DummyProducer() req.registerProducer(producer, True) self.assertEquals(['pause'], producer.events)
def test_setResponseCodeAcceptsIntegers(self): """ L{http.Request.setResponseCode} accepts C{int} or C{long} for the code parameter and raises L{TypeError} if passed anything else. """ req = http.Request(DummyChannel(), None) req.setResponseCode(1) req.setResponseCode(1L) self.assertRaises(TypeError, req.setResponseCode, "1")
def test_registerProducerWhenQueuedDoesntPausePullProducer(self): """ Calling L{Request.registerProducer} with an IPullProducer when the request is queued does not pause the producer, because it doesn't make sense to pause a pull producer. """ req = http.Request(DummyChannel(), True) producer = DummyProducer() req.registerProducer(producer, False) self.assertEquals([], producer.events)
def test_registerProducerWhenNotQueuedRegistersPullProducer(self): """ Calling L{Request.registerProducer} with an IPullProducer when the request is not queued registers the producer as a pull producer on the request's transport. """ req = http.Request(DummyChannel(), False) producer = DummyProducer() req.registerProducer(producer, False) self.assertEquals([(producer, False)], req.transport.producers)
def test_finishAfterConnectionLost(self): """ Calling L{Request.finish} after L{Request.connectionLost} has been called results in a L{RuntimeError} being raised. """ channel = DummyChannel() transport = channel.transport req = http.Request(channel, False) req.connectionLost(Failure(ConnectionLost("The end."))) self.assertRaises(RuntimeError, req.finish)
def _create_post_request(self, path, data): request = http.Request(http.HTTPChannel(), True) request.method = "post" request.path = path request.args = {} request.gotLength(1000) request.requestHeaders = Headers( {"Content-Type": ['application/json']}) request.handleContentChunk(json.dumps(data)) return request
def test_connectionLost(self): """ Ensure that the CGI process ends cleanly when the request connection is lost. """ d = DummyChannel() request = http.Request(d, True) protocol = twcgi.CGIProcessProtocol(request) request.connectionLost(failure.Failure(ConnectionLost("Connection done"))) protocol.processEnded(failure.Failure(error.ProcessTerminated()))
def test_connectionLostNotification(self): """ L{Request.connectionLost} triggers all finish notification Deferreds and cleans up per-request state. """ d = DummyChannel() request = http.Request(d, True) finished = request.notifyFinish() request.connectionLost(Failure(ConnectionLost("Connection done"))) self.assertIdentical(request.channel, None) return self.assertFailure(finished, ConnectionLost)
def test_parseCookiesMultipleHeaders(self): """ L{http.Request.parseCookies} can extract cookies from multiple Cookie headers. """ req = http.Request(DummyChannel(), None) req.requestHeaders.setRawHeaders( "cookie", ['test="lemur"', 'test2="panda"']) req.parseCookies() self.assertEquals(req.received_cookies, {"test": '"lemur"', "test2": '"panda"'})
def test_parseCookies(self): """ L{http.Request.parseCookies} extracts cookies from C{requestHeaders} and adds them to C{received_cookies}. """ req = http.Request(DummyChannel(), None) req.requestHeaders.setRawHeaders( "cookie", ['test="lemur"; test2="panda"']) req.parseCookies() self.assertEquals(req.received_cookies, {"test": '"lemur"', "test2": '"panda"'})
def test_finishNotification(self): """ L{Request.finish} triggers all finish notification Deferreds. """ request = http.Request(DummyChannel(), False) finished = request.notifyFinish() # Force the request to have a non-None content attribute. This is # probably a bug in Request. request.gotLength(1) request.finish() return finished
def test_setResponseCodeAndMessage(self): """ L{http.Request.setResponseCode} takes a status code and a message and causes them to be used as the response status. """ channel = DummyChannel() req = http.Request(channel, None) req.setResponseCode(202, "happily accepted") req.write('') self.assertEqual(channel.transport.written.getvalue().splitlines()[0], '%s 202 happily accepted' % (req.clientproto, ))