1515
1616
1717class API ():
18-
1918 def run_forever (self ):
2019 try :
2120 print ("Listening on port %d for clients.." % self .port )
@@ -26,28 +25,20 @@ def run_forever(self):
2625 except Exception as e :
2726 print ("ERROR: WebSocketsServer: " + str (e ))
2827 exit (1 )
29-
3028 def new_client (self , client , server ):
3129 pass
32-
3330 def client_left (self , client , server ):
3431 pass
35-
3632 def message_received (self , client , server , message ):
3733 pass
38-
3934 def set_fn_new_client (self , fn ):
4035 self .new_client = fn
41-
4236 def set_fn_client_left (self , fn ):
4337 self .client_left = fn
44-
4538 def set_fn_message_received (self , fn ):
4639 self .message_received = fn
47-
4840 def send_message (self , client , msg ):
4941 self ._unicast_ (client , msg )
50-
5142 def send_message_to_all (self , msg ):
5243 self ._multicast_ (msg )
5344
@@ -83,13 +74,13 @@ def _new_client_(self, handler):
8374 }
8475 self .clients .append (client )
8576 self .new_client (client , self )
86-
77+
8778 def _client_left_ (self , handler ):
8879 client = self .handler_to_client (handler )
8980 self .client_left (client , self )
9081 if client in self .clients :
9182 self .clients .remove (client )
92-
83+
9384 def _unicast_ (self , to_client , msg ):
9485 to_client ['handler' ].send_message (msg )
9586
@@ -123,6 +114,7 @@ def handle(self):
123114 elif self .valid_client :
124115 self .read_next_message ()
125116
117+
126118 def read_next_message (self ):
127119
128120 b1 = self .rfile .read (1 )
@@ -165,9 +157,11 @@ def read_next_message(self):
165157 decoded += chr (char )
166158 self .server ._message_received_ (self , decoded )
167159
160+
168161 def send_message (self , message ):
169162 self .send_text (message )
170163
164+
171165 def send_text (self , message ):
172166 '''
173167 +-+-+-+-+-------+-+-------------+-------------------------------+
@@ -181,60 +175,58 @@ def send_text(self, message):
181175 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
182176 | Extended payload length continued, if payload len == 127 |
183177 + - - - - - - - - - - - - - - - +-------------------------------+
184- | | Masking-key, if MASK set to 1 |
185- +-------------------------------+-------------------------------+
186- | Masking-key (continued) | Payload Data |
187- +-------------------------------- - - - - - - - - - - - - - - - +
188- : Payload Data continued ... :
189- + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
190178 | Payload Data continued ... |
191179 +---------------------------------------------------------------+
192-
180+
193181 NOTES
194182 Fragmented(=continuation) messages are not being used since their usage
195183 is needed in very limited cases - when we don't know the payload length.
196184 '''
197185
198- # 0 = '0x00' = 0b00000000
199- # 125 = '0x7d' = 0b01111101
200- # 126 = '0x7e' = 0b01111110
201- # 127 = '0x7f' = 0b01111111
202- # 128 = '0x80' = 0b10000000
203- # 129 = '0x81' = 0b10000001
204-
205- # assure message is of acceptable format and encode to UTF-8 if needed
206- encoded_message = None
186+ FIN = 0x80
187+ OPCODE_TEXT = 0x01
188+ EXT_PAYLOAD_16BITS = 0x7e
189+ EXT_PAYLOAD_64BITS = 0x7f
190+
191+ # Validate message
207192 if isinstance (message , bytes ):
208193 message = try_decode_UTF8 (message ) # this is slower but assures we have UTF-8
209194 if not message :
210- raise Exception ("Can\' t send message, message is not valid UTF-8" )
195+ print ("Can\' t send message, message is not valid UTF-8" )
211196 return False
212- elif not (isinstance (message , str ) or isinstance (message , unicode )):
213- raise Exception ('Can\' t send message, message has to be a string or bytes. Given type is %s' % type (message ))
197+ elif isinstance (message , str ):
198+ pass
199+ else :
200+ print ('Can\' t send message, message has to be a string or bytes. Given type is %s' % type (message ))
214201 return False
215- encoded_message = encode_to_UTF8 (message )
216- length = len (encoded_message )
217-
218- # normal payload
219- if length <= 125 :
220- #print("sending single frame of size %s", length)
221- self .request .send (b'\x81 ' )
222- self .request .send (chr (length ).encode ())
223- self .request .send (encoded_message )
202+
203+ header = bytearray ()
204+ payload = encode_to_UTF8 (message )
205+ payload_length = len (payload )
206+
207+ # Normal payload
208+ if payload_length <= 125 :
209+ header .append (FIN | OPCODE_TEXT )
210+ header .append (payload_length )
211+
212+ # Extended payload
213+ elif payload_length >= 126 and payload_length <= 65535 :
214+ header .append (FIN | OPCODE_TEXT )
215+ header .append (EXT_PAYLOAD_16BITS )
216+ header .extend (struct .pack (">H" , payload_length ))
217+
218+ # Huge extended payload
219+ elif payload_length < 18446744073709551616 :
220+ header .append (FIN | OPCODE_TEXT )
221+ header .append (EXT_PAYLOAD_64BITS )
222+ header .extend (struct .pack (">Q" , payload_length ))
224223
225- # extended payload
226- elif length >= 126 and length <= 65535 :
227- #print("sending extended frame of size %s", length)
228- self .request .send (b'\x81 \x7e ' )
229- self .request .send (struct .pack (">H" , length )) # MUST be 16bits
230- self .request .send (encoded_message )
231-
232- # huge extended payload
233- elif length < 18446744073709551616 :
234- #print("sending extended frame of size %s", length)
235- self .request .send (b'\x81 \x7f ' )
236- self .request .send (struct .pack (">Q" , length )) # MUST be 64bits
237- self .request .send (encoded_message )
224+ else :
225+ raise Exception ("Message is too big. Consider breaking it into chunks." )
226+ return
227+
228+ self .request .send (header + payload )
229+
238230
239231 def handshake (self ):
240232 message = self .request .recv (1024 ).decode ().strip ()
@@ -254,6 +246,7 @@ def handshake(self):
254246 self .valid_client = True
255247 self .server ._new_client_ (self )
256248
249+
257250 def make_handshake_response (self , key ):
258251 return \
259252 'HTTP/1.1 101 Switching Protocols\r \n ' \
@@ -262,12 +255,14 @@ def make_handshake_response(self, key):
262255 'Sec-WebSocket-Accept: %s\r \n ' \
263256 '\r \n ' % self .calculate_response_key (key )
264257
258+
265259 def calculate_response_key (self , key ):
266260 GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
267261 hash = sha1 (key .encode () + GUID .encode ())
268262 response_key = b64encode (hash .digest ()).strip ()
269263 return response_key .decode ('ASCII' )
270264
265+
271266 def finish (self ):
272267 self .server ._client_left_ (self )
273268
0 commit comments