I'm doing a study on WebSocket protocol and trying to implement a simple ECHO service for now with Python on the backend.
It seems to work fine but the connection drops right after being established.
Here is my client:
<!doctype html>
<head>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
function Client()
{
//var ws = new WebSocket("ws://echo.websocket.org"); // this works fine
var ws = new WebSocket("ws://localhost:8000");
ws.onopen = function(e){ $("#response").append(">> Connected<br />"); }
ws.onclose = function(e){ $("#response").append(">> Disconnected<br />"); }
ws.onerror = function(e){ $("#response").append(">> ERROR: " + e.data + "<br />"); }
ws.onmessage = function(e){ $("#response").append("> " + e.data + "<br />"); }
this.sendCmd = function()
{
var message = $("#cmd").val();
$("#response").append(message + "<br />");
ws.send(message);
return false;
}
this.disconnect = function()
{
ws.close();
}
}
// onload
$(function() {
$("#response").append(">> Connecting<br />");
client = new Client();
$("#send").click(client.sendCmd);
$("#disconnect").click(client.disconnect);
});
</script>
</head>
<body>
<input type="text" name="cmd" id="cmd" /> | Send | Disconnect<br />
<hr />
<span id="response"></span>
</body>
</html>
Here is the server:
import SocketServer
import socket
from hashlib import sha1
from base64 import b64encode
PORT = 8000
MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
class Handler(SocketServer.BaseRequestHandler):
# incoming connection
def setup(self):
self.data = self.request.recv(1024).strip()
print "connection established", self.client_address
self.headers = self.headsToDict(self.data.split("\n"))
# incoming message
def handle(self):
# its a handshake
if "Upgrade" in self.headers and self.headers["Upgrade"] == "websocket":
key = self.headers["Sec-WebSocket-Key"]
accept = b64encode(sha1(key + MAGIC).hexdigest().decode('hex'))
response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" # "HTTP/1.1 101 Switching Protocols\r\n"
print "< HTTP/1.1 101 Web Socket Protocol Handshake" # "HTTP/1.1 101 Switching Protocols\r\n"
response += "Upgrade: websocket\r\n"
print "< Upgrade: websocket"
response += "Connection: Upgrade\r\n"
print "< Connection: Upgrade"
response += "Sec-WebSocket-Accept: "+accept+"\r\n\r\n"
print "< Sec-WebSocket-Accept: "+accept
self.request.send(response)
# its a normal message, echo it back
else:
print self.data
self.request.send(self.data)
# connection dropped
def finish(self):
print "connection lost", self.client_address
# convert a list of headers to a dictionary for convenience
def headsToDict(self, hdata):
rzygi = {}
for item in hdata:
print '>', item
item = item.split(':')
if len(item) > 1:
rzygi[item[0].strip()] = item[1].strip()
return rzygi
server = SocketServer.TCPServer(("", PORT), Handler)
server.socket_type = socket.SOCK_STREAM # didnt help
print "serving at port", PORT
try:
server.serve_forever()
except KeyboardInterrupt:
pass
server.server_close()
As mentioned, the connection is established successfully but then drops straight away, which makes me think the code is correct but there is something missing to keep the socket open. Here is the server output:
serving at port 8000
connection established ('127.0.0.1', 52633)
> GET / HTTP/1.1
> Upgrade: websocket
> Connection: Upgrade
> Host: localhost:8000
> Sec-WebSocket-Origin: http://localhost
> Sec-WebSocket-Key: qWGnhdFQ6l8Xs9awgQURfA==
> Sec-WebSocket-Version: 8
< HTTP/1.1 101 Web Socket Protocol Handshake
< Upgrade: websocket
< Connection: Upgrade
< Sec-WebSocket-Accept: fei4E4LQvPnf4y2ilebVsxRofvc=
connection lost ('127.0.0.1', 52633)
How do I keep the socket open?
edit: server code comments
The connection is closed each time after handle. You should rather stay there reading incoming data:
# incoming connection
def setup(self):
print "connection established", self.client_address
def handle(self):
while 1:
try:
self.data = self.request.recv(1024).strip()
# incoming message
self.headers = self.headsToDict(self.data.split("\r\n"))
# its a handshake
if "Upgrade" in self.headers and self.headers["Upgrade"] == "websocket":
key = self.headers["Sec-WebSocket-Key"]
accept = b64encode(sha1(key + 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: "+accept+"\r\n\r\n"
print response
self.request.send(response)
# its a normal message, echo it back
else:
print self.data
self.request.send(self.data)
except:
print "except"
break
Related
The problem:
I'm getting a 500 Internal Server Error when making an AJAX request, and am getting back this in the Chrome Console and I don't understand what I am doing wrong, I'm new to writing AJAX calls and handling them in PHP:
{"readyState":4,"responseText":"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>500 Internal Server Error</title>\n</head><body>\n<h1>Internal Server Error</h1>\n<p>The server encountered an internal error or\nmisconfiguration and was unable to complete\nyour request.</p>\n<p>Please contact the server administrator at \n webmaster#mywebdomain.com to inform them of the time this error occurred,\n and the actions you performed just before this error.</p>\n<p>More information about this error may be available\nin the server error log.</p>\n<p>Additionally, a 500 Internal Server Error\nerror was encountered while trying to use an ErrorDocument to handle the request.</p>\n<hr>\n<address>Apache/2.4.18 (Unix) OpenSSL/1.0.1e-fips mod_jk/1.2.37 mod_bwlimited/1.4 Server at mywebdomain.com Port 80</address>\n</body></html>\n","status":500,"statusText":"Internal Server Error"}
Here is the javascript (I would add that I find it odd that it seems to want 'http' when everything on our server is 'https', If I change to 'https', then it brings back 'No Access-Control-Allow-Origin is present'... nonsense, which isn' true, I specify that in the header, not sure if that's because this is the same domain):
$.ajax({
url: 'http://mywebdomain.com/admin/custom/modules/cac_customize_agent_comp/views/getID.php',
method: 'POST',
dataType: 'text',
data: {wnID: $("#wn_writing946b_number_ida").val(), pcgID: $("#aos_products_cac_customize_agent_comp_1aos_products_ida").val()}
})
.done(function(response) {
console.log("response");
$("#displayText").html(response);
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.log(JSON.stringify(jqXHR));
console.log("AJAX error: " + textStatus + ' : ' + errorThrown);
$("#displayText").html("There was a problem retrieving the records...");
})
.always(function() {
console.log("complete");
});
And here is the php file:
<?php
header('Access-Control-Allow-Origin: *');
header('content-type: text/html; charset: utf-8');
$wnID = $_POST['wnID'];
$pcgID = $_POST['pcgID'];
function getDefaultPercentage($wnID, $pcgID){
$sql = "SELECT wn_writing_number_cstm.title_c
FROM wn_writing_number_cac_customize_agent_comp_1_c
RIGHT OUTER JOIN wn_writing_number_cstm ON wn_writing_number_cac_customize_agent_comp_1_c.wn_writing946b_number_ida = wn_writing_number_cstm.id_c
WHERE wn_writing_number_cstm.id_c = '" . $wnID . "'";
$result = $GLOBALS['db']->query($sql);
while($row = $GLOBALS['db']->fetchByAssoc($result) ){
$titleWN = $row['title_c'];
} //end while
$sql = "SELECT pcg_product_comp_grid_cstm.title_c, pcg_product_comp_grid_cstm.percentage_c
FROM aos_products_pcg_product_comp_grid_1_c
LEFT OUTER JOIN pcg_product_comp_grid_cstm ON aos_products_pcg_product_comp_grid_1_c.aos_products_pcg_product_comp_grid_1pcg_product_comp_grid_idb = pcg_product_comp_grid_cstm.id_c
WHERE aos_products_pcg_product_comp_grid_1_c.aos_products_pcg_product_comp_grid_1aos_products_ida = '" . $pcgID . "'";
$result = $GLOBALS['db']->query($sql);
while($row = $GLOBALS['db']->fetchByAssoc($result) ){
$titlePCG = $row['title_c'];
$percentage = $row['percentage_c'];
} //end while
if($titlePCG == $titleWN){
$fullTitle = '';
switch ($titlePCG) {
case "TR":
$fullTitle = 'Trainee';
break;
case "SA":
$fullTitle = 'Sub-Agent';
break;
case "A":
$fullTitle = 'Agent';
break;
case "GA":
$fullTitle = 'General Agent';
break;
case "MGA":
$fullTitle = 'Managing General Agent';
break;
case "FMO":
$fullTitle = 'Field Marketing Organization';
break;
case "DM":
$fullTitle = 'District Manager';
break;
case "RVP":
$fullTitle = 'Regional Vice President';
break;
default:
"";
} //end switch
} //end if
if($titlePCG != '' && $titleWN != ''){
$textToOutput = $fullTitle . ": " . $percentage . "% is the default percentage.";
}
else {
$textToOutput = "There was a problem retrieving the records...";
}
return $textToOutput;
} //end getDefaultPercentage function
$textToOutput = getDefaultPercentage($wnID, $pcgID);
echo $textToOutput;
?>
Here is the php error I am getting from the error log:
[Tue Jun 14 14:25:37.752301 2016] [core:error] [pid 7823] [client XX.XX.XXX.XX:XXXXX] End of script output before headers: getID.php, referer: http://mywebdomain.com/admin/index.php
[Tue Jun 14 14:25:39.347480 2016] [:error] [pid 7822] [client XX.XX.XXX.XX:XXXXX] SoftException in Application.cpp:256: File "/home/mywebdomain/public_html/admin/custom/modules/cac_customize_agent_comp/views/getID.php" is writeable by group, referer: http://mywebdomain.com/admin/index.php
[Tue Jun 14 14:25:39.347557 2016] [core:error] [pid 7822] [client XX.XX.XXX.XX:XXXXX] End of script output before headers: getID.php, referer: http://mywebdomain.com/admin/index.php
The answer to my problem was a permissions issue.
I moved the file back to the core directory, and decided to forget trying to get permissions just right on every folder in between, and bam, worked like a charm.
Case in Point, when you are getting a 500 error from navigating directly to the actual url, permissions is a likely suspect.
Also, the headers I included were not needed.
There is some Syntax error in your code! That's why a 500 is shown.
Most probably you have to enable display_startup_errors=1 in the apache.conf or php.ini or do a ini_set();
P.S Look for some more blahblah_error=0 and change to blahblash_error=1 and you're done.
And don't forget to restart the apache too after doing any of these changes
I am trying to implement WebSocket communication between a client "web browser" and a Apache 2.4/PHP 5.6 server that is running on Windows 2008 R2.
My application is assume to be running on this URL "HTTPS"
https://myApllication.example.com
I am not sure if there is something I need to install on the server before this works or not.
My code so far looks like this
$(function(){
if(!("WebSocket" in window)){
console.log('your browser does not support WebSocket');
} else {
connect();
$('#disconnect').click(function(){
socket.close();
});
}
function connect(){
var socket;
var host = "ws://myApplication.example.com:8000/dev/icws/poll2.php";
try{
var socket = new WebSocket(host, {
protocolVersion: 13,
origin: 'https://www.example.com'
});
message('Socket Status: '+socket.readyState);
socket.onopen = function(){
message('Socket Status: '+socket.readyState+' (open)');
}
socket.onmessage = function(msg){
message('Received: ' + msg.data);
}
socket.onclose = function(){
message('Socket Status: '+ socket.readyState + ' (Closed)');
}
} catch(exception){
message(exception);
}
} //End connect()
function message(msg){
console.log(msg);
}//End message()
});
This code will give me this error in the console.
DOMException [SecurityError: "The operation is insecure."
code: 18
nsresult: 0x80530012
Here is how my poll2.php script looks like
<?php
header("Content-Type: text/event-stream" . PHP_EOL);
header("Cache-Control: no-cache" . PHP_EOL);
echo "data: {mykey: 'myvalue', eventName: 'eventName'}" . PHP_EOL . PHP_EOL;
?>
** Edited**
After changing the host from
var host = "ws://myApplication.example.com:8000/dev/icws/poll2.php";
to
var host = "wss://myApplication.example.com:8000/dev/icws/poll2.php";
I no longer get the security error but I get this in the console.
Socket Status: 0 (ready)
Socket Status: 3 (Closed)
Firefox can't establish a connection to the server at wss://myApplication.example.com:8000/dev/icws/poll2.php
What could be causing the connection not to be made?
Do I have to open the port 8000 on the server?
I am trying to send string/text data from browser client to python server and simply print it out. I have followed several examples on the internet, and all are the same: by using javascript
web_socket.send("text to be sent")
and (python)
data = web_socket.recv(1024)
print data
they receive what they want, what is clear and nice printout "text to be sent" on server site.
You can find my .html and .py below:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
<script src="jquery.js"></script>
<script type="application/javascript">
var ws;
function init() {
var servermsg = document.getElementById("servermsg");
ws = new WebSocket("ws://127.0.0.1:9877/");
ws.onopen = function(){
servermsg.innerHTML = servermsg.innerHTML + "<br>Server connected";
};
ws.onmessage = function(e){
servermsg.innerHTML = servermsg.innerHTML + "<br><< Recieved data: " + e.data;
};
ws.onclose = function(){
servermsg.innerHTML = servermsg.innerHTML + "<br>Server disconnected";
};
}
function postmsg(){
var text = document.getElementById("message").value;
ws.send(text);
servermsg.innerHTML = servermsg.innerHTML + "<br>>> Data sent: " + text;
}
//$(function(){
// var text = document.getElementById("message").value;
// ws.send(text);
// servermsg.innerHTML = servermsg.innerHTML + "<br>Sent: " + text;
//});
</script>
</head>
<body onload="init();">
<form action="" onSubmit="postmsg();return false;">
<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>
Server:
#!/usr/bin/env python
import socket
import threading
import struct
import hashlib
import base64
PORT = 9877
_address = ""
def create_handshake_resp(handshake):
final_line = ""
lines = handshake.splitlines()
for line in lines:
parts = line.partition(": ")
if parts[0] == "Sec-WebSocket-Key":
key = parts[2]
magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
accept_key = base64.b64encode(hashlib.sha1(key+magic).digest())
return (
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: WebSocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: " + accept_key + "\r\n\r\n")
def handle(s, addr):
data = s.recv(1024)
response = create_handshake_resp(data)
s.sendto(response, addr)
lock = threading.Lock()
while 1:
print "Waiting for data from", addr
data = s.recv(1024)
print "Done"
if not data:
print "No data"
break
print 'Data from', addr, ':', data
print 'Client closed:', addr
lock.acquire()
clients.remove(s)
lock.release()
s.close()
def start_server():
print 'STARTING SERVER...'
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', PORT))
s.listen(1)
print 'SERVER STARTED'
while 1:
conn, addr = s.accept()
print 'NEW CONNECTION ['+str(len(clients))+'], connected by ', addr
clients.append(conn)
threading.Thread(target = handle, args = (conn, addr)).start()
clients = []
start_server()
And server printout (when input was like "AA", or "ABC"):
STARTING SERVER...
SERVER STARTED
NEW CONNECTION [0], connected by ('127.0.0.1', 43877)
Waiting for data from ('127.0.0.1', 43877)
Done
Data from ('127.0.0.1', 43877) : ����w�q
Waiting for data from ('127.0.0.1', 43877)
Done
Data from ('127.0.0.1', 43877) : ��)B�h
Waiting for data from ('127.0.0.1', 43877)
I'm working on something similar myself. The Websocket protocol mandates that the client sends all its data using a mask. This is why you see 'garbage' - it's the masked text.
https://www.rfc-editor.org/rfc/rfc6455#section-5
"a client MUST mask all frames that it
sends to the server"
Read section 5 of the protocol and all will become clear. The browser (ie the client) is just implementing the protocol as it should (when you call ws.send). You need to do your bit.
Note also that when the sever sends data to the client it must NOT mask. But it still has to supply other info before the actual data (type, length etc).
To send a message from server side to websocket client you need to do as follows:
message = bytearray([0b10000001, len(original_msg)])
for byte in bytearray(original_msg):
message.append(byte)
See a stable server to client unidirectional socket library at https://github.com/westial/SingleSocket
The problem with the junk data was the javascript code sends the masked data and you must unmask it on the server side and the server sendes the unmasked data to client side.To solve this problem see my git-hub page
[][1]https://github.com/mohanbe/web-chat
I tried to implement my first websocket example but I cannot make it work.
I use a python webserver:
import threading
import socket
def start_server():
tick = 0
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 1234))
sock.listen(100)
while True:
print 'listening...'
csock, address = sock.accept()
tick+=1
print 'connection!'
handshake(csock, tick)
print 'handshaken'
while True:
interact(csock, tick)
tick+=1
def send_data(client, str):
#_write(request, '\x00' + message.encode('utf-8') + '\xff')
str = '\x00' + str.encode('utf-8') + '\xff'
return client.send(str)
def recv_data(client, count):
data = client.recv(count)
return data.decode('utf-8', 'ignore')
def handshake(client, tick):
our_handshake = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"+"Upgrade: WebSocket\r\n"+"Connection: Upgrade\r\n"+"WebSocket-Origin: http://localhost:8888\r\n"+"WebSocket-Location: "+" ws://localhost:1234/websession\r\n\r\n"
shake = recv_data(client, 255)
print shake
#We want to send this without any encoding
client.send(our_handshake)
def interact(client, tick):
data = recv_data(client, 255)
print 'got:%s' %(data)
send_data(client, "clock ! tick%d" % (tick))
send_data(client, "out ! %s" %(data))
if __name__ == '__main__':
start_server()
And HTML:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Web Socket Example</title>
<meta charset="UTF-8">
<script>
window.onload = function() {
var s = new WebSocket("ws://localhost:1234/");
s.onopen = function(e) { s.send('Ping'); }
s.onmessage = function(e) { alert("got: " + e.data); }
s.onclose = function(e) { alert("closed"); }
};
</script>
</head>
<body>
<div id="holder" style="width:600px; height:300px"></div>
</body>
</html>
When I point my browser to http://localhost/websocket.html over apache2
I got the following error:
python websocketserver.py
listening...
connection!
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: localhost:1234
Origin: http://localhost
Sec-WebSocket-Key: A4sVkUhjVlTZbJrp2NUrqg==
Sec-WebSocket-Version: 13
handshaken
got:
Traceback (most recent call last):
File "websocketserver.py", line 43, in <module>
start_server()
File "websocketserver.py", line 17, in start_server
interact(csock, tick)
File "websocketserver.py", line 40, in interact
send_data(client, "out ! %s" %(data))
File "websocketserver.py", line 24, in send_data
return client.send(str)
socket.error: [Errno 32] Broken pipe
Can someone help me to fix this?
Thanks
You're responding with the older Hixie 75 protocol but the client only speaks the newer HyBi/IETF RFC 6455 WebSocket protocol.
Your response to the handshake should look more like this (the accept value is calculated from the key value from the client):
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
In HyBi/6455, the frames are no longer delimited with \x00 and \xff. Instead there is a header to every frame that contains several pieces of data including frame type and payload length.
See the spec for more information. Or better yet, you could refer and/or use an existing python WebSocket implementation such as pywebsocket, tornado, or my own project websockify which contains websocket.py which is a generic websocket server lib.
javascript
$('#send').on('click', function() {
$.ajax({
'url': $('#url').val(),
'type': 'post',
'complete': function (jqXHR, textStatus) {
var msg = "Status: " + jqXHR.status + " (" + jqXHR.statusText + " - " + textStatus + ")<br />";
msg += jqXHR.getAllResponseHeaders().replace(/\n/g, "<br />");
$('#results').html(msg);
}
});
});
php
header("HTTP/1.0 200 Some message here");
flush();
exit();
Results
Status: 200 (OK - success)
Date: Wed, 07 Dec 2011 21:57:50 GMT
X-Powered-By: PHP/5.3.6
Transfer-Encoding: chunked
Connection: Keep-Alive
Server: Apache/2.2.17 (Unix) mod_ssl/2.2.17 OpenSSL/0.9.8r DAV/2 PHP/5.3.6
Content-Type: text/html
Keep-Alive: timeout=5, max=100
Question
How do I get the "Some message here" part of the header?
http
http protocol
6.1 Status-Line
The first line of a Response message is the Status-Line, consisting of
the protocol version followed by a numeric status code and its
associated textual phrase, with each element separated by SP
characters. No CR or LF is allowed except in the final CRLF sequence.
Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
Got it. It's jqXHR.statusText.
$.get("test.php").complete(function(jqXHR) {
console.log(jqXHR.statusText);
});
Just tried it out in Chrome with your exact PHP code.
Have you tried xhrobject.getResponseHeader() yet? jQuery docs say it's also available there. If you don't know the header's name, try getAllResponseHeaders().
Also, can you see that message in your browser's debugging console (network tab, connection headers)? If it's not there, it will hardly be available from js.