http_client.xml 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <class name="HTTPClient">
  2. <constructor>
  3. <body>
  4. <![CDATA[
  5. self.socket = None
  6. self.received_data = ""
  7. self.send_data = ""
  8. self.queue = []
  9. self.IDs = []
  10. ]]>
  11. </body>
  12. </constructor>
  13. <scxml initial="init">
  14. <state id="init">
  15. <onentry>
  16. <script>
  17. self.ID = str(uuid.uuid4())
  18. </script>
  19. <raise scope="output" event="create_socket" port="socket_out">
  20. <parameter expr="self.ID"/>
  21. </raise>
  22. </onentry>
  23. <transition port="socket_in" event="created_socket" cond="self.ID == ID" target="../waiting">
  24. <parameter name="socket"/>
  25. <parameter name="ID"/>
  26. <script>
  27. self.socket = socket
  28. </script>
  29. </transition>
  30. </state>
  31. <state id="waiting">
  32. <onentry>
  33. <raise scope="broad" event="http_client_initialized"/>
  34. </onentry>
  35. <transition event="connect" target="../connecting">
  36. <parameter name="address"/>
  37. <parameter name="timeout"/>
  38. <script>
  39. self.address = address
  40. self.timeout = timeout
  41. </script>
  42. </transition>
  43. </state>
  44. <state id="connecting" initial="connecting">
  45. <state id="connecting">
  46. <onentry>
  47. <raise scope="output" event="connect_socket" port="socket_out">
  48. <parameter expr="self.socket"/>
  49. <parameter expr="self.address"/>
  50. </raise>
  51. <script>
  52. print("Connecting ...")
  53. </script>
  54. </onentry>
  55. <transition port="socket_in" event="error_socket" target="../cooldown"/>
  56. <transition port="socket_in" event="connected_socket" cond="self.socket == socket" target="../../connected">
  57. <parameter name="socket"/>
  58. <raise scope="broad" event="http_client_ready"/>
  59. </transition>
  60. </state>
  61. <state id="cooldown">
  62. <transition after="0.1" target="../connecting">
  63. <script>
  64. print("COOLDOWN")
  65. </script>
  66. </transition>
  67. </state>
  68. <transition after="self.timeout" target="../waiting">
  69. <raise scope="broad" event="http_client_timeout"/>
  70. </transition>
  71. </state>
  72. <parallel id="connected">
  73. <state id="listening" initial="listen">
  74. <state id="listen">
  75. <onentry>
  76. <raise scope="output" port="socket_out" event="recv_socket">
  77. <parameter expr="self.socket"/>
  78. </raise>
  79. </onentry>
  80. <transition event="received_socket" port="socket_in" cond="(self.socket == socket) and (len(data) > 0)" target=".">
  81. <parameter name="socket"/>
  82. <parameter name="data"/>
  83. <script>
  84. if sys.version_info[0] > 2:
  85. if isinstance(data, bytes):
  86. data = data.decode()
  87. self.received_data += data
  88. </script>
  89. </transition>
  90. <transition event="received_socket" port="socket_in" cond="(self.socket == socket) and (len(data) == 0)" target="../close">
  91. <parameter name="socket"/>
  92. <parameter name="data"/>
  93. </transition>
  94. </state>
  95. <state id="close">
  96. </state>
  97. </state>
  98. <state id="sending" initial="waiting_for_data">
  99. <state id="waiting_for_data">
  100. <transition cond="len(self.send_data) > 0" target="../transferring">
  101. <raise scope="output" port="socket_out" event="send_socket">
  102. <parameter expr="self.socket"/>
  103. <parameter expr="self.send_data"/>
  104. </raise>
  105. </transition>
  106. </state>
  107. <state id="transferring">
  108. <transition event="sent_socket" port="socket_in" cond="self.socket == socket" target="../waiting_for_data">
  109. <parameter name="socket"/>
  110. <parameter name="sent_bytes"/>
  111. <script>
  112. self.send_data = self.send_data[sent_bytes:]
  113. </script>
  114. </transition>
  115. </state>
  116. </state>
  117. <state id="queueing">
  118. <state id="queueing">
  119. <onentry>
  120. </onentry>
  121. <transition event="HTTP_input" target=".">
  122. <parameter name="data"/>
  123. <parameter name="ID"/>
  124. <script>
  125. self.send_data += "POST / HTTP/1.0\r\n"
  126. self.send_data += "Content-Length: %i\r\n" % len(str(data))
  127. self.send_data += "\r\n"
  128. self.send_data += data
  129. self.IDs.append(ID)
  130. </script>
  131. </transition>
  132. </state>
  133. </state>
  134. <state id="parsing" initial="wait_for_header">
  135. <state id="wait_for_header">
  136. <transition cond="'\r\n\r\n' in self.received_data" target="../wait_for_payload">
  137. <script>
  138. header, self.received_data = self.received_data.split("\r\n\r\n", 1)
  139. header = header.lower()
  140. if "content-length" in header:
  141. _, after = header.split("content-length:", 1)
  142. after, _ = after.split("\r\n", 1)
  143. after = after.strip()
  144. self.length = int(after)
  145. else:
  146. self.length = float('inf')
  147. </script>
  148. </transition>
  149. </state>
  150. <state id="wait_for_payload">
  151. <transition cond="len(self.received_data) >= self.length and self.IDs[0] is not None" target="../wait_for_header">
  152. <script>
  153. data = self.received_data[:self.length]
  154. self.received_data = self.received_data[self.length:]
  155. </script>
  156. <raise event="HTTP_output" scope="broad">
  157. <parameter expr="data"/>
  158. <parameter expr="self.IDs.pop(0)"/>
  159. </raise>
  160. </transition>
  161. <transition cond="len(self.received_data) >= self.length and self.IDs[0] is None" target="../wait_for_header">
  162. <script>
  163. # Drop data
  164. self.received_data = self.received_data[self.length:]
  165. self.IDs.pop(0)
  166. </script>
  167. </transition>
  168. </state>
  169. </state>
  170. </parallel>
  171. </scxml>
  172. </class>