# instead of request.connection.write_headers.
def handle_request(request):
self.http1 = request.version.startswith("HTTP/1.")
+ if not self.http1:
+ # This test will be skipped if we're using HTTP/2,
+ # so just close it out cleanly using the modern interface.
+ request.connection.write_headers(
+ ResponseStartLine('', 200, 'OK'),
+ HTTPHeaders())
+ request.connection.finish()
+ return
message = b"Hello world"
request.write(utf8("HTTP/1.1 200 OK\r\n"
"Content-Length: %d\r\n\r\n" % len(message)))
from tornado import gen
from tornado.httpclient import AsyncHTTPClient
-from tornado.httputil import HTTPHeaders
+from tornado.httputil import HTTPHeaders, ResponseStartLine
from tornado.ioloop import IOLoop
from tornado.log import gen_log
from tornado.netutil import Resolver, bind_sockets
class NoContentLengthHandler(RequestHandler):
- @gen.coroutine
+ @asynchronous
def get(self):
- # Emulate the old HTTP/1.0 behavior of returning a body with no
- # content-length. Tornado handles content-length at the framework
- # level so we have to go around it.
- stream = self.request.connection.stream
- yield stream.write(b"HTTP/1.0 200 OK\r\n\r\n"
- b"hello")
- stream.close()
+ if self.request.version.startswith('HTTP/1'):
+ # Emulate the old HTTP/1.0 behavior of returning a body with no
+ # content-length. Tornado handles content-length at the framework
+ # level so we have to go around it.
+ stream = self.request.connection.detach()
+ stream.write(b"HTTP/1.0 200 OK\r\n\r\n"
+ b"hello")
+ stream.close()
+ else:
+ self.finish('HTTP/1 required')
class EchoPostHandler(RequestHandler):
def test_no_content_length(self):
response = self.fetch("/no_content_length")
- self.assertEquals(b"hello", response.body)
+ if response.body == b"HTTP/1 required":
+ self.skipTest("requires HTTP/1.x")
+ else:
+ self.assertEquals(b"hello", response.body)
def sync_body_producer(self, write):
write(b'1234')
class HTTP100ContinueTestCase(AsyncHTTPTestCase):
def respond_100(self, request):
self.http1 = request.version.startswith('HTTP/1.')
+ if not self.http1:
+ request.connection.write_headers(ResponseStartLine('', 200, 'OK'),
+ HTTPHeaders())
+ request.connection.finish()
+ return
self.request = request
self.request.connection.stream.write(
b"HTTP/1.1 100 CONTINUE\r\n\r\n",
class HTTP204NoContentTestCase(AsyncHTTPTestCase):
def respond_204(self, request):
self.http1 = request.version.startswith('HTTP/1.')
+ if not self.http1:
+ # Close the request cleanly in HTTP/2; it will be skipped anyway.
+ request.connection.write_headers(ResponseStartLine('', 200, 'OK'),
+ HTTPHeaders())
+ request.connection.finish()
+ return
# A 204 response never has a body, even if doesn't have a content-length
# (which would otherwise mean read-until-close). Tornado always
# sends a content-length, so we simulate here a server that sends
#
# Tests of a 204 response with a Content-Length header are included
# in SimpleHTTPClientTestMixin.
- request.connection.stream.write(
+ stream = request.connection.detach()
+ stream.write(
b"HTTP/1.1 204 No content\r\n\r\n")
+ stream.close()
def get_app(self):
return self.respond_204
@gen.coroutine
def prepare(self):
- with self.in_method('prepare'):
- yield gen.Task(IOLoop.current().add_callback)
+ # Note that asynchronous prepare() does not block data_received,
+ # so we don't use in_method here.
+ self.methods.append('prepare')
+ yield gen.Task(IOLoop.current().add_callback)
@gen.coroutine
def data_received(self, data):
# When the content-length is too high, the connection is simply
# closed without completing the response. An error is logged on
# the server.
- with ExpectLog(app_log, "Uncaught exception"):
+ with ExpectLog(app_log, "(Uncaught exception|Exception in callback)"):
with ExpectLog(gen_log,
"(Cannot send error response after headers written"
"|Failed to flush partial response)"):
# When the content-length is too low, the connection is closed
# without writing the last chunk, so the client never sees the request
# complete (which would be a framing error).
- with ExpectLog(app_log, "Uncaught exception"):
+ with ExpectLog(app_log, "(Uncaught exception|Exception in callback)"):
with ExpectLog(gen_log,
"(Cannot send error response after headers written"
"|Failed to flush partial response)"):
class ClientCloseTest(SimpleHandlerTestCase):
class Handler(RequestHandler):
def get(self):
- # Simulate a connection closed by the client during
- # request processing. The client will see an error, but the
- # server should respond gracefully (without logging errors
- # because we were unable to write out as many bytes as
- # Content-Length said we would)
- self.request.connection.stream.close()
- self.write('hello')
+ if self.request.version.startswith('HTTP/1'):
+ # Simulate a connection closed by the client during
+ # request processing. The client will see an error, but the
+ # server should respond gracefully (without logging errors
+ # because we were unable to write out as many bytes as
+ # Content-Length said we would)
+ self.request.connection.stream.close()
+ self.write('hello')
+ else:
+ # TODO: add a HTTP2-compatible version of this test.
+ self.write('requires HTTP/1.x')
def test_client_close(self):
response = self.fetch('/')
+ if response.body == b'requires HTTP/1.x':
+ self.skipTest('requires HTTP/1.x')
self.assertEqual(response.code, 599)