http_client.xml 8.5 KB

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