1425bb815Sopenharmony_ci#!/usr/bin/env python
2425bb815Sopenharmony_ci
3425bb815Sopenharmony_ci# Copyright JS Foundation and other contributors, http://js.foundation
4425bb815Sopenharmony_ci#
5425bb815Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
6425bb815Sopenharmony_ci# you may not use this file except in compliance with the License.
7425bb815Sopenharmony_ci# You may obtain a copy of the License at
8425bb815Sopenharmony_ci#
9425bb815Sopenharmony_ci#     http://www.apache.org/licenses/LICENSE-2.0
10425bb815Sopenharmony_ci#
11425bb815Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software
12425bb815Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS
13425bb815Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14425bb815Sopenharmony_ci# See the License for the specific language governing permissions and
15425bb815Sopenharmony_ci# limitations under the License.
16425bb815Sopenharmony_ci
17425bb815Sopenharmony_ciimport struct
18425bb815Sopenharmony_ci
19425bb815Sopenharmony_ciMAX_BUFFER_SIZE = 128
20425bb815Sopenharmony_ciWEBSOCKET_BINARY_FRAME = 2
21425bb815Sopenharmony_ciWEBSOCKET_FIN_BIT = 0x80
22425bb815Sopenharmony_ci
23425bb815Sopenharmony_ciclass WebSocket(object):
24425bb815Sopenharmony_ci    def __init__(self, protocol):
25425bb815Sopenharmony_ci
26425bb815Sopenharmony_ci        self.data_buffer = b""
27425bb815Sopenharmony_ci        self.protocol = protocol
28425bb815Sopenharmony_ci
29425bb815Sopenharmony_ci    def __handshake(self):
30425bb815Sopenharmony_ci        """ Client Handshake Request. """
31425bb815Sopenharmony_ci        self.__send_data(b"GET /jerry-debugger HTTP/1.1\r\n" +
32425bb815Sopenharmony_ci                         b"Upgrade: websocket\r\n" +
33425bb815Sopenharmony_ci                         b"Connection: Upgrade\r\n" +
34425bb815Sopenharmony_ci                         b"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n")
35425bb815Sopenharmony_ci
36425bb815Sopenharmony_ci        # Expected answer from the handshake.
37425bb815Sopenharmony_ci        expected = (b"HTTP/1.1 101 Switching Protocols\r\n" +
38425bb815Sopenharmony_ci                    b"Upgrade: websocket\r\n" +
39425bb815Sopenharmony_ci                    b"Connection: Upgrade\r\n" +
40425bb815Sopenharmony_ci                    b"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n")
41425bb815Sopenharmony_ci
42425bb815Sopenharmony_ci        len_expected = len(expected)
43425bb815Sopenharmony_ci
44425bb815Sopenharmony_ci        while len(self.data_buffer) < len_expected:
45425bb815Sopenharmony_ci            self.data_buffer += self.protocol.receive_data()
46425bb815Sopenharmony_ci
47425bb815Sopenharmony_ci        if self.data_buffer[0:len_expected] != expected:
48425bb815Sopenharmony_ci            raise Exception("Unexpected handshake")
49425bb815Sopenharmony_ci
50425bb815Sopenharmony_ci        if len(self.data_buffer) > len_expected:
51425bb815Sopenharmony_ci            self.data_buffer = self.data_buffer[len_expected:]
52425bb815Sopenharmony_ci        else:
53425bb815Sopenharmony_ci            self.data_buffer = b""
54425bb815Sopenharmony_ci
55425bb815Sopenharmony_ci    def connect(self, config_size):
56425bb815Sopenharmony_ci        """  WebSockets connection. """
57425bb815Sopenharmony_ci        self.protocol.connect()
58425bb815Sopenharmony_ci        self.data_buffer = b""
59425bb815Sopenharmony_ci        self.__handshake()
60425bb815Sopenharmony_ci
61425bb815Sopenharmony_ci        # It will return with the Network configurations, which has the following struct:
62425bb815Sopenharmony_ci        # header [2] - opcode[1], size[1]
63425bb815Sopenharmony_ci        # configuration [config_size]
64425bb815Sopenharmony_ci        len_expected = config_size + 2
65425bb815Sopenharmony_ci
66425bb815Sopenharmony_ci        while len(self.data_buffer) < len_expected:
67425bb815Sopenharmony_ci            self.data_buffer += self.protocol.receive_data()
68425bb815Sopenharmony_ci
69425bb815Sopenharmony_ci        expected = struct.pack("BB",
70425bb815Sopenharmony_ci                               WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT,
71425bb815Sopenharmony_ci                               config_size)
72425bb815Sopenharmony_ci
73425bb815Sopenharmony_ci        if self.data_buffer[0:2] != expected:
74425bb815Sopenharmony_ci            raise Exception("Unexpected configuration")
75425bb815Sopenharmony_ci
76425bb815Sopenharmony_ci        result = self.data_buffer[2:len_expected]
77425bb815Sopenharmony_ci        self.data_buffer = self.data_buffer[len_expected:]
78425bb815Sopenharmony_ci
79425bb815Sopenharmony_ci        return result
80425bb815Sopenharmony_ci
81425bb815Sopenharmony_ci    def __send_data(self, data):
82425bb815Sopenharmony_ci        """ Private function to send data using the given protocol. """
83425bb815Sopenharmony_ci        size = len(data)
84425bb815Sopenharmony_ci
85425bb815Sopenharmony_ci        while size > 0:
86425bb815Sopenharmony_ci            bytes_send = self.protocol.send_data(data)
87425bb815Sopenharmony_ci            if bytes_send < size:
88425bb815Sopenharmony_ci                data = data[bytes_send:]
89425bb815Sopenharmony_ci            size -= bytes_send
90425bb815Sopenharmony_ci
91425bb815Sopenharmony_ci    def send_message(self, byte_order, packed_data):
92425bb815Sopenharmony_ci        """ Send message. """
93425bb815Sopenharmony_ci        message = struct.pack(byte_order + "BBI",
94425bb815Sopenharmony_ci                              WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT,
95425bb815Sopenharmony_ci                              WEBSOCKET_FIN_BIT + struct.unpack(byte_order + "B", packed_data[0])[0],
96425bb815Sopenharmony_ci                              0) + packed_data[1:]
97425bb815Sopenharmony_ci
98425bb815Sopenharmony_ci        self.__send_data(message)
99425bb815Sopenharmony_ci
100425bb815Sopenharmony_ci    def close(self):
101425bb815Sopenharmony_ci        """ Close the WebSockets connection. """
102425bb815Sopenharmony_ci        self.protocol.close()
103425bb815Sopenharmony_ci
104425bb815Sopenharmony_ci    def get_message(self, blocking):
105425bb815Sopenharmony_ci        """ Receive message. """
106425bb815Sopenharmony_ci
107425bb815Sopenharmony_ci        # Connection was closed
108425bb815Sopenharmony_ci        if self.data_buffer is None:
109425bb815Sopenharmony_ci            return None
110425bb815Sopenharmony_ci
111425bb815Sopenharmony_ci        while True:
112425bb815Sopenharmony_ci            if len(self.data_buffer) >= 2:
113425bb815Sopenharmony_ci                if ord(self.data_buffer[0]) != WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT:
114425bb815Sopenharmony_ci                    raise Exception("Unexpected data frame")
115425bb815Sopenharmony_ci
116425bb815Sopenharmony_ci                size = ord(self.data_buffer[1])
117425bb815Sopenharmony_ci                if size == 0 or size >= 126:
118425bb815Sopenharmony_ci                    raise Exception("Unexpected data frame")
119425bb815Sopenharmony_ci
120425bb815Sopenharmony_ci                if len(self.data_buffer) >= size + 2:
121425bb815Sopenharmony_ci                    result = self.data_buffer[2:size + 2]
122425bb815Sopenharmony_ci                    self.data_buffer = self.data_buffer[size + 2:]
123425bb815Sopenharmony_ci                    return result
124425bb815Sopenharmony_ci
125425bb815Sopenharmony_ci            if not blocking and not self.protocol.ready():
126425bb815Sopenharmony_ci                return b''
127425bb815Sopenharmony_ci
128425bb815Sopenharmony_ci            data = self.protocol.receive_data(MAX_BUFFER_SIZE)
129425bb815Sopenharmony_ci
130425bb815Sopenharmony_ci            if not data:
131425bb815Sopenharmony_ci                self.data_buffer = None
132425bb815Sopenharmony_ci                return None
133425bb815Sopenharmony_ci            self.data_buffer += data
134