def test_HTTP11_pipelining(self): self.httpserver.protocol = "HTTP/1.1" self.PROTOCOL = "HTTP/1.1" # Test pipelining. httplib doesn't support this directly. self.persistent = True conn = self.HTTP_CONN # Put request 1 conn.putrequest("GET", "/hello", skip_host=True) conn.putheader("Host", self.HOST) conn.endheaders() for trial in range(5): # Put next request conn._output(ntob('GET /hello?%s HTTP/1.1' % trial)) conn._output(ntob("Host: %s" % self.HOST, 'ascii')) conn._send_output() # Retrieve previous response response = conn.response_class(conn.sock, method="GET") response.begin() body = response.read(13) self.assertEqual(response.status, 200) self.assertEqual(body, ntob("Hello, world!")) # Retrieve final response response = conn.response_class(conn.sock, method="GET") response.begin() body = response.read() self.assertEqual(response.status, 200) self.assertEqual(body, ntob("Hello, world!")) conn.close()
def output(self): if self.body is None: return [] elif isinstance(self.body, (tuple, list)): return [ntob(x) for x in self.body] else: return [ntob(self.body)]
def test_empty_string_app(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return [ ntob('Hello'), ntob(''), ntob(' '), ntob(''), ntob('world') ]
def test_HTTP11_pipelining(self): self.httpserver.protocol = 'HTTP/1.1' self.PROTOCOL = 'HTTP/1.1' # Test pipelining. httplib doesn't support this directly. self.persistent = True conn = self.HTTP_CONN # Put request 1 conn.putrequest('GET', '/hello', skip_host=True) conn.putheader('Host', self.HOST) conn.endheaders() for trial in range(5): # Put next request conn._output(ntob('GET /hello?%s HTTP/1.1' % trial)) conn._output(ntob('Host: %s' % self.HOST, 'ascii')) conn._send_output() # Retrieve previous response response = conn.response_class(conn.sock, method='GET') response.begin() body = response.read(13) self.assertEqual(response.status, 200) self.assertEqual(body, b'Hello, world!') # Retrieve final response response = conn.response_class(conn.sock, method='GET') response.begin() body = response.read() self.assertEqual(response.status, 200) self.assertEqual(body, b'Hello, world!') conn.close()
def test_100_Continue(self): if cherrypy.server.protocol_version != 'HTTP/1.1': return self.skip() self.PROTOCOL = 'HTTP/1.1' self.persistent = True conn = self.HTTP_CONN # Try a page without an Expect request header first. # Note that httplib's response.begin automatically ignores # 100 Continue responses, so we must manually check for it. try: conn.putrequest('POST', '/upload', skip_host=True) conn.putheader('Host', self.HOST) conn.putheader('Content-Type', 'text/plain') conn.putheader('Content-Length', '4') conn.endheaders() conn.send(ntob("d'oh")) response = conn.response_class(conn.sock, method='POST') version, status, reason = response._read_status() self.assertNotEqual(status, 100) finally: conn.close() # Now try a page with an Expect header... try: conn.connect() conn.putrequest('POST', '/upload', skip_host=True) conn.putheader('Host', self.HOST) conn.putheader('Content-Type', 'text/plain') conn.putheader('Content-Length', '17') conn.putheader('Expect', '100-continue') conn.endheaders() response = conn.response_class(conn.sock, method='POST') # ...assert and then skip the 100 response version, status, reason = response._read_status() self.assertEqual(status, 100) while True: line = response.fp.readline().strip() if line: self.fail( '100 Continue should not output any headers. Got %r' % line) else: break # ...send the body body = ntob('I am a small file') conn.send(body) # ...get the final response response.begin() self.status, self.headers, self.body = webtest.shb(response) self.assertStatus(200) self.assertBody("thanks for '%s'" % body) finally: conn.close()
def output(self): if self.body is None: return [] elif isinstance(self.body, six.text_type): return [ntob(self.body)] elif isinstance(self.body, six.binary_type): return [self.body] else: return [ntob(x) for x in self.body]
def bar(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain'), ('Content-Length', '3')] write = start_response(status, response_headers) write(ntob('boo')) # This should fail according to the WSGI spec. try: noname except NameError: start_response(status, response_headers, sys.exc_info()) return [ntob('Hello world')]
def test_100_Continue(self): self.httpserver.protocol = "HTTP/1.1" self.PROTOCOL = "HTTP/1.1" self.persistent = True conn = self.HTTP_CONN # Try a page without an Expect request header first. # Note that httplib's response.begin automatically ignores # 100 Continue responses, so we must manually check for it. conn.putrequest("POST", "/upload", skip_host=True) conn.putheader("Host", self.HOST) conn.putheader("Content-Type", "text/plain") conn.putheader("Content-Length", "4") conn.endheaders() conn.send(ntob("d'oh")) response = conn.response_class(conn.sock, method="POST") version, status, reason = response._read_status() self.assertNotEqual(status, 100) conn.close() # Now try a page with an Expect header... conn.connect() conn.putrequest("POST", "/upload", skip_host=True) conn.putheader("Host", self.HOST) conn.putheader("Content-Type", "text/plain") conn.putheader("Content-Length", "17") conn.putheader("Expect", "100-continue") conn.endheaders() response = conn.response_class(conn.sock, method="POST") # ...assert and then skip the 100 response version, status, reason = response._read_status() self.assertEqual(status, 100) while True: line = response.fp.readline().strip() if line: self.fail( "100 Continue should not output any headers. Got %r" % line) else: break # ...send the body body = ntob("I am a small file") conn.send(body) # ...get the final response response.begin() self.status, self.headers, self.body = webtest.shb(response) self.assertStatus(200) self.assertBody("thanks for '%s'" % body) conn.close()
def test_100_Continue(self): self.httpserver.protocol = "HTTP/1.1" self.PROTOCOL = "HTTP/1.1" self.persistent = True conn = self.HTTP_CONN # Try a page without an Expect request header first. # Note that httplib's response.begin automatically ignores # 100 Continue responses, so we must manually check for it. conn.putrequest("POST", "/upload", skip_host=True) conn.putheader("Host", self.HOST) conn.putheader("Content-Type", "text/plain") conn.putheader("Content-Length", "4") conn.endheaders() conn.send(ntob("d'oh")) response = conn.response_class(conn.sock, method="POST") version, status, reason = response._read_status() self.assertNotEqual(status, 100) conn.close() # Now try a page with an Expect header... conn.connect() conn.putrequest("POST", "/upload", skip_host=True) conn.putheader("Host", self.HOST) conn.putheader("Content-Type", "text/plain") conn.putheader("Content-Length", "17") conn.putheader("Expect", "100-continue") conn.endheaders() response = conn.response_class(conn.sock, method="POST") # ...assert and then skip the 100 response version, status, reason = response._read_status() self.assertEqual(status, 100) while True: line = response.fp.readline().strip() if line: self.fail("100 Continue should not output any headers. Got %r" % line) else: break # ...send the body body = ntob("I am a small file") conn.send(body) # ...get the final response response.begin() self.status, self.headers, self.body = webtest.shb(response) self.assertStatus(200) self.assertBody("thanks for '%s'" % body) conn.close()
def test_Chunked_Encoding(self): if cherrypy.server.protocol_version != 'HTTP/1.1': return self.skip() if (hasattr(self, 'harness') and 'modpython' in self.harness.__class__.__name__.lower()): # mod_python forbids chunked encoding return self.skip() self.PROTOCOL = 'HTTP/1.1' # Set our HTTP_CONN to an instance so it persists between requests. self.persistent = True conn = self.HTTP_CONN # Try a normal chunked request (with extensions) body = ntob('8;key=value\r\nxx\r\nxxxx\r\n5\r\nyyyyy\r\n0\r\n' 'Content-Type: application/json\r\n' '\r\n') conn.putrequest('POST', '/upload', skip_host=True) conn.putheader('Host', self.HOST) conn.putheader('Transfer-Encoding', 'chunked') conn.putheader('Trailer', 'Content-Type') # Note that this is somewhat malformed: # we shouldn't be sending Content-Length. # RFC 2616 says the server should ignore it. conn.putheader('Content-Length', '3') conn.endheaders() conn.send(body) response = conn.getresponse() self.status, self.headers, self.body = webtest.shb(response) self.assertStatus('200 OK') self.assertBody("thanks for '%s'" % ntob('xx\r\nxxxxyyyyy')) # Try a chunked request that exceeds server.max_request_body_size. # Note that the delimiters and trailer are included. body = ntob('3e3\r\n' + ('x' * 995) + '\r\n0\r\n\r\n') conn.putrequest('POST', '/upload', skip_host=True) conn.putheader('Host', self.HOST) conn.putheader('Transfer-Encoding', 'chunked') conn.putheader('Content-Type', 'text/plain') # Chunked requests don't need a content-length ## conn.putheader("Content-Length", len(body)) conn.endheaders() conn.send(body) response = conn.getresponse() self.status, self.headers, self.body = webtest.shb(response) self.assertStatus(413) conn.close()
def putrequest(self, method, url): if self._HTTPConnection__response and self._HTTPConnection__response.isclosed(): self._HTTPConnection__response = None if self._HTTPConnection__state == http.client._CS_IDLE: self._HTTPConnection__state = http.client._CS_REQ_STARTED else: raise http.client.CannotSendRequest() self._method = method if not url: url = ntob('/') request = ntob(' ').join((method.encode("ASCII"), url, self._http_vsn_str.encode("ASCII"))) self._output(request)
def foo(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) # This should fail according to the WSGI spec. start_response(status, response_headers) return [ntob('Hello world')]
def __call__(self, environ, start_response): req, resp = Request(environ), Response() try: # Recode handler name as Python supports unicode method names # and we should try match those first. # Source of the idea: cherrypy._cpwsgi.AppResponse.recode_path_qs latin1_handler_name = environ['PATH_INFO'].lstrip('/').replace( '/', '_') try: unicode_handler_name = bton(ntob(latin1_handler_name), 'utf-8') handler = getattr(self, unicode_handler_name) except (UnicodeError, AttributeError): handler = getattr(self, latin1_handler_name) except AttributeError: resp.status = '404 Not Found' else: output = handler(req, resp) if (output is not None and not any( resp.status.startswith(status_code) for status_code in ('204', '304'))): resp.body = output try: resp.headers.setdefault('Content-Length', str(len(output))) except TypeError: if not isinstance(output, types.GeneratorType): raise start_response(resp.status, resp.headers.items()) return resp.output()
def test_garbage_in(self): # Connect without SSL regardless of server.scheme c = HTTPConnection('%s:%s' % (self.interface(), self.PORT)) c._output(ntob('gjkgjklsgjklsgjkljklsg')) c._send_output() response = c.response_class(c.sock, method='GET') try: response.begin() self.assertEqual(response.status, 400) self.assertEqual(response.fp.read(22), ntob('Malformed Request-Line')) c.close() except socket.error as ex: # "Connection reset by peer" is also acceptable. if ex.errno != errno.ECONNRESET: raise
def test_status_syntax_error(self): self.getPage("/bad") self.assertStatus(500) self.assertStatus(500) self.assertInBody( "Illegal response status from server (%s is non-numeric)." % (repr(ntob('error'))))
def test_parse_uri_unsafe_uri(self): """Test that malicious URI does not allow HTTP injection. This effectively checks that sending GET request with URL /%A0%D0blah%20key%200%20900%204%20data is not converted into GET / blah key 0 900 4 data HTTP/1.1 which would be a security issue otherwise. """ c = self._get_http_connection() c._output( ntob('GET /%A0%D0blah%20key%200%20900%204%20data HTTP/1.1', 'utf-8')) c._send_output() response = self._get_http_response(c, method='GET') response.begin() assert response.status == HTTP_OK assert response.fp.read(12) == b'Hello world!' c.close()
def test_garbage_in(self): # Connect without SSL regardless of server.scheme c = HTTPConnection('%s:%s' % (self.interface(), self.PORT)) c._output(ntob('gjkgjklsgjklsgjkljklsg')) c._send_output() response = c.response_class(c.sock, method="GET") try: response.begin() self.assertEqual(response.status, 400) self.assertEqual(response.fp.read(22), ntob("Malformed Request-Line")) c.close() except socket.error: e = sys.exc_info()[1] # "Connection reset by peer" is also acceptable. if e.errno != errno.ECONNRESET: raise
def test_queue_full(self): conns = [] overflow_conn = None try: # Make 15 initial requests and leave them open, which should use # all of wsgiserver's WorkerThreads and fill its Queue. for i in range(15): conn = self.HTTP_CONN(self.HOST, self.PORT) conn.putrequest('POST', '/upload', skip_host=True) conn.putheader('Host', self.HOST) conn.putheader('Content-Type', 'text/plain') conn.putheader('Content-Length', '4') conn.endheaders() conns.append(conn) # Now try a 16th conn, which should be closed by the server immediately. overflow_conn = self.HTTP_CONN(self.HOST, self.PORT) # Manually connect since httplib won't let us set a timeout for res in socket.getaddrinfo(self.HOST, self.PORT, 0, socket.SOCK_STREAM): af, socktype, proto, canonname, sa = res overflow_conn.sock = socket.socket(af, socktype, proto) overflow_conn.sock.settimeout(5) overflow_conn.sock.connect(sa) break overflow_conn.putrequest('GET', '/', skip_host=True) overflow_conn.putheader('Host', self.HOST) overflow_conn.endheaders() response = overflow_conn.response_class(overflow_conn.sock, method='GET') try: response.begin() except socket.error as exc: if exc.args[0] in socket_reset_errors: pass # Expected. else: tmpl = ( 'Overflow conn did not get RST. ' 'Got {exc.args!r} instead' ) raise AssertionError(tmpl.format(**locals())) except BadStatusLine: # This is a special case in OS X. Linux and Windows will # RST correctly. assert sys.platform == 'darwin' else: raise AssertionError('Overflow conn did not get RST ') finally: for conn in conns: conn.send(ntob('done')) response = conn.response_class(conn.sock, method='POST') response.begin() self.body = response.read() self.assertBody("thanks for 'done'") self.assertEqual(response.status, 200) conn.close() if overflow_conn: overflow_conn.close()
def test_queue_full(self): conns = [] overflow_conn = None try: # Make 15 initial requests and leave them open, which should use # all of wsgiserver's WorkerThreads and fill its Queue. for i in range(15): conn = self.HTTP_CONN(self.HOST, self.PORT) conn.putrequest('POST', '/upload', skip_host=True) conn.putheader('Host', self.HOST) conn.putheader('Content-Type', 'text/plain') conn.putheader('Content-Length', '4') conn.endheaders() conns.append(conn) # Now try a 16th conn, which should be closed by the server immediately. overflow_conn = self.HTTP_CONN(self.HOST, self.PORT) # Manually connect since httplib won't let us set a timeout for res in socket.getaddrinfo(self.HOST, self.PORT, 0, socket.SOCK_STREAM): af, socktype, proto, canonname, sa = res overflow_conn.sock = socket.socket(af, socktype, proto) overflow_conn.sock.settimeout(5) overflow_conn.sock.connect(sa) break overflow_conn.putrequest('GET', '/', skip_host=True) overflow_conn.putheader('Host', self.HOST) overflow_conn.endheaders() response = overflow_conn.response_class(overflow_conn.sock, method='GET') try: response.begin() except socket.error as exc: if exc.args[0] in socket_reset_errors: pass # Expected. else: tmpl = ('Overflow conn did not get RST. ' 'Got {exc.args!r} instead') raise AssertionError(tmpl.format(**locals())) except BadStatusLine: # This is a special case in OS X. Linux and Windows will # RST correctly. assert sys.platform == 'darwin' else: raise AssertionError('Overflow conn did not get RST ') finally: for conn in conns: conn.send(ntob('done')) response = conn.response_class(conn.sock, method='POST') response.begin() self.body = response.read() self.assertBody("thanks for 'done'") self.assertEqual(response.status, 200) conn.close() if overflow_conn: overflow_conn.close()
def test_No_CRLF(self): self.persistent = True conn = self.HTTP_CONN conn.send(ntob('GET /hello HTTP/1.1\n\n')) response = conn.response_class(conn.sock, method='GET') response.begin() self.body = response.read() self.assertBody('HTTP requires CRLF terminators') conn.close() conn.connect() conn.send(ntob('GET /hello HTTP/1.1\r\n\n')) response = conn.response_class(conn.sock, method='GET') response.begin() self.body = response.read() self.assertBody('HTTP requires CRLF terminators') conn.close()
def test_malformed_request_line(self): # Test missing version in Request-Line if self.scheme == 'https': c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT)) else: c = HTTPConnection('%s:%s' % (self.interface(), self.PORT)) c._output(ntob('GET /')) c._send_output() if hasattr(c, 'strict'): response = c.response_class(c.sock, strict=c.strict, method='GET') else: # Python 3.2 removed the 'strict' feature, saying: # "http.client now always assumes HTTP/1.x compliant servers." response = c.response_class(c.sock, method='GET') response.begin() self.assertEqual(response.status, 400) self.assertEqual(response.fp.read(22), ntob("Malformed Request-Line")) c.close()
def test_malformed_request_line(self): if getattr(cherrypy.server, 'using_apache', False): return self.skip('skipped due to known Apache differences...') # Test missing version in Request-Line c = self.make_connection() c._output(ntob('GET /')) c._send_output() if hasattr(c, 'strict'): response = c.response_class(c.sock, strict=c.strict, method='GET') else: # Python 3.2 removed the 'strict' feature, saying: # "http.client now always assumes HTTP/1.x compliant servers." response = c.response_class(c.sock, method='GET') response.begin() self.assertEqual(response.status, 400) self.assertEqual(response.fp.read(22), ntob('Malformed Request-Line')) c.close()
def start_response(self, status, headers, exc_info=None): """WSGI callable to begin the HTTP response.""" # "The application may call start_response more than once, # if and only if the exc_info argument is provided." if self.started_response and not exc_info: raise AssertionError("WSGI start_response called a second " "time with no exc_info.") self.started_response = True # "if exc_info is provided, and the HTTP headers have already been # sent, start_response must raise an error, and should raise the # exc_info tuple." if (exc_info is not None) and self.req.sent_headers: try: if py3k: raise exc_info[0](exc_info[1]).with_traceback(exc_info[2]) else: raise (exc_info[0], exc_info[1], exc_info[2]) finally: exc_info = None # According to PEP 3333, when using Python 3, the response status # and headers must be bytes masquerading as unicode; that is, they # must be of type "str" but are restricted to code points in the # "latin-1" set. if not isinstance(status, str): raise TypeError("WSGI response status is not of type str.") self.req.status = ntob(status) for k, v in headers: if not isinstance(k, str): raise TypeError( "WSGI response header key %s is not of type str." % repr(k)) if not isinstance(v, str): raise TypeError( "WSGI response header value %s is not of type str." % repr(v)) if k.lower() == 'content-length': self.remaining_bytes_out = int(v) self.req.outheaders.append((ntob(k), ntob(v))) return self.write
def test_HTTP11_pipelining(self): if cherrypy.server.protocol_version != 'HTTP/1.1': return self.skip() self.PROTOCOL = 'HTTP/1.1' # Test pipelining. httplib doesn't support this directly. self.persistent = True conn = self.HTTP_CONN # Put request 1 conn.putrequest('GET', '/hello', skip_host=True) conn.putheader('Host', self.HOST) conn.endheaders() for trial in range(5): # Put next request conn._output(ntob('GET /hello HTTP/1.1')) conn._output(ntob('Host: %s' % self.HOST, 'ascii')) conn._send_output() # Retrieve previous response response = conn.response_class(conn.sock, method='GET') # there is a bug in python3 regarding the buffering of # ``conn.sock``. Until that bug get's fixed we will # monkey patch the ``reponse`` instance. # https://bugs.python.org/issue23377 if six.PY3: response.fp = conn.sock.makefile('rb', 0) response.begin() body = response.read(13) self.assertEqual(response.status, 200) self.assertEqual(body, ntob('Hello, world!')) # Retrieve final response response = conn.response_class(conn.sock, method='GET') response.begin() body = response.read() self.assertEqual(response.status, 200) self.assertEqual(body, ntob('Hello, world!')) conn.close()
def _test_Chunked_Encoding(self): self.httpserver.protocol = "HTTP/1.1" self.PROTOCOL = "HTTP/1.1" # Set our HTTP_CONN to an instance so it persists between requests. self.persistent = True conn = self.HTTP_CONN # Try a normal chunked request (with extensions) body = ntob("8;key=value\r\nxx\r\nxxxx\r\n5\r\nyyyyy\r\n0\r\n" "Content-Type: application/json\r\n" "\r\n") conn.putrequest("POST", "/upload", skip_host=True) conn.putheader("Host", self.HOST) conn.putheader("Transfer-Encoding", "chunked") conn.putheader("Trailer", "Content-Type") # Note that this is somewhat malformed: # we shouldn't be sending Content-Length. # RFC 2616 says the server should ignore it. conn.putheader("Content-Length", "3") conn.endheaders() conn.send(body) response = conn.getresponse() self.status, self.headers, self.body = webtest.shb(response) self.assertStatus('200 OK') self.assertBody("thanks for '%s'" % ntob('xx\r\nxxxxyyyyy')) # Try a chunked request that exceeds server.max_request_body_size. # Note that the delimiters and trailer are included. body = ntob("3e3\r\n" + ("x" * 995) + "\r\n0\r\n\r\n") conn.putrequest("POST", "/upload", skip_host=True) conn.putheader("Host", self.HOST) conn.putheader("Transfer-Encoding", "chunked") conn.putheader("Content-Type", "text/plain") # Chunked requests don't need a content-length ## conn.putheader("Content-Length", len(body)) conn.endheaders() conn.send(body) response = conn.getresponse() self.status, self.headers, self.body = webtest.shb(response) self.assertStatus(413) conn.close()
def test_No_CRLF(self): self.httpserver.protocol = "HTTP/1.1" self.PROTOCOL = "HTTP/1.1" self.persistent = True conn = self.HTTP_CONN conn.send(ntob('GET /hello HTTP/1.1\n\n')) response = conn.response_class(conn.sock, method="GET") response.begin() self.body = response.read() self.assertBody("HTTP requires CRLF terminators") conn.close() conn.connect() conn.send(ntob('GET /hello HTTP/1.1\r\n\n')) response = conn.response_class(conn.sock, method="GET") response.begin() self.body = response.read() self.assertBody("HTTP requires CRLF terminators") conn.close()
def test_app(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) output = ['Hello, world!\n', 'This is a wsgi app running within CherryPy!\n\n'] keys = list(environ.keys()) keys.sort() for k in keys: output.append('%s: %s\n' % (k, environ[k])) return [ntob(x, 'utf-8') for x in output]
def test_app(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) output = ['Hello, world!\n', 'This is a wsgi app running within Cheroot!\n\n'] keys = list(environ.keys()) keys.sort() for k in keys: output.append('%s: %s\n' % (k,environ[k])) return [ntob(x, 'utf-8') for x in output]
def test_parse_uri_invalid_uri(self): c = self._get_http_connection() c._output(ntob('GET /йопта! HTTP/1.1', 'utf-8')) c._send_output() response = self._get_http_response(c, method='GET') response.begin() assert response.status == HTTP_BAD_REQUEST assert response.fp.read(21) == b'Malformed Request-URI' c.close() for uri in ['hello', url_quote('привіт')]: self.getPage(uri) self.assertStatus(HTTP_BAD_REQUEST)
def test_malformed_header(self): c = self.make_connection() c.putrequest('GET', '/') c.putheader('Content-Type', 'text/plain') # See https://github.com/cherrypy/cherrypy/issues/941 c._output(ntob('Re, 1.2.3.4#015#012')) c.endheaders() response = c.getresponse() self.status = str(response.status) self.assertStatus(400) self.body = response.fp.read(20) self.assertBody('Illegal header line.')
def test_chunked_request_payload_readline(self): if self.scheme == "https": c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT)) else: c = HTTPConnection('%s:%s' % (self.interface(), self.PORT)) c.putrequest("POST", "/echo_lines") c.putheader("Transfer-Encoding", "chunked") c.endheaders() c.send(ntob("13\r\nI am a\nrequest body\r\n0\r\n\r\n")) response = c.getresponse() self.status, self.headers, self.body = webtest.shb(response) c.close() self.assertStatus(200) self.assertBody("I am a\nrequest body")
def test_request_payload_readline(self): if self.scheme == "https": c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT)) else: c = HTTPConnection('%s:%s' % (self.interface(), self.PORT)) c.putrequest("POST", "/echo_lines") body = ntob("I am a\nrequest body") c.putheader("Content-Length", len(body)) c.endheaders() c.send(body) response = c.getresponse() self.status, self.headers, self.body = webtest.shb(response) c.close() self.assertStatus(200) self.assertBody(body)
def test_no_content_length(self): # "The presence of a message-body in a request is signaled by the # inclusion of a Content-Length or Transfer-Encoding header field in # the request's message-headers." # # Send a message with neither header and no body. if self.scheme == "https": c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT)) else: c = HTTPConnection('%s:%s' % (self.interface(), self.PORT)) c.request("POST", "/no_body") response = c.getresponse() self.body = response.fp.read() self.status = str(response.status) self.assertStatus(200) self.assertBody(ntob('Hello world!'))
def test_max_body(self): if self.scheme == "https": c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT)) else: c = HTTPConnection('%s:%s' % (self.interface(), self.PORT)) c.putrequest("POST", "/echo") body = ntob("x" * 1001) c.putheader("Content-Length", len(body)) c.endheaders() c.send(body) response = c.getresponse() self.status, self.headers, self.body = webtest.shb(response) c.close() self.assertStatus(413) self.assertBody("The entity sent with the request exceeds " "the maximum allowed bytes.")
def test_malformed_header(self): if self.scheme == 'https': c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT)) else: c = HTTPConnection('%s:%s' % (self.interface(), self.PORT)) c.putrequest('GET', '/') c.putheader('Content-Type', 'text/plain') # See http://www.cherrypy.org/ticket/941 c._output(ntob('Re, 1.2.3.4#015#012')) c.endheaders() response = c.getresponse() self.status = str(response.status) self.assertStatus(400) self.body = response.fp.read(20) self.assertBody("Illegal header line.")
def test_max_body(self): if self.scheme == "https": c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT)) else: c = HTTPConnection('%s:%s' % (self.interface(), self.PORT)) c.putrequest("POST", "/echo") body = ntob("x" * 1001) c.putheader("Content-Length", len(body)) c.endheaders() c.send(body) response = c.getresponse() self.status, self.headers, self.body = webtest.shb(response) c.close() self.assertStatus(413) self.assertBody( "The entity sent with the request exceeds " "the maximum allowed bytes.")
def test_598(self): remote_data_conn = urlopen('%s://%s:%s/one_megabyte_of_a/' % (self.scheme, self.HOST, self.PORT,)) buf = remote_data_conn.read(512) time.sleep(timeout * 0.6) remaining = (1024 * 1024) - 512 while remaining: data = remote_data_conn.read(remaining) if not data: break else: buf += data remaining -= len(data) self.assertEqual(len(buf), 1024 * 1024) self.assertEqual(buf, ntob('a' * 1024 * 1024)) self.assertEqual(remaining, 0) remote_data_conn.close()
def echo_lines(self, req, resp): f = req.environ['wsgi.input'] output = [] while True: line = f.readline().decode("ISO-8859-1") if not line: break output.append(line) if hasattr(f, 'read_trailer_lines'): for line in f.read_trailer_lines(): k, v = line.split(ntob(":"), 1) k = tonative(k.strip()) v = tonative(v.strip()) resp.headers[k] = v return output
def test_598(self): self.httpserver.protocol = "HTTP/1.1" self.PROTOCOL = "HTTP/1.1" remote_data_conn = urlopen('%s://%s:%s/one_megabyte_of_a' % (self.scheme, self.HOST, self.PORT)) buf = remote_data_conn.read(512) time.sleep(timeout * 0.6) remaining = (1024 * 1024) - 512 while remaining: data = remote_data_conn.read(remaining) if not data: break else: buf += data remaining -= len(data) self.assertEqual(len(buf), 1024 * 1024) self.assertEqual(buf, ntob("a" * 1024 * 1024)) self.assertEqual(remaining, 0) remote_data_conn.close()
def test_HTTP11_Timeout(self): # If we timeout without sending any data, # the server will close the conn with a 408. if cherrypy.server.protocol_version != 'HTTP/1.1': return self.skip() self.PROTOCOL = 'HTTP/1.1' # Connect but send nothing. self.persistent = True conn = self.HTTP_CONN conn.auto_open = False conn.connect() # Wait for our socket timeout time.sleep(timeout * 2) # The request should have returned 408 already. response = conn.response_class(conn.sock, method='GET') response.begin() self.assertEqual(response.status, 408) conn.close() # Connect but send half the headers only. self.persistent = True conn = self.HTTP_CONN conn.auto_open = False conn.connect() conn.send(ntob('GET /hello HTTP/1.1')) conn.send(('Host: %s' % self.HOST).encode('ascii')) # Wait for our socket timeout time.sleep(timeout * 2) # The conn should have already sent 408. response = conn.response_class(conn.sock, method='GET') response.begin() self.assertEqual(response.status, 408) conn.close()
def test_normal_request(self): self.getPage("/hello") self.assertStatus(200) self.assertBody(ntob('Hello world!'))
def test_readall_or_close(self): self.httpserver.protocol = "HTTP/1.1" self.PROTOCOL = "HTTP/1.1" if self.scheme == "https": self.HTTP_CONN = HTTPSConnection else: self.HTTP_CONN = HTTPConnection # Test a max of 0 (the default) and then reset to what it was above. old_max = self.httpserver.max_request_body_size for new_max in (0, old_max): self.httpserver.max_request_body_size = new_max self.persistent = True conn = self.HTTP_CONN # Get a POST page with an error conn.putrequest("POST", "/err_before_read", skip_host=True) conn.putheader("Host", self.HOST) conn.putheader("Content-Type", "text/plain") conn.putheader("Content-Length", "1000") conn.putheader("Expect", "100-continue") conn.endheaders() response = conn.response_class(conn.sock, method="POST") # ...assert and then skip the 100 response version, status, reason = response._read_status() self.assertEqual(status, 100) while True: skip = response.fp.readline().strip() if not skip: break # ...send the body conn.send(ntob("x" * 1000)) # ...get the final response response.begin() self.status, self.headers, self.body = webtest.shb(response) self.assertStatus(500) # Now try a working page with an Expect header... conn._output(ntob('POST /upload HTTP/1.1')) conn._output(ntob("Host: %s" % self.HOST, 'ascii')) conn._output(ntob("Content-Type: text/plain")) conn._output(ntob("Content-Length: 17")) conn._output(ntob("Expect: 100-continue")) conn._send_output() response = conn.response_class(conn.sock, method="POST") # ...assert and then skip the 100 response version, status, reason = response._read_status() self.assertEqual(status, 100) while True: skip = response.fp.readline().strip() if not skip: break # ...send the body body = ntob("I am a small file") conn.send(body) # ...get the final response response.begin() self.status, self.headers, self.body = webtest.shb(response) self.assertStatus(200) self.assertBody("thanks for '%s'" % body) conn.close()
def test_HTTP11_Timeout_after_request(self): # If we timeout after at least one request has succeeded, # the server will close the conn without 408. self.httpserver.protocol = "HTTP/1.1" self.PROTOCOL = "HTTP/1.1" # Make an initial request self.persistent = True conn = self.HTTP_CONN conn.putrequest("GET", "/timeout?t=%s" % timeout, skip_host=True) conn.putheader("Host", self.HOST) conn.endheaders() response = conn.response_class(conn.sock, method="GET") response.begin() self.assertEqual(response.status, 200) self.body = response.read() self.assertBody(str(timeout)) # Make a second request on the same socket conn._output(ntob('GET /hello HTTP/1.1')) conn._output(ntob("Host: %s" % self.HOST, 'ascii')) conn._send_output() response = conn.response_class(conn.sock, method="GET") response.begin() self.assertEqual(response.status, 200) self.body = response.read() self.assertBody("Hello, world!") # Wait for our socket timeout time.sleep(timeout * 2) # Make another request on the same socket, which should error conn._output(ntob('GET /hello HTTP/1.1')) conn._output(ntob("Host: %s" % self.HOST, 'ascii')) conn._send_output() response = conn.response_class(conn.sock, method="GET") try: response.begin() except: if not isinstance(sys.exc_info()[1], (socket.error, BadStatusLine)): self.fail("Writing to timed out socket didn't fail" " as it should have: %s" % sys.exc_info()[1]) else: if response.status != 408: self.fail("Writing to timed out socket didn't fail" " as it should have: %s" % response.read()) conn.close() # Make another request on a new socket, which should work self.persistent = True conn = self.HTTP_CONN conn.putrequest("GET", "/pov", skip_host=True) conn.putheader("Host", self.HOST) conn.endheaders() response = conn.response_class(conn.sock, method="GET") response.begin() self.assertEqual(response.status, 200) self.body = response.read() self.assertBody(pov) # Make another request on the same socket, # but timeout on the headers conn.send(ntob('GET /hello HTTP/1.1')) # Wait for our socket timeout time.sleep(timeout * 2) response = conn.response_class(conn.sock, method="GET") try: response.begin() except: if not isinstance(sys.exc_info()[1], (socket.error, BadStatusLine)): self.fail("Writing to timed out socket didn't fail" " as it should have: %s" % sys.exc_info()[1]) else: self.fail("Writing to timed out socket didn't fail" " as it should have: %s" % response.read()) conn.close() # Retry the request on a new connection, which should work self.persistent = True conn = self.HTTP_CONN conn.putrequest("GET", "/pov", skip_host=True) conn.putheader("Host", self.HOST) conn.endheaders() response = conn.response_class(conn.sock, method="GET") response.begin() self.assertEqual(response.status, 200) self.body = response.read() self.assertBody(pov) conn.close()