Skip to content

Commit

Permalink
Ensure that MAX_FRAME_SIZE is respected when sending responses to cli…
Browse files Browse the repository at this point in the history
…ents
  • Loading branch information
kahuang committed Feb 25, 2019
1 parent 3271f02 commit c047c49
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 7 deletions.
1 change: 0 additions & 1 deletion tornado_http2/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ def __init__(self, stream, is_client, params=None, context=None):
self._initial_settings_written = Future()
self._serving_future = None
self._settings = {}

self.streams = {}
self.next_stream_id = 1 if is_client else 2
self.hpack_decoder = HpackDecoder(
Expand Down
4 changes: 3 additions & 1 deletion tornado_http2/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,9 @@ def _write_chunk(self, chunk, callback=None):
if chunk:
yield self.write_lock.acquire()
while chunk:
allowance = yield self.window.consume(len(chunk))
bytes_to_write = min(len(chunk), self.conn.setting(
constants.Setting.MAX_FRAME_SIZE))
allowance = yield self.window.consume(bytes_to_write)

yield self.conn._write_frame(
Frame(constants.FrameType.DATA, 0,
Expand Down
3 changes: 2 additions & 1 deletion tornado_http2/test/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def benchmark(version):

def print_result(label, elapsed):
print('HTTP/%s: %d requests in %0.3fs: %f QPS' % (label, options.n, elapsed,
options.n / elapsed))
options.n / elapsed))


@gen.coroutine
Expand All @@ -58,5 +58,6 @@ def main():
elapsed = yield benchmark(int(version))
print_result(version, elapsed)


if __name__ == '__main__':
IOLoop.current().run_sync(main)
8 changes: 7 additions & 1 deletion tornado_http2/test/encoding_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from tornado_http2.encoding import BitEncoder, BitDecoder, EODError


class TestData(object):
def __init__(self, *args):
self.args = args
Expand All @@ -14,20 +15,23 @@ def decode(self, test, decoder):
for arg in self.args:
test.assertEqual(self.decode_value(decoder), arg)


class Bits(TestData):
def encode_value(self, encoder, arg):
encoder.write_bit(arg)

def decode_value(self, decoder):
return decoder.read_bit()


class HpackInt(TestData):
def encode_value(self, encoder, arg):
encoder.write_hpack_int(arg)

def decode_value(self, decoder):
return decoder.read_hpack_int()


class HuffChar(TestData):
def __init__(self, data):
# convert strings to a sequence of bytes
Expand All @@ -39,6 +43,7 @@ def encode_value(self, encoder, arg):
def decode_value(self, decoder):
return decoder.read_huffman_char(None)


test_data = [
('1-bit', [Bits(1)], [0b10000000], False),
('5-bits', [Bits(1, 0, 1, 1, 0)], [0b10110000], False),
Expand Down Expand Up @@ -78,7 +83,8 @@ def decode_value(self, decoder):
# Individual huffman-encoded characters
('huff1', [HuffChar(b'a')], [0b00011000], False),
('huff2', [HuffChar(b'Hi')], [0b11000110, 0b01100000], False),
]
]


class BitEncodingTest(unittest.TestCase):
def test_bit_encoder(self):
Expand Down
1 change: 1 addition & 0 deletions tornado_http2/test/runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ def configure_httpclient():
logging.getLogger("tornado.access").setLevel(logging.CRITICAL)
tornado.testing.main()


if __name__ == '__main__':
main()
21 changes: 18 additions & 3 deletions tornado_http2/test/server_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,45 @@ def get(self):

class ServerTest(AsyncHTTP2TestCase):
def get_app(self):
class LargeResponseHandler(RequestHandler):
class LargeChunkedResponseHandler(RequestHandler):
@gen.coroutine
def get(self):
for i in range(200):
self.write(b'a' * 1024)
yield self.flush()

class ExtraLargeResponseHandler(RequestHandler):
"""Send the data all at once"""

def get(self):
self.write(b'a' * 1024 * 200)
self.flush()

return Application([
('/hello', HelloHandler),
('/large', LargeResponseHandler),
('/large', LargeChunkedResponseHandler),
('/extralarge', ExtraLargeResponseHandler),
])

def test_hello(self):
resp = self.fetch('/hello')
resp.rethrow()
self.assertEqual(resp.body, b'Hello HTTP/2.0')

def test_large_response(self):
def test_large_response_chunked(self):
# This mainly tests that WINDOW_UPDATE frames are sent as needed,
# since this response exceeds the default 64KB window.
resp = self.fetch('/large')
resp.rethrow()
self.assertEqual(len(resp.body), 200 * 1024)

def test_large_response(self):
# This mainly tests that the server will respect the MAX_FRAME_SIZE
# when sending data on the connection.
resp = self.fetch('/extralarge')
resp.rethrow()
self.assertEqual(len(resp.body), 200 * 1024)


class HTTPSTest(AsyncHTTP2TestCase):
def get_app(self):
Expand Down

0 comments on commit c047c49

Please sign in to comment.