This is a question regarding handshaking in Websocket Protocol 76.
I have written a client and server, but am having trouble getting the client to accept the handshake. I can see that it is being returned, but the client immediately closes the connection. I'm guessing that my md5sum response must be incorrect.
As far as I can tell, I'm following the proper procedure, can anyone tell me what I am doing wrong?
def create_handshake_resp(handshake):
# parse request
final_line = ""
lines = handshake.splitlines()
for line in lines:
parts = line.partition(":")
if parts[0] == "Sec-WebSocket-Key1":
key1 = parts[2]
elif parts[0] == "Sec-WebSocket-Key2":
key2 = parts[2]
final_line = line
#concat the keys and encrypt
e = hashlib.md5()
e.update(parse_key(key1))
e.update(parse_key(key2))
e.update(final_line)
return "HTTP/1.1 101 WebSocket Protocol Handshake\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\nWebSocket-Origin: http://%s\r\nWebSocket-Location: ws://%s/\r\nWebSocket-Protocol: sample\r\n\r\n%s" % (httphost, sockethost, e.digest())
def parse_key(key):
spaces = -1
digits = ""
for c in key:
if c == " ":
spaces += 1
if is_number(c):
digits = digits + c
new_key = int(digits) / spaces
return str(new_key)
As you can see, I am performing what I think to be the correct operations on the keys (divide numbers by space count, concat results and the last line of the request and then MD5) and a 16 byte response is definitely being returned.
Any help would be much appreciated, and as soon as I have a working copy I will post it here.
Thanks.
EDIT:
Changed the headers to comply with kanaka's response. Handshake is still not being accepted by the client. I found out how to display the requests in Chromium, and this is the request and response being given:
(P) t=1291739663323 [st=3101] WEB_SOCKET_SEND_REQUEST_HEADERS
--> GET / HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: ---
Origin: http://---
Sec-WebSocket-Key1: 3E 203C 220 642;
Sec-WebSocket-Key2: Lg 590 ~5 703O G7 =%t 9
\x74\x66\xef\xab\x50\x60\x35\xc6\x0a
(P) t=1291739663324 [st=3102] SOCKET_STREAM_SENT
(P) t=1291739663348 [st=3126] SOCKET_STREAM_RECEIVED
(P) t=1291739663348 [st=3126] WEB_SOCKET_READ_RESPONSE_HEADERS
--> HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Origin: http://---
Sec-WebSocket-Location: ws://---/
Sec-WebSocket-Protocol: sample
\xe7\x6f\xb9\xcf\xae\x70\x57\x43\xc6\x20\x85\xe7\x39\x2e\x83\xec\x0
Ad verbatim, except I've removed the IP address for obvious reasons.
You have a couple problems that immediately jump out at me:
Here is how I calculate the response chksum in wsproxy (part of noVNC an HTML5 VNC client):
import struct, md5
...
spaces1 = key1.count(" ")
spaces2 = key2.count(" ")
num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1
num2 = int("".join([c for c in key2 if c.isdigit()])) / spaces2
return md5(struct.pack('>II8s', num1, num2, key3)).digest()
Here is a working example of a WebSocket client/server (client in Javascript, server in Python 2.6)
It used examples from various places (including kanaka's answer/noVNC, and this page and this page)
Works with Chrome 10.0.648.127, Safari 5.0.3 and MobileSafari on iPad from iOS 4.3
It's by no means well written code (the example HTML page is especially terrible) - use at your own risk and so on..
#!/usr/bin/env python
import socket
import threading
import struct
import hashlib
PORT = 9876
def create_handshake_resp(handshake):
final_line = ""
lines = handshake.splitlines()
for line in lines:
parts = line.partition(": ")
if parts[0] == "Sec-WebSocket-Key1":
key1 = parts[2]
elif parts[0] == "Sec-WebSocket-Key2":
key2 = parts[2]
elif parts[0] == "Host":
host = parts[2]
elif parts[0] == "Origin":
origin = parts[2]
final_line = line
spaces1 = key1.count(" ")
spaces2 = key2.count(" ")
num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1
num2 = int("".join([c for c in key2 if c.isdigit()])) / spaces2
token = hashlib.md5(struct.pack('>II8s', num1, num2, final_line)).digest()
return (
"HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
"Upgrade: WebSocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Origin: %s\r\n"
"Sec-WebSocket-Location: ws://%s/\r\n"
"\r\n"
"%s") % (
origin, host, token)
def handle(s, addr):
data = s.recv(1024)
s.send(create_handshake_resp(data))
lock = threading.Lock()
while 1:
print "Waiting for data from", s, addr
data = s.recv(1024)
print "Done"
if not data:
print "No data"
break
print 'Data from', addr, ':', data
# Broadcast received data to all clients
lock.acquire()
[conn.send(data) for conn in clients]
lock.release()
print 'Client closed:', addr
lock.acquire()
clients.remove(s)
lock.release()
s.close()
def start_server():
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', PORT))
s.listen(1)
while 1:
conn, addr = s.accept()
print 'Connected by', addr
clients.append(conn)
threading.Thread(target = handle, args = (conn, addr)).start()
clients = []
start_server()
Also, a crappy example HTML page to show it working:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
<script type="application/javascript">
var ws;
function init() {
var servermsg = document.getElementById("servermsg");
ws = new WebSocket("ws://localhost:9876/");
ws.onopen = function(){
servermsg.innerHTML = servermsg.innerHTML + "<br>Server connected";
servermsg.innerHTML = servermsg.innerHTML + "<br>Sending message to server";
ws.send("Hello Mr. Server!");
};
ws.onmessage = function(e){
servermsg.innerHTML = servermsg.innerHTML + "<br>Recieved data: " + e.data;
};
ws.onclose = function(){
console.log("Server disconnected");
servermsg.innerHTML = servermsg.innerHTML + "<br>Connected";
};
}
function postmsg(){
var text = document.getElementById("message").value;
ws.send(text);
servermsg.innerHTML = servermsg.innerHTML + "<br>Sent: " + text;
return false;
}
</script>
</head>
<body onload="init();">
<form action="" onSubmit="postmsg()">
<input type="text" name="message" value="" id="message">
<input type="submit" name="submit" value="" id="submit">
</form>
<div id="servermsg"><h1>Message log:</h1></div>
</body>
</html>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With