@@ -86,6 +86,18 @@ def send_message(self, client, msg):
8686 def send_message_to_all (self , msg ):
8787 self ._multicast (msg )
8888
89+ def shutdown_gracefully (self , status = CLOSE_STATUS_NORMAL , reason = DEFAULT_CLOSE_REASON ):
90+ """
91+ Close with a websocket handshake
92+
93+ 1. Send CLOSE to all clients
94+ 2. Close TCP
95+ """
96+ self .keep_alive = False
97+ for client in self .clients :
98+ client ["handler" ].send_close (CLOSE_STATUS_NORMAL , reason )
99+ self .server_close ()
100+
89101
90102class WebsocketServer (ThreadingMixIn , TCPServer , API ):
91103 """
@@ -258,7 +270,16 @@ def send_close(self, status=CLOSE_STATUS_NORMAL, reason=DEFAULT_CLOSE_REASON):
258270 """
259271 if status < CLOSE_STATUS_NORMAL or status > 1015 :
260272 raise Exception (f"CLOSE status must be between 1000 and 1015, got { status } " )
261- self .request .send (struct .pack ('!H' , status ) + reason , OPCODE_CLOSE_CONN )
273+
274+ header = bytearray ()
275+ payload = struct .pack ('!H' , status ) + reason
276+ payload_length = len (payload )
277+ assert payload_length <= 125 , "We only support short closing reasons at the moment"
278+
279+ # Send CLOSE with status & reason
280+ header .append (FIN | OPCODE_CLOSE_CONN )
281+ header .append (payload_length )
282+ self .request .send (header + payload )
262283
263284 def send_text (self , message , opcode = OPCODE_TEXT ):
264285 """
0 commit comments