-
-
Save jkp/3136208 to your computer and use it in GitHub Desktop.
import struct | |
import SocketServer | |
from base64 import b64encode | |
from hashlib import sha1 | |
from mimetools import Message | |
from StringIO import StringIO | |
class WebSocketsHandler(SocketServer.StreamRequestHandler): | |
magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' | |
def setup(self): | |
SocketServer.StreamRequestHandler.setup(self) | |
print "connection established", self.client_address | |
self.handshake_done = False | |
def handle(self): | |
while True: | |
if not self.handshake_done: | |
self.handshake() | |
else: | |
self.read_next_message() | |
def read_next_message(self): | |
length = ord(self.rfile.read(2)[1]) & 127 | |
if length == 126: | |
length = struct.unpack(">H", self.rfile.read(2))[0] | |
elif length == 127: | |
length = struct.unpack(">Q", self.rfile.read(8))[0] | |
masks = [ord(byte) for byte in self.rfile.read(4)] | |
decoded = "" | |
for char in self.rfile.read(length): | |
decoded += chr(ord(char) ^ masks[len(decoded) % 4]) | |
self.on_message(decoded) | |
def send_message(self, message): | |
self.request.send(chr(129)) | |
length = len(message) | |
if length <= 125: | |
self.request.send(chr(length)) | |
elif length >= 126 and length <= 65535: | |
self.request.send(126) | |
self.request.send(struct.pack(">H", length)) | |
else: | |
self.request.send(127) | |
self.request.send(struct.pack(">Q", length)) | |
self.request.send(message) | |
def handshake(self): | |
data = self.request.recv(1024).strip() | |
headers = Message(StringIO(data.split('\r\n', 1)[1])) | |
if headers.get("Upgrade", None) != "websocket": | |
return | |
print 'Handshaking...' | |
key = headers['Sec-WebSocket-Key'] | |
digest = b64encode(sha1(key + self.magic).hexdigest().decode('hex')) | |
response = 'HTTP/1.1 101 Switching Protocols\r\n' | |
response += 'Upgrade: websocket\r\n' | |
response += 'Connection: Upgrade\r\n' | |
response += 'Sec-WebSocket-Accept: %s\r\n\r\n' % digest | |
self.handshake_done = self.request.send(response) | |
def on_message(self, message): | |
print messsage | |
if __name__ == "__main__": | |
server = SocketServer.TCPServer( | |
("localhost", 9999), WebSocketsHandler) | |
server.serve_forever() |
I'm working on something using this as the websocket backend and the Webkit notification API :D
https://github.com/Mause/websocket-notifications/
You have a typo in your on_message method; you've got the letter S three times on line 63
def on_message(self, message):
print messsage # typo
You should change the serve_forever() part to something like this:
try:
server.serve_forever()
except KeyboardInterrupt:
print "Got ^C"
server.server_close();
print "bye!"
Awesome, thanks for sharing!
I am facing a weird problem. Only one client can be connected to this server. Am I missing something really basic ?
@Applterate Check this for multiple client support.
Very impressive. I am happy to be a witness to the excellence of this code.
im trying to use this with python3.. struggling with mimetools.Message can somebody give me a hint how to replace correct with the email package?
I'm totally noob with python and need to make something fast so pelase can you help me. How can i create instance of this class and use send_message() method from external file ?
self.request.send(126) should be self.request.send(chr(126)), same for 127.
You have a typo on line 63:
def on_message(self, message):
- print messsage
+ print message
Thanks for this nice example, which inspired (as in reuse some code) to make a combined HTTP/WebSocket server using one and the same port. The HTTP server part is based on the standard SimpleHTTPServer library.
See https://gist.github.com/SevenW/47be2f9ab74cac26bf21
and for more background:
http://www.sevenwatt.com/main/websocket-html-webserver-python/ for some background
Thank you @jkp that was very helpful. I've taken it one step further than @SevenW and turned it in to an git repository (https://github.com/mbirtwell/simple-websockets). I've got examples there of getting this to work with SimpleHTTPServer, wsgiref and werkzeug.
I have added support for SSL/HTTPS, and added exception handling. I updated my gist,
https://gist.github.com/SevenW/47be2f9ab74cac26bf21
and also created a repository for further maintenance:
https://github.com/SevenW/httpwebsockethandler
An elaborate example for the web interface for Plugwise-2-py can be found here:
https://github.com/SevenW/Plugwise-2-py
Thanks @jkp, that's lightweight and serves most development purposes. I recommend adding
SocketServer.TCPServer.allow_reuse_address = True
to set SO_REUSEADDR
socket option before TCPServer instantiation on line 66 in case the script is run repeatedly during development.
Here is a somewhat more modular version if anyone wants to use this as an import.
Hi, I trying launch the web socket server with param secure and I get this error:
File "main.py", line 50, in _ws_main
server.socket = ssl.wrap_socket (server.socket, certfile='./server.pem', server_side=True)
File "/usr/lib/python2.7/ssl.py", line 891, in wrap_socket
ciphers=ciphers)
File "/usr/lib/python2.7/ssl.py", line 509, in __init__
self._context.load_cert_chain(certfile, keyfile)
ssl.SSLError: [SSL] PEM lib (_ssl.c:2525)
I read that this error is caused for a bad private key, but I've added it as a parameter in the function wrap_socket() and result is the same. My cert and key are self-signed, I created it with openssl command.
How I can resolve this?
Thanks in advance
i need to send binary data over this server. will it work ?
I am facing a weird problem. Only one client can be connected to this server. Am I missing something really basic ?
To handle multiple client support:
At the beginning of the websocketserver-py file: I added:
import sys ###
import threading ###
from SocketServer import ThreadingMixIn ###
At the end of the websocketserver-py file: I added a class ThreadedWebSocketServer and adapted the 'if name == "main" ' part.
class ThreadedWebSocketServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
"""Handle requests in a separate thread."""
# Ctrl-C will cleanly kill all spawned threads
daemon_threads = True
# much faster rebinding
allow_reuse_address = True
def __init__(self, server_address, RequestHandlerClass):
SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass)
if name == "main":
##server = SocketServer.TCPServer(
## ("127.0.0.1", 8088), WebSocketsHandler)
server = ThreadedWebSocketServer(("127.0.0.1", 8088), WebSocketsHandler) ###
# terminate with Ctrl-C
try:
server.serve_forever()
except KeyboardInterrupt:
print ('========================================')
print ('^C received, shutting down server')
print ('========================================')
server.socket.close()
sys.exit(0)
Try it out with something like this (http://jsfiddle.net/ is handy for quick tests...):