|
import pytest |
|
|
|
from .._events import ( |
|
ConnectionClosed, |
|
Data, |
|
EndOfMessage, |
|
Event, |
|
InformationalResponse, |
|
Request, |
|
Response, |
|
) |
|
from .._state import ( |
|
_SWITCH_CONNECT, |
|
_SWITCH_UPGRADE, |
|
CLIENT, |
|
CLOSED, |
|
ConnectionState, |
|
DONE, |
|
IDLE, |
|
MIGHT_SWITCH_PROTOCOL, |
|
MUST_CLOSE, |
|
SEND_BODY, |
|
SEND_RESPONSE, |
|
SERVER, |
|
SWITCHED_PROTOCOL, |
|
) |
|
from .._util import LocalProtocolError |
|
|
|
|
|
def test_ConnectionState() -> None: |
|
cs = ConnectionState() |
|
|
|
|
|
|
|
assert cs.states == {CLIENT: IDLE, SERVER: IDLE} |
|
|
|
cs.process_event(CLIENT, Request) |
|
|
|
assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} |
|
|
|
|
|
with pytest.raises(LocalProtocolError): |
|
cs.process_event(CLIENT, Request) |
|
assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} |
|
|
|
cs.process_event(SERVER, InformationalResponse) |
|
assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} |
|
|
|
cs.process_event(SERVER, Response) |
|
assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_BODY} |
|
|
|
cs.process_event(CLIENT, EndOfMessage) |
|
cs.process_event(SERVER, EndOfMessage) |
|
assert cs.states == {CLIENT: DONE, SERVER: DONE} |
|
|
|
|
|
|
|
cs.process_event(SERVER, ConnectionClosed) |
|
assert cs.states == {CLIENT: MUST_CLOSE, SERVER: CLOSED} |
|
|
|
|
|
def test_ConnectionState_keep_alive() -> None: |
|
|
|
cs = ConnectionState() |
|
cs.process_event(CLIENT, Request) |
|
cs.process_keep_alive_disabled() |
|
cs.process_event(CLIENT, EndOfMessage) |
|
assert cs.states == {CLIENT: MUST_CLOSE, SERVER: SEND_RESPONSE} |
|
|
|
cs.process_event(SERVER, Response) |
|
cs.process_event(SERVER, EndOfMessage) |
|
assert cs.states == {CLIENT: MUST_CLOSE, SERVER: MUST_CLOSE} |
|
|
|
|
|
def test_ConnectionState_keep_alive_in_DONE() -> None: |
|
|
|
|
|
|
|
cs = ConnectionState() |
|
cs.process_event(CLIENT, Request) |
|
cs.process_event(CLIENT, EndOfMessage) |
|
assert cs.states[CLIENT] is DONE |
|
cs.process_keep_alive_disabled() |
|
assert cs.states[CLIENT] is MUST_CLOSE |
|
|
|
|
|
def test_ConnectionState_switch_denied() -> None: |
|
for switch_type in (_SWITCH_CONNECT, _SWITCH_UPGRADE): |
|
for deny_early in (True, False): |
|
cs = ConnectionState() |
|
cs.process_client_switch_proposal(switch_type) |
|
cs.process_event(CLIENT, Request) |
|
cs.process_event(CLIENT, Data) |
|
assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} |
|
|
|
assert switch_type in cs.pending_switch_proposals |
|
|
|
if deny_early: |
|
|
|
cs.process_event(SERVER, Response) |
|
assert not cs.pending_switch_proposals |
|
|
|
cs.process_event(CLIENT, EndOfMessage) |
|
|
|
if deny_early: |
|
assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY} |
|
else: |
|
assert cs.states == { |
|
CLIENT: MIGHT_SWITCH_PROTOCOL, |
|
SERVER: SEND_RESPONSE, |
|
} |
|
|
|
cs.process_event(SERVER, InformationalResponse) |
|
assert cs.states == { |
|
CLIENT: MIGHT_SWITCH_PROTOCOL, |
|
SERVER: SEND_RESPONSE, |
|
} |
|
|
|
cs.process_event(SERVER, Response) |
|
assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY} |
|
assert not cs.pending_switch_proposals |
|
|
|
|
|
_response_type_for_switch = { |
|
_SWITCH_UPGRADE: InformationalResponse, |
|
_SWITCH_CONNECT: Response, |
|
None: Response, |
|
} |
|
|
|
|
|
def test_ConnectionState_protocol_switch_accepted() -> None: |
|
for switch_event in [_SWITCH_UPGRADE, _SWITCH_CONNECT]: |
|
cs = ConnectionState() |
|
cs.process_client_switch_proposal(switch_event) |
|
cs.process_event(CLIENT, Request) |
|
cs.process_event(CLIENT, Data) |
|
assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} |
|
|
|
cs.process_event(CLIENT, EndOfMessage) |
|
assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE} |
|
|
|
cs.process_event(SERVER, InformationalResponse) |
|
assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE} |
|
|
|
cs.process_event(SERVER, _response_type_for_switch[switch_event], switch_event) |
|
assert cs.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL} |
|
|
|
|
|
def test_ConnectionState_double_protocol_switch() -> None: |
|
|
|
|
|
for server_switch in [None, _SWITCH_UPGRADE, _SWITCH_CONNECT]: |
|
cs = ConnectionState() |
|
cs.process_client_switch_proposal(_SWITCH_UPGRADE) |
|
cs.process_client_switch_proposal(_SWITCH_CONNECT) |
|
cs.process_event(CLIENT, Request) |
|
cs.process_event(CLIENT, EndOfMessage) |
|
assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE} |
|
cs.process_event( |
|
SERVER, _response_type_for_switch[server_switch], server_switch |
|
) |
|
if server_switch is None: |
|
assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY} |
|
else: |
|
assert cs.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL} |
|
|
|
|
|
def test_ConnectionState_inconsistent_protocol_switch() -> None: |
|
for client_switches, server_switch in [ |
|
([], _SWITCH_CONNECT), |
|
([], _SWITCH_UPGRADE), |
|
([_SWITCH_UPGRADE], _SWITCH_CONNECT), |
|
([_SWITCH_CONNECT], _SWITCH_UPGRADE), |
|
]: |
|
cs = ConnectionState() |
|
for client_switch in client_switches: |
|
cs.process_client_switch_proposal(client_switch) |
|
cs.process_event(CLIENT, Request) |
|
with pytest.raises(LocalProtocolError): |
|
cs.process_event(SERVER, Response, server_switch) |
|
|
|
|
|
def test_ConnectionState_keepalive_protocol_switch_interaction() -> None: |
|
|
|
cs = ConnectionState() |
|
cs.process_client_switch_proposal(_SWITCH_UPGRADE) |
|
cs.process_event(CLIENT, Request) |
|
cs.process_keep_alive_disabled() |
|
cs.process_event(CLIENT, Data) |
|
assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} |
|
|
|
|
|
cs.process_event(CLIENT, EndOfMessage) |
|
assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE} |
|
|
|
|
|
cs.process_event(SERVER, Response) |
|
assert cs.states == {CLIENT: MUST_CLOSE, SERVER: SEND_BODY} |
|
|
|
|
|
def test_ConnectionState_reuse() -> None: |
|
cs = ConnectionState() |
|
|
|
with pytest.raises(LocalProtocolError): |
|
cs.start_next_cycle() |
|
|
|
cs.process_event(CLIENT, Request) |
|
cs.process_event(CLIENT, EndOfMessage) |
|
|
|
with pytest.raises(LocalProtocolError): |
|
cs.start_next_cycle() |
|
|
|
cs.process_event(SERVER, Response) |
|
cs.process_event(SERVER, EndOfMessage) |
|
|
|
cs.start_next_cycle() |
|
assert cs.states == {CLIENT: IDLE, SERVER: IDLE} |
|
|
|
|
|
|
|
cs.process_event(CLIENT, Request) |
|
cs.process_keep_alive_disabled() |
|
cs.process_event(CLIENT, EndOfMessage) |
|
cs.process_event(SERVER, Response) |
|
cs.process_event(SERVER, EndOfMessage) |
|
|
|
with pytest.raises(LocalProtocolError): |
|
cs.start_next_cycle() |
|
|
|
|
|
|
|
cs = ConnectionState() |
|
cs.process_event(CLIENT, Request) |
|
cs.process_event(CLIENT, EndOfMessage) |
|
cs.process_event(CLIENT, ConnectionClosed) |
|
cs.process_event(SERVER, Response) |
|
cs.process_event(SERVER, EndOfMessage) |
|
|
|
with pytest.raises(LocalProtocolError): |
|
cs.start_next_cycle() |
|
|
|
|
|
|
|
cs = ConnectionState() |
|
cs.process_client_switch_proposal(_SWITCH_UPGRADE) |
|
cs.process_event(CLIENT, Request) |
|
cs.process_event(CLIENT, EndOfMessage) |
|
cs.process_event(SERVER, InformationalResponse, _SWITCH_UPGRADE) |
|
|
|
with pytest.raises(LocalProtocolError): |
|
cs.start_next_cycle() |
|
|
|
|
|
|
|
cs = ConnectionState() |
|
cs.process_client_switch_proposal(_SWITCH_UPGRADE) |
|
cs.process_event(CLIENT, Request) |
|
cs.process_event(CLIENT, EndOfMessage) |
|
cs.process_event(SERVER, Response) |
|
cs.process_event(SERVER, EndOfMessage) |
|
|
|
cs.start_next_cycle() |
|
assert cs.states == {CLIENT: IDLE, SERVER: IDLE} |
|
|
|
|
|
def test_server_request_is_illegal() -> None: |
|
|
|
|
|
cs = ConnectionState() |
|
with pytest.raises(LocalProtocolError): |
|
cs.process_event(SERVER, Request) |
|
|