|
@@ -1,192 +1,177 @@
|
|
|
-<?xml version="1.0" encoding="UTF-8"?>
|
|
|
-<diagram author="Yentl Van Tendeloo" name="HTTP client">
|
|
|
- <description>
|
|
|
- HTTP client.
|
|
|
- </description>
|
|
|
- <top>
|
|
|
- import uuid
|
|
|
- </top>
|
|
|
-
|
|
|
- <inport name="socket_in"/>
|
|
|
- <outport name="socket_out"/>
|
|
|
-
|
|
|
- <inport name="request_in"/>
|
|
|
- <outport name="request_out"/>
|
|
|
-
|
|
|
- <class name="HTTPClient">
|
|
|
- <constructor>
|
|
|
- <body>
|
|
|
- <![CDATA[
|
|
|
- self.socket = None
|
|
|
- self.received_data = ""
|
|
|
- self.send_data = ""
|
|
|
- self.queue = []
|
|
|
- self.IDs = []
|
|
|
- ]]>
|
|
|
- </body>
|
|
|
- </constructor>
|
|
|
- <scxml initial="init">
|
|
|
- <state id="init">
|
|
|
+<class name="HTTPClient">
|
|
|
+ <constructor>
|
|
|
+ <body>
|
|
|
+ <![CDATA[
|
|
|
+ self.socket = None
|
|
|
+ self.received_data = ""
|
|
|
+ self.send_data = ""
|
|
|
+ self.queue = []
|
|
|
+ self.IDs = []
|
|
|
+ ]]>
|
|
|
+ </body>
|
|
|
+ </constructor>
|
|
|
+
|
|
|
+ <scxml initial="init">
|
|
|
+ <state id="init">
|
|
|
+ <onentry>
|
|
|
+ <script>
|
|
|
+ self.ID = str(uuid.uuid4())
|
|
|
+ </script>
|
|
|
+ <raise scope="output" event="create_socket" port="socket_out">
|
|
|
+ <parameter expr="self.ID"/>
|
|
|
+ </raise>
|
|
|
+ </onentry>
|
|
|
+
|
|
|
+ <transition port="socket_in" event="created_socket" cond="self.ID == ID" target="../waiting">
|
|
|
+ <parameter name="socket"/>
|
|
|
+ <parameter name="ID"/>
|
|
|
+ <script>
|
|
|
+ self.socket = socket
|
|
|
+ </script>
|
|
|
+ </transition>
|
|
|
+ </state>
|
|
|
+
|
|
|
+ <state id="waiting">
|
|
|
+ <onentry>
|
|
|
+ <raise scope="broad" event="http_client_initialized"/>
|
|
|
+ </onentry>
|
|
|
+
|
|
|
+ <transition event="connect" target="../connecting">
|
|
|
+ <parameter name="address"/>
|
|
|
+ <parameter name="timeout"/>
|
|
|
+
|
|
|
+ <script>
|
|
|
+ self.address = address
|
|
|
+ self.timeout = timeout
|
|
|
+ </script>
|
|
|
+ </transition>
|
|
|
+ </state>
|
|
|
+
|
|
|
+ <state id="connecting" initial="connecting">
|
|
|
+ <state id="connecting">
|
|
|
<onentry>
|
|
|
- <script>
|
|
|
- self.ID = str(uuid.uuid4())
|
|
|
- </script>
|
|
|
- <raise scope="output" event="create_socket" port="socket_out">
|
|
|
- <parameter expr="self.ID"/>
|
|
|
+ <raise scope="output" event="connect_socket" port="socket_out">
|
|
|
+ <parameter expr="self.socket"/>
|
|
|
+ <parameter expr="self.address"/>
|
|
|
</raise>
|
|
|
</onentry>
|
|
|
|
|
|
- <transition port="socket_in" event="created_socket" cond="self.ID == ID" target="../waiting">
|
|
|
+ <transition port="socket_in" event="error_socket" target="../cooldown"/>
|
|
|
+
|
|
|
+ <transition port="socket_in" event="connected_socket" cond="self.socket == socket" target="../../connected">
|
|
|
<parameter name="socket"/>
|
|
|
- <parameter name="ID"/>
|
|
|
- <script>
|
|
|
- self.socket = socket
|
|
|
- </script>
|
|
|
+ <raise scope="broad" event="http_client_ready"/>
|
|
|
</transition>
|
|
|
</state>
|
|
|
|
|
|
- <state id="waiting">
|
|
|
- <onentry>
|
|
|
- <raise scope="output" port="request_out" event="http_client_initialized"/>
|
|
|
- </onentry>
|
|
|
-
|
|
|
- <transition port="request_in" event="connect" target="../connecting">
|
|
|
- <parameter name="address"/>
|
|
|
- <parameter name="timeout"/>
|
|
|
-
|
|
|
- <script>
|
|
|
- self.address = address
|
|
|
- self.timeout = timeout
|
|
|
- </script>
|
|
|
- </transition>
|
|
|
+ <state id="cooldown">
|
|
|
+ <transition after="0.1" target="../connecting"/>
|
|
|
</state>
|
|
|
|
|
|
- <state id="connecting" initial="connecting">
|
|
|
- <state id="connecting">
|
|
|
+ <transition after="self.timeout" target="../waiting">
|
|
|
+ <raise scope="broad" event="http_client_timeout"/>
|
|
|
+ </transition>
|
|
|
+ </state>
|
|
|
+
|
|
|
+ <parallel id="connected">
|
|
|
+ <state id="listening" initial="listen">
|
|
|
+ <state id="listen">
|
|
|
<onentry>
|
|
|
- <raise scope="output" event="connect_socket" port="socket_out">
|
|
|
+ <raise scope="output" port="socket_out" event="recv_socket">
|
|
|
<parameter expr="self.socket"/>
|
|
|
- <parameter expr="self.address"/>
|
|
|
</raise>
|
|
|
</onentry>
|
|
|
-
|
|
|
- <transition port="socket_in" event="error_socket" target="../cooldown"/>
|
|
|
-
|
|
|
- <transition port="socket_in" event="connected_socket" cond="self.socket == socket" target="../../connected">
|
|
|
+ <transition event="received_socket" port="socket_in" cond="(self.socket == socket) and (len(data) > 0)" target=".">
|
|
|
+ <parameter name="socket"/>
|
|
|
+ <parameter name="data"/>
|
|
|
+ <script>
|
|
|
+ self.received_data += data
|
|
|
+ </script>
|
|
|
+ </transition>
|
|
|
+ <transition event="received_socket" port="socket_in" cond="(self.socket == socket) and (len(data) == 0)" target="../close">
|
|
|
<parameter name="socket"/>
|
|
|
- <raise scope="output" port="request_out" event="http_client_ready"/>
|
|
|
+ <parameter name="data"/>
|
|
|
</transition>
|
|
|
</state>
|
|
|
-
|
|
|
- <state id="cooldown">
|
|
|
- <transition after="0.1" target="../connecting"/>
|
|
|
+ <state id="close">
|
|
|
</state>
|
|
|
-
|
|
|
- <transition after="self.timeout" target="../waiting">
|
|
|
- <raise scope="output" port="request_out" event="http_client_timeout"/>
|
|
|
- </transition>
|
|
|
</state>
|
|
|
|
|
|
- <parallel id="connected">
|
|
|
- <state id="listening" initial="listen">
|
|
|
- <state id="listen">
|
|
|
- <onentry>
|
|
|
- <raise scope="output" port="socket_out" event="recv_socket">
|
|
|
- <parameter expr="self.socket"/>
|
|
|
- </raise>
|
|
|
- </onentry>
|
|
|
- <transition event="received_socket" port="socket_in" cond="(self.socket == socket) and (len(data) > 0)" target=".">
|
|
|
- <parameter name="socket"/>
|
|
|
- <parameter name="data"/>
|
|
|
- <script>
|
|
|
- self.received_data += data
|
|
|
- </script>
|
|
|
- </transition>
|
|
|
- <transition event="received_socket" port="socket_in" cond="(self.socket == socket) and (len(data) == 0)" target="../close">
|
|
|
- <parameter name="socket"/>
|
|
|
- <parameter name="data"/>
|
|
|
- </transition>
|
|
|
- </state>
|
|
|
- <state id="close">
|
|
|
- </state>
|
|
|
+ <state id="sending" initial="waiting_for_data">
|
|
|
+ <state id="waiting_for_data">
|
|
|
+ <transition cond="len(self.send_data) > 0" target="../transferring">
|
|
|
+ <raise scope="output" port="socket_out" event="send_socket">
|
|
|
+ <parameter expr="self.socket"/>
|
|
|
+ <parameter expr="self.send_data"/>
|
|
|
+ </raise>
|
|
|
+ </transition>
|
|
|
</state>
|
|
|
-
|
|
|
- <state id="sending" initial="waiting_for_data">
|
|
|
- <state id="waiting_for_data">
|
|
|
- <transition cond="len(self.send_data) > 0" target="../transferring">
|
|
|
- <raise scope="output" port="socket_out" event="send_socket">
|
|
|
- <parameter expr="self.socket"/>
|
|
|
- <parameter expr="self.send_data"/>
|
|
|
- </raise>
|
|
|
- </transition>
|
|
|
- </state>
|
|
|
- <state id="transferring">
|
|
|
- <transition event="sent_socket" port="socket_in" cond="self.socket == socket" target="../waiting_for_data">
|
|
|
- <parameter name="socket"/>
|
|
|
- <parameter name="sent_bytes"/>
|
|
|
- <script>
|
|
|
- self.send_data = self.send_data[sent_bytes:]
|
|
|
- </script>
|
|
|
- </transition>
|
|
|
- </state>
|
|
|
+ <state id="transferring">
|
|
|
+ <transition event="sent_socket" port="socket_in" cond="self.socket == socket" target="../waiting_for_data">
|
|
|
+ <parameter name="socket"/>
|
|
|
+ <parameter name="sent_bytes"/>
|
|
|
+ <script>
|
|
|
+ self.send_data = self.send_data[sent_bytes:]
|
|
|
+ </script>
|
|
|
+ </transition>
|
|
|
</state>
|
|
|
+ </state>
|
|
|
|
|
|
+ <state id="queueing">
|
|
|
<state id="queueing">
|
|
|
- <state id="queueing">
|
|
|
- <onentry>
|
|
|
- </onentry>
|
|
|
- <transition port="request_in" event="HTTP_input" target=".">
|
|
|
- <parameter name="data"/>
|
|
|
- <parameter name="ID"/>
|
|
|
- <script>
|
|
|
- self.send_data += "POST / HTTP/1.0\r\n"
|
|
|
- self.send_data += "Content-Length: %i\r\n" % len(str(data))
|
|
|
- self.send_data += "\r\n"
|
|
|
- self.send_data += data
|
|
|
- self.IDs.append(ID)
|
|
|
- </script>
|
|
|
- </transition>
|
|
|
- </state>
|
|
|
+ <onentry>
|
|
|
+ </onentry>
|
|
|
+ <transition event="HTTP_input" target=".">
|
|
|
+ <parameter name="data"/>
|
|
|
+ <parameter name="ID"/>
|
|
|
+ <script>
|
|
|
+ self.send_data += "POST / HTTP/1.0\r\n"
|
|
|
+ self.send_data += "Content-Length: %i\r\n" % len(str(data))
|
|
|
+ self.send_data += "\r\n"
|
|
|
+ self.send_data += data
|
|
|
+ self.IDs.append(ID)
|
|
|
+ </script>
|
|
|
+ </transition>
|
|
|
</state>
|
|
|
+ </state>
|
|
|
|
|
|
- <state id="parsing" initial="wait_for_header">
|
|
|
- <state id="wait_for_header">
|
|
|
- <transition cond="'\r\n\r\n' in self.received_data" target="../wait_for_payload">
|
|
|
- <script>
|
|
|
- header, self.received_data = self.received_data.split("\r\n\r\n", 1)
|
|
|
- header = header.lower()
|
|
|
- if "content-length" in header:
|
|
|
- _, after = header.split("content-length:", 1)
|
|
|
- after, _ = after.split("\r\n", 1)
|
|
|
- after = after.strip()
|
|
|
- self.length = int(after)
|
|
|
- else:
|
|
|
- self.length = float('inf')
|
|
|
- </script>
|
|
|
- </transition>
|
|
|
- </state>
|
|
|
- <state id="wait_for_payload">
|
|
|
- <transition cond="len(self.received_data) >= self.length and self.IDs[0] is not None" target="../wait_for_header">
|
|
|
- <script>
|
|
|
- data = self.received_data[:self.length]
|
|
|
- self.received_data = self.received_data[self.length:]
|
|
|
- </script>
|
|
|
- <raise event="HTTP_output" scope="output" port="request_out">
|
|
|
- <parameter expr="data"/>
|
|
|
- <parameter expr="self.IDs.pop(0)"/>
|
|
|
- </raise>
|
|
|
- </transition>
|
|
|
+ <state id="parsing" initial="wait_for_header">
|
|
|
+ <state id="wait_for_header">
|
|
|
+ <transition cond="'\r\n\r\n' in self.received_data" target="../wait_for_payload">
|
|
|
+ <script>
|
|
|
+ header, self.received_data = self.received_data.split("\r\n\r\n", 1)
|
|
|
+ header = header.lower()
|
|
|
+ if "content-length" in header:
|
|
|
+ _, after = header.split("content-length:", 1)
|
|
|
+ after, _ = after.split("\r\n", 1)
|
|
|
+ after = after.strip()
|
|
|
+ self.length = int(after)
|
|
|
+ else:
|
|
|
+ self.length = float('inf')
|
|
|
+ </script>
|
|
|
+ </transition>
|
|
|
+ </state>
|
|
|
+ <state id="wait_for_payload">
|
|
|
+ <transition cond="len(self.received_data) >= self.length and self.IDs[0] is not None" target="../wait_for_header">
|
|
|
+ <script>
|
|
|
+ data = self.received_data[:self.length]
|
|
|
+ self.received_data = self.received_data[self.length:]
|
|
|
+ </script>
|
|
|
+ <raise event="HTTP_output" scope="broad">
|
|
|
+ <parameter expr="data"/>
|
|
|
+ <parameter expr="self.IDs.pop(0)"/>
|
|
|
+ </raise>
|
|
|
+ </transition>
|
|
|
|
|
|
- <transition cond="len(self.received_data) >= self.length and self.IDs[0] is None" target="../wait_for_header">
|
|
|
- <script>
|
|
|
- # Drop data
|
|
|
- self.received_data = self.received_data[self.length:]
|
|
|
- self.IDs.pop(0)
|
|
|
- </script>
|
|
|
- </transition>
|
|
|
- </state>
|
|
|
+ <transition cond="len(self.received_data) >= self.length and self.IDs[0] is None" target="../wait_for_header">
|
|
|
+ <script>
|
|
|
+ # Drop data
|
|
|
+ self.received_data = self.received_data[self.length:]
|
|
|
+ self.IDs.pop(0)
|
|
|
+ </script>
|
|
|
+ </transition>
|
|
|
</state>
|
|
|
- </parallel>
|
|
|
- </scxml>
|
|
|
- </class>
|
|
|
-</diagram>
|
|
|
+ </state>
|
|
|
+ </parallel>
|
|
|
+ </scxml>
|
|
|
+</class>
|