Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

246 lignes
8.6 KiB

  1. import pytest
  2. from .._util import LocalProtocolError
  3. from .._events import *
  4. from .._state import *
  5. from .._state import ConnectionState, _SWITCH_UPGRADE, _SWITCH_CONNECT
  6. def test_ConnectionState():
  7. cs = ConnectionState()
  8. # Basic event-triggered transitions
  9. assert cs.states == {CLIENT: IDLE, SERVER: IDLE}
  10. cs.process_event(CLIENT, Request)
  11. # The SERVER-Request special case:
  12. assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
  13. # Illegal transitions raise an error and nothing happens
  14. with pytest.raises(LocalProtocolError):
  15. cs.process_event(CLIENT, Request)
  16. assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
  17. cs.process_event(SERVER, InformationalResponse)
  18. assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
  19. cs.process_event(SERVER, Response)
  20. assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_BODY}
  21. cs.process_event(CLIENT, EndOfMessage)
  22. cs.process_event(SERVER, EndOfMessage)
  23. assert cs.states == {CLIENT: DONE, SERVER: DONE}
  24. # State-triggered transition
  25. cs.process_event(SERVER, ConnectionClosed)
  26. assert cs.states == {CLIENT: MUST_CLOSE, SERVER: CLOSED}
  27. def test_ConnectionState_keep_alive():
  28. # keep_alive = False
  29. cs = ConnectionState()
  30. cs.process_event(CLIENT, Request)
  31. cs.process_keep_alive_disabled()
  32. cs.process_event(CLIENT, EndOfMessage)
  33. assert cs.states == {CLIENT: MUST_CLOSE, SERVER: SEND_RESPONSE}
  34. cs.process_event(SERVER, Response)
  35. cs.process_event(SERVER, EndOfMessage)
  36. assert cs.states == {CLIENT: MUST_CLOSE, SERVER: MUST_CLOSE}
  37. def test_ConnectionState_keep_alive_in_DONE():
  38. # Check that if keep_alive is disabled when the CLIENT is already in DONE,
  39. # then this is sufficient to immediately trigger the DONE -> MUST_CLOSE
  40. # transition
  41. cs = ConnectionState()
  42. cs.process_event(CLIENT, Request)
  43. cs.process_event(CLIENT, EndOfMessage)
  44. assert cs.states[CLIENT] is DONE
  45. cs.process_keep_alive_disabled()
  46. assert cs.states[CLIENT] is MUST_CLOSE
  47. def test_ConnectionState_switch_denied():
  48. for switch_type in (_SWITCH_CONNECT, _SWITCH_UPGRADE):
  49. for deny_early in (True, False):
  50. cs = ConnectionState()
  51. cs.process_client_switch_proposal(switch_type)
  52. cs.process_event(CLIENT, Request)
  53. cs.process_event(CLIENT, Data)
  54. assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
  55. assert switch_type in cs.pending_switch_proposals
  56. if deny_early:
  57. # before client reaches DONE
  58. cs.process_event(SERVER, Response)
  59. assert not cs.pending_switch_proposals
  60. cs.process_event(CLIENT, EndOfMessage)
  61. if deny_early:
  62. assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY}
  63. else:
  64. assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL,
  65. SERVER: SEND_RESPONSE}
  66. cs.process_event(SERVER, InformationalResponse)
  67. assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL,
  68. SERVER: SEND_RESPONSE}
  69. cs.process_event(SERVER, Response)
  70. assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY}
  71. assert not cs.pending_switch_proposals
  72. _response_type_for_switch = {
  73. _SWITCH_UPGRADE: InformationalResponse,
  74. _SWITCH_CONNECT: Response,
  75. None: Response,
  76. }
  77. def test_ConnectionState_protocol_switch_accepted():
  78. for switch_event in [_SWITCH_UPGRADE, _SWITCH_CONNECT]:
  79. cs = ConnectionState()
  80. cs.process_client_switch_proposal(switch_event)
  81. cs.process_event(CLIENT, Request)
  82. cs.process_event(CLIENT, Data)
  83. assert cs.states == {CLIENT: SEND_BODY,
  84. SERVER: SEND_RESPONSE}
  85. cs.process_event(CLIENT, EndOfMessage)
  86. assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL,
  87. SERVER: SEND_RESPONSE}
  88. cs.process_event(SERVER, InformationalResponse)
  89. assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL,
  90. SERVER: SEND_RESPONSE}
  91. cs.process_event(SERVER,
  92. _response_type_for_switch[switch_event],
  93. switch_event)
  94. assert cs.states == {CLIENT: SWITCHED_PROTOCOL,
  95. SERVER: SWITCHED_PROTOCOL}
  96. def test_ConnectionState_double_protocol_switch():
  97. # CONNECT + Upgrade is legal! Very silly, but legal. So we support
  98. # it. Because sometimes doing the silly thing is easier than not.
  99. for server_switch in [None, _SWITCH_UPGRADE, _SWITCH_CONNECT]:
  100. cs = ConnectionState()
  101. cs.process_client_switch_proposal(_SWITCH_UPGRADE)
  102. cs.process_client_switch_proposal(_SWITCH_CONNECT)
  103. cs.process_event(CLIENT, Request)
  104. cs.process_event(CLIENT, EndOfMessage)
  105. assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL,
  106. SERVER: SEND_RESPONSE}
  107. cs.process_event(SERVER,
  108. _response_type_for_switch[server_switch],
  109. server_switch)
  110. if server_switch is None:
  111. assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY}
  112. else:
  113. assert cs.states == {CLIENT: SWITCHED_PROTOCOL,
  114. SERVER: SWITCHED_PROTOCOL}
  115. def test_ConnectionState_inconsistent_protocol_switch():
  116. for client_switches, server_switch in [
  117. ([], _SWITCH_CONNECT),
  118. ([], _SWITCH_UPGRADE),
  119. ([_SWITCH_UPGRADE], _SWITCH_CONNECT),
  120. ([_SWITCH_CONNECT], _SWITCH_UPGRADE),
  121. ]:
  122. cs = ConnectionState()
  123. for client_switch in client_switches:
  124. cs.process_client_switch_proposal(client_switch)
  125. cs.process_event(CLIENT, Request)
  126. with pytest.raises(LocalProtocolError):
  127. cs.process_event(SERVER, Response, server_switch)
  128. def test_ConnectionState_keepalive_protocol_switch_interaction():
  129. # keep_alive=False + pending_switch_proposals
  130. cs = ConnectionState()
  131. cs.process_client_switch_proposal(_SWITCH_UPGRADE)
  132. cs.process_event(CLIENT, Request)
  133. cs.process_keep_alive_disabled()
  134. cs.process_event(CLIENT, Data)
  135. assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
  136. # the protocol switch "wins"
  137. cs.process_event(CLIENT, EndOfMessage)
  138. assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE}
  139. # but when the server denies the request, keep_alive comes back into play
  140. cs.process_event(SERVER, Response)
  141. assert cs.states == {CLIENT: MUST_CLOSE, SERVER: SEND_BODY}
  142. def test_ConnectionState_reuse():
  143. cs = ConnectionState()
  144. with pytest.raises(LocalProtocolError):
  145. cs.start_next_cycle()
  146. cs.process_event(CLIENT, Request)
  147. cs.process_event(CLIENT, EndOfMessage)
  148. with pytest.raises(LocalProtocolError):
  149. cs.start_next_cycle()
  150. cs.process_event(SERVER, Response)
  151. cs.process_event(SERVER, EndOfMessage)
  152. cs.start_next_cycle()
  153. assert cs.states == {CLIENT: IDLE, SERVER: IDLE}
  154. # No keepalive
  155. cs.process_event(CLIENT, Request)
  156. cs.process_keep_alive_disabled()
  157. cs.process_event(CLIENT, EndOfMessage)
  158. cs.process_event(SERVER, Response)
  159. cs.process_event(SERVER, EndOfMessage)
  160. with pytest.raises(LocalProtocolError):
  161. cs.start_next_cycle()
  162. # One side closed
  163. cs = ConnectionState()
  164. cs.process_event(CLIENT, Request)
  165. cs.process_event(CLIENT, EndOfMessage)
  166. cs.process_event(CLIENT, ConnectionClosed)
  167. cs.process_event(SERVER, Response)
  168. cs.process_event(SERVER, EndOfMessage)
  169. with pytest.raises(LocalProtocolError):
  170. cs.start_next_cycle()
  171. # Succesful protocol switch
  172. cs = ConnectionState()
  173. cs.process_client_switch_proposal(_SWITCH_UPGRADE)
  174. cs.process_event(CLIENT, Request)
  175. cs.process_event(CLIENT, EndOfMessage)
  176. cs.process_event(SERVER, InformationalResponse, _SWITCH_UPGRADE)
  177. with pytest.raises(LocalProtocolError):
  178. cs.start_next_cycle()
  179. # Failed protocol switch
  180. cs = ConnectionState()
  181. cs.process_client_switch_proposal(_SWITCH_UPGRADE)
  182. cs.process_event(CLIENT, Request)
  183. cs.process_event(CLIENT, EndOfMessage)
  184. cs.process_event(SERVER, Response)
  185. cs.process_event(SERVER, EndOfMessage)
  186. cs.start_next_cycle()
  187. assert cs.states == {CLIENT: IDLE, SERVER: IDLE}
  188. def test_server_request_is_illegal():
  189. # There used to be a bug in how we handled the Request special case that
  190. # made this allowed...
  191. cs = ConnectionState()
  192. with pytest.raises(LocalProtocolError):
  193. cs.process_event(SERVER, Request)