|
15 | 15 | from hyper.contrib import HTTP20Adapter
|
16 | 16 | from hyper.packages.hyperframe.frame import (
|
17 | 17 | Frame, SettingsFrame, WindowUpdateFrame, DataFrame, HeadersFrame,
|
18 |
| - GoAwayFrame, |
| 18 | + GoAwayFrame, RstStreamFrame |
19 | 19 | )
|
20 | 20 | from hyper.packages.hpack.hpack import Encoder
|
21 | 21 | from hyper.packages.hpack.huffman import HuffmanEncoder
|
22 | 22 | from hyper.packages.hpack.huffman_constants import (
|
23 | 23 | REQUEST_CODES, REQUEST_CODES_LENGTH
|
24 | 24 | )
|
25 |
| -from hyper.http20.exceptions import ConnectionError |
| 25 | +from hyper.http20.exceptions import ConnectionError, StreamResetError |
26 | 26 | from server import SocketLevelTest
|
27 | 27 |
|
28 | 28 | # Turn off certificate verification for the tests.
|
@@ -506,6 +506,101 @@ def socket_handler(listener):
|
506 | 506 |
|
507 | 507 | self.tear_down()
|
508 | 508 |
|
| 509 | + def test_resetting_stream_with_frames_in_flight(self): |
| 510 | + """ |
| 511 | + Hyper emits only one RST_STREAM frame, despite the other frames in |
| 512 | + flight. |
| 513 | + """ |
| 514 | + self.set_up() |
| 515 | + |
| 516 | + recv_event = threading.Event() |
| 517 | + |
| 518 | + def socket_handler(listener): |
| 519 | + sock = listener.accept()[0] |
| 520 | + |
| 521 | + # We get two messages for the connection open and then a HEADERS |
| 522 | + # frame. |
| 523 | + receive_preamble(sock) |
| 524 | + sock.recv(65535) |
| 525 | + |
| 526 | + # Now, send the headers for the response. This response has no |
| 527 | + # body. |
| 528 | + f = build_headers_frame( |
| 529 | + [(':status', '204'), ('content-length', '0')] |
| 530 | + ) |
| 531 | + f.flags.add('END_STREAM') |
| 532 | + f.stream_id = 1 |
| 533 | + sock.send(f.serialize()) |
| 534 | + |
| 535 | + # Wait for the message from the main thread. |
| 536 | + recv_event.wait() |
| 537 | + sock.close() |
| 538 | + |
| 539 | + self._start_server(socket_handler) |
| 540 | + conn = self.get_connection() |
| 541 | + stream_id = conn.request('GET', '/') |
| 542 | + |
| 543 | + # Now, trigger the RST_STREAM frame by closing the stream. |
| 544 | + conn._send_rst_frame(stream_id, 0) |
| 545 | + |
| 546 | + # Now, eat the Headers frame. This should not cause an exception. |
| 547 | + conn._recv_cb() |
| 548 | + |
| 549 | + # However, attempting to get the response should. |
| 550 | + with pytest.raises(KeyError): |
| 551 | + conn.get_response(stream_id) |
| 552 | + |
| 553 | + # Awesome, we're done now. |
| 554 | + recv_event.set() |
| 555 | + |
| 556 | + self.tear_down() |
| 557 | + |
| 558 | + def test_stream_can_be_reset_multiple_times(self): |
| 559 | + """ |
| 560 | + Confirm that hyper gracefully handles receiving multiple RST_STREAM |
| 561 | + frames. |
| 562 | + """ |
| 563 | + self.set_up() |
| 564 | + |
| 565 | + recv_event = threading.Event() |
| 566 | + |
| 567 | + def socket_handler(listener): |
| 568 | + sock = listener.accept()[0] |
| 569 | + |
| 570 | + # We get two messages for the connection open and then a HEADERS |
| 571 | + # frame. |
| 572 | + receive_preamble(sock) |
| 573 | + sock.recv(65535) |
| 574 | + |
| 575 | + # Now, send two RST_STREAM frames. |
| 576 | + for _ in range(0, 2): |
| 577 | + f = RstStreamFrame(1) |
| 578 | + sock.send(f.serialize()) |
| 579 | + |
| 580 | + # Wait for the message from the main thread. |
| 581 | + recv_event.wait() |
| 582 | + sock.close() |
| 583 | + |
| 584 | + self._start_server(socket_handler) |
| 585 | + conn = self.get_connection() |
| 586 | + conn.request('GET', '/') |
| 587 | + |
| 588 | + # Now, eat the RstStream frames. The first one throws a |
| 589 | + # StreamResetError. |
| 590 | + with pytest.raises(StreamResetError): |
| 591 | + conn._consume_single_frame() |
| 592 | + |
| 593 | + # The next should throw no exception. |
| 594 | + conn._consume_single_frame() |
| 595 | + |
| 596 | + assert conn.reset_streams == set([1]) |
| 597 | + |
| 598 | + # Awesome, we're done now. |
| 599 | + recv_event.set() |
| 600 | + |
| 601 | + self.tear_down() |
| 602 | + |
| 603 | + |
509 | 604 | class TestRequestsAdapter(SocketLevelTest):
|
510 | 605 | # This uses HTTP/2.
|
511 | 606 | h2 = True
|
|
0 commit comments