I'm writing my own script to connect to a websocket server with JavaScript using the WebSocket API. I'm having problems with the connection closing straight away.
Here's the client side script:
var host = 'ws://localhost:8080';
try
{
debug.add('Connection request submitted for ' + host);
socket = new WebSocket(host);
debug.readyStateListener();
debug.add('Socket request started');
socket.onopen = function()
{
debug.add('Connection opened');
}
socket.onmessage = function(message)
{
debug.add('data received ' + message.data);
}
socket.onclose = function()
{
debug.add('Connection closed');
}
}
catch(e)
{
debug.add('WebSockets error ' + e.toString() );
}
This is the debug I receive:
Connection request submitted for ws://localhost:8080
socket readyState change to 0
Socket request started
socket readyState change to 3
Connection closed
The debug.readyStateListener() polls socket.readyState for changes. What's happening is it changes to 0 meaning the connection is opening, then straight away changes to 3 that the connection has been closed.
The server receives the connection fine but the connection is then closed straight away by the client.
I've tried it in Opera 11 with WebSockets enabled and in the latest version of Chrome. Both time's I get the same result.
I can communicate perfectly with the server through a raw connection, or simply by visiting http://localhost:8080/ in my browser this is the result:
GET / HTTP/1.1
User-Agent: Opera/9.80 (Windows NT 6.1; U; IBM EVV/3.0/EAK01AG9/LE; en) Presto/2.9.168 Version/11.51
Host: localhost:8080
Accept: application/xhtml+voice+xml;version=1.2, application/x-xhtml+voice+xml;version=1.2, text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: nl-NL,nl;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
The connection also stays alive without any flaws by sending a request through http until I end it.
Going through the JavaScript WebSocket API the server receives this request:
GET / HTTP/1.1
Host: localhost:8080
Origin: http://localhost
Upgrade: WebSocket
Sec-WebSocket-key1: L58(b Q]'9 4 9\ 0 *+ 6 a4
Connection: Upgrade
Sec-WebSocket-Key2: \+ 1 5d/9541840N*4
My last guess would be Connection: upgrade or Upgrade: WebSocket not being supported properly by the client. To me it would be more logical to receive Connection: keep-alive but I have no idea how to reslove this.
Related
I hope this is not a duplicate...
I am trying to POST user email & password to a php file and it seems that the php file isn't getting those values.
The js code:
function ReceiveLoginData() {
let text = this.responseText;
console.log(text);
let json_data = JSON.parse(
text.substring(1, text.length - 1).replaceAll("\\u0022", "\"")
);
// there is a lot more code... but its irrelevant.
}
function SubmitLogin() {
var email_addr = document.getElementsByClassName("login-email")[0].value;
var passwd = document.getElementsByClassName("login-passwd")[0].value;
var req = new XMLHttpRequest();
req.onload = ReceiveLoginData;
// req.onreadystatechange = ReceiveLoginData; // does not work...
req.open("POST", "/users/auth/login.php"); // ...,true); or ...,false); fail too...
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
let data_to_send = "uemail=" +
window.encodeURIComponent(email_addr) +
"&upasswd=" +
window.encodeURIComponent(passwd);
// data_to_send = "uemail="+email_addr ... works neither
req.send(data_to_send);
}
PHP (actually its location is localhost:4000/users/auth/login.php)
<?php
$uemail = $_POST["uemail"];
$upasswd = $_POST["upasswd"];
$login_err = true;
// set it to false otherwise
function SendData(string $str)
{
echo json_encode($str, JSON_HEX_QUOT | JSON_HEX_APOS);
}
function main_fn()
{
$uemail = strtolower($uemail);
if (strlen($uemail) == 0) {
SendData("[\"noemail\"]");
}
// and much more but again irrelevant...
}
main_fn();
?>
I learnt that using window.encodeURIComponent(...) is safer from here: https://stackoverflow.com/a/17382629/18243229
but neither of the ways work.
Whatever I got to know after literal 5 hours of debugging and getting fed up(I blame my noviceness):
The PHP form is being executed. ReceiveLoginData function prints ["noemail"] whenever the submit button is pressed
The Network debugging tab in chrome's dev tools shows that connection is established with php file.
Some information which might just be useful:
Response Headers (source):
HTTP/1.1 200 OK
Host: localhost:4000
Date: Sun, 18 Sep 2022 16:59:49 GMT
Connection: close
X-Powered-By: PHP/8.1.10
Content-type: text/html; charset=UTF-8
Request Headers (source):
POST /users/auth/login.php HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 31
Content-type: application/x-www-form-urlencoded
Host: localhost:4000
Origin: http://localhost:4000
Referer: http://localhost:4000/users/auth/auth.html?
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
sec-ch-ua: "Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
Payload: (source | URL encoded)
uemail=email%40gmail.com&upasswd=1234
uemail: email%40gmail.com
upasswd: 1234
Response:
"[\u0022noemail\u0022]"
What else I did...
I didn't waste those 5 hours on this project...
I tried to remake a smaller project with the same mechanism and the same js code calling a PHP file and voila, the php file got the values posted to it...
Everything "seems" correct according to my knowledge but why does PHP not get the $_POST values?
Also, I'm currently focusing on Google Chrome and am on Linux (ig that makes no difference...)
From the code you have posted i can spot one problem.
the $uemail = $_POST["uemail"]; is in the global scope and the code inside the main_fn function is trying to use that variable but that variable is not available in that scope because it is only available in the global scope. So it seems to me you need to pass them as arguments to get them into the functions scope.
Changeing the function definition
from: function main_fn()
to: function main_fn($uemail, $upasswd)
and calling it
with: main_fn($uemail, $upasswd);
instead of: main_fn();
should do the trick
Hope this helps :-)
We have to implement WebSocket connection with third-party WebSocket server. But they are using Authorization header for establishing a connection between WebSocket client and WebSocket server. But I could not find any solution to add Authorization header in WebSocket client during connection.
Please help me to resolve this issue.
This is my code I am using
var ws = new WebSocketClient();
var headers ={Authorization: 'token'}
ws.on('connectFailed', function(error) {
console.log('Connect Error: ' + error.toString());
});
ws.on('connect', function(connection) {
console.log('WebSocket Client Connected');
});
ws.connect('wss://url', '',headers);
Response is like this
Server responded with a non-101 status: 400 Bad Request
Response Headers Follow:
server: nginx/1.11.7
date: Thu, 11 Jul 2019 19:49:58 GMT
content-type: text/html
content-length: 31
connection: keep-alive
When I am trying to receive a message from Socket.IO using the native socket module, instead of receiving the message, I receive this:
GET /socket.io/?EIO=3&transport=polling&t=MIlsTQ_ HTTP/1.1
Host: localhost:5000
Connection: keep-alive
Accept: */*
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
DNT: 1
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
How do I fix this, and receive the message rather than the data?
Client code:
socketsSend: function() {
const socket = io.connect('http://localhost:5000');
/*var socket = io.Socket('http://localhost', {
port: 5000
});*/
socket.connect();
socket._connectTimer = setTimeout(function() {
socket.close();
}, 500);
socket.on('connect', function() {
// socket connected successfully, clear the timer
clearTimeout(socket._connectTimer);
});
Server code:
import socket
import json
addr = 'localhost',5000
sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(addr)
sock.listen(3)
while True:
connection, client_address = sock.accept()
data = connection.recv(100000)
print(data.decode('utf-8'))
From this how to guide:
In general, they [socket's recv and send functions] return when the associated network buffers have been filled (send) or
emptied (recv). They then tell you how many bytes they handled. It is your
responsibility to call them again until your message has been completely dealt with.
So, from my understanding, connection.recv(100000) won't return any data until its buffer fills up, so to fix this you would need to set buffer to something low, (maybe 2048?).
response is JSON
Edge: sometimes inserts response property value into DOM briefly then removes it, sometimes logs error "SCRIPT5: access denied" (indicating CORS), response fully accessible from Debugger, request shown in network tab
Chrome: response empty string, request not shown in network tab, no console message
Firefox: console error 'response "malformed JSON"' on breakpoint line using response in JSON.parse(), thus before usage, request not shown in network tab, Firebug and integrated
JS (current browsers only):
var session = "";
var request;
function checkLogin()
{
if(request.readyState > 3)
{
var response = JSON.parse(request.response);
if(verify(response)) // verify inserts argument property "error" in DOM on error via innerHTML on element
{
// do something
}
}
}
function login()
{
request = new XMLHttpRequest();
request.onreadystatechange = checkLogin;
request.open("GET", "authenticateUser.php?user=" + document.getElementById("user").value + "&credential="+md5(document.getElementById("password").value));
request.send();
}
Edge request from network tab:
Anforderungs-URL: http://*MYDOMAIN*/authenticateUser.php?user=df&credential=d41d8cd98f00b204e9800998ecf8427e
Anforderungsmethode: GET
Statuscode: 200 / OK
- Anforderungsheader
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: de-DE, de; q=0.8, en-US; q=0.5, en; q=0.3
Connection: Keep-Alive
Host: *MYDOMAIN*
Referer: http://*MYDOMAIN*/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586
- Antwortheader
Connection: Keep-Alive
Content-Length: 65
Content-Type: application/json
Date: Sat, 21 Nov 2015 07:42:17 GMT
Keep-Alive: timeout=15, max=94
Server: Apache
X-Powered-By: PHP/5.5.29
response:
{"success":false,"error":"authentication failed or unauthorised"}
What is going to make my phps' response be usable in JS code (in Chrome and Firefox) and its properties value be insertable into the DOM?
login() is the handler of onsubmit. I didn't return false; thus the form from which the user and pw came from - having no action - submitted to its own page and made the browser reload the page: cancelling the request in Chrome and Ff. Only Edge let the equal page JS handle the repsonse from the former loads request - until it was to be inserted.
Took me 6 hours and a detour through JSONP and Charles then listing the network traffic from Chrome where the page itself appeared after the request thus letting me have the idea that the page "reloaded" and finally remembering onsubmits handler cancels the submit with return false and else submits leading to a load of the action (or self if none) which is what happened here.
I am trying to create a simple WebSocket connection in JavaScript against my Rails app. I get the following:
WebSocket connection to 'ws://localhost:4000/' failed: Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing
What am I doing wrong? Here is my code:
JavaScript:
var socket = new WebSocket('ws://localhost:4000');
socket.onopen = function() {
var handshake =
"GET / HTTP/1.1\n" +
"Host: localhost\n" +
"Upgrade: websocket\n" +
"Connection: Upgrade\n" +
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\n" +
"Sec-WebSocket-Protocol: quote\n" +
"Sec-WebSocket-Version: 13\n" +
"Origin: http://localhost\n";
socket.send(handshake);
};
socket.onmessage = function(data) {
console.log(data);
};
Ruby:
require 'rubygems'
require 'em-websocket-server'
module QuoteService
class WebSocket < EventMachine::WebSocket::Server
def on_connect
handshake_response = "HTTP/1.1 101 Switching Protocols\n"
handshake_response << "Upgrade: websocket\n"
handshake_response << "Connection: Upgrade\n"
handshake_response << "Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=\n"
handshake_response << "Sec-WebSocket-Protocol: quote\n"
send_message(handshake_response)
end
def on_receive(data)
puts 'RECEIVED: ' + data
end
end
end
EventMachine.run do
print 'Starting WebSocket server...'
EventMachine.start_server '0.0.0.0', 4000, QuoteService::WebSocket
puts 'running'
end
The handshake headers are per Wikipedia.
I think that once the connection is open the request and response have already occurred, so sending headers at that point is too late. In addition, headers have to end with a blank line, which you omitted.
According to the demos, you don't even have to set headers in the client or the server--the ruby module automatically takes care of the headers on the server side, and html5 automatically takes care of the headers on the client side. I think this should work:
require "em-websocket-server"
class EchoServer < EM::WebSocket::Server
def on_connect
EM::WebSocket::Log.debug "Connected"
puts "I felt a connection."
end
def on_receive msg
puts "RECEIVED: #{msg}"
send_message msg
end
end
EM.run do
myhost = "0.0.0.0"
myport = 8000
puts "Starting WebSocket server. Listening on port #{myport}..."
EM.start_server myhost, myport, EchoServer
end
html file:
<!DOCTYPE html> <html> <head><title>Test</title>
<script type="text/javascript">
var myWebSocket = new WebSocket("ws://localhost:8000");
myWebSocket.onopen = function(evt) {
console.log("Connection open. Sending message...");
myWebSocket.send("Hello WebSockets!"); };
myWebSocket.onmessage = function(evt) {
console.log(evt.data);
myWebSocket.close(); };
myWebSocket.onclose = function(evt) {
console.log("Connection closed."); };
myWebSocket.onerror = function(err) {
alert(err.name + " => " + err.message); } </script>
</head> <body> <div>Hello</div> </body> </html>
And it does work in Safari 5.1.9 (which is an older browser): I see the expected output on both the server and the client. However, the code does not work in Firefox 21: I get the error message...
Firefox can't establish a connection to the server at ws://localhost:8000/.
var myWebSocket = new WebSocket("ws://localhost:8000");
I notice that in both Firebug and Safari Developer Tools, the server does not send a Sec-WebSocket-Accept header:
Response Headers
Connection Upgrade
Upgrade WebSocket
WebSocket-Location ws://localhost:8000/
WebSocket-Origin null
Request Headers
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Cache-Control no-cache
Connection keep-alive, Upgrade
DNT 1
Host localhost:8000
Origin null
Pragma no-cache
Sec-WebSocket-Key r9xT+ywe533EHF09wxelkg==
Sec-WebSocket-Version 13
Upgrade websocket
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:21.0) Gecko/20100101 Firefox/21.0
Nothing I tried would make the code work in Firefox 21.0. To check whether Firefox 21.0 even supports websockets, I went to:
http://www.websocket.org/echo.html
and it said my browser does support websockets.
Is there any reason you have to use the em-websocket-server module? The last modification for that module on github was three years ago. And whenever you see require rubygems in ruby code, that should alert you that the code is old. I tried the newer em-websocket module, and I was able to successfully transfer data back and forth using websockets on both Firefox 21.0 and Safari 5.1.9:
require 'em-websocket'
myhost = "0.0.0.0"
myport = 8000
EM.run {
puts "Listening on port #{myport}..."
EM::WebSocket.run(:host => myhost, :port => myport, :debug => false) do |ws|
ws.onopen do |handshake|
path = handshake.path
query_str = handshake.query
origin = handshake.origin
puts "WebSocket opened:"
puts "\t path \t\t -> #{path}"
puts "\t query_str \t -> #{query_str}"
puts "\t origin \t -> #{origin}"
end
ws.onmessage { |msg|
ws.send "Pong: #{msg}"
}
ws.onclose {
puts "WebSocket closed"
}
ws.onerror { |e|
puts "Error: #{e.message}"
}
end
}
Same client side code. Now the response headers include Sec-WebSocket-Accept:
Response Headers
Connection Upgrade
Sec-WebSocket-Accept LyIm6d+kAAqkcTR744tVK9HMepY=
Upgrade websocket
Request Headers
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Cache-Control no-cache
Connection keep-alive, Upgrade
DNT 1
Host localhost:8000
Origin null
Pragma no-cache
Sec-WebSocket-Key pbK8lFHQAF+arl9tFvHn/Q==
Sec-WebSocket-Version 13
Upgrade websocket
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:21.0) Gecko/20100101 Firefox/21.0
In your code, I don't think you are setting any headers. Instead, you are just sending messages back and forth that happen to contain characters that look like headers. Apparently, your browser requires the Sec-WebSocket-Accept header in the response before it will allow the connection, and when the em-websocket-server module fails to set that header in the response, your browser refuses the connection.
The relevant source code for em-websockets-server looks like this:
module EM
module WebSocket
module Protocol
module Version76
# generate protocol 76 compatible response headers
def response
response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
response << "Upgrade: WebSocket\r\n"
response << "Connection: Upgrade\r\n"
response << "Sec-WebSocket-Origin: #{origin}\r\n"
response << "Sec-WebSocket-Location: #{scheme}://#{host}#{path}\r\n"
if protocol
response << "Sec-WebSocket-Protocol: #{protocol}\r\n"
end
response << "\r\n"
response << Digest::MD5.digest(keyset)
response
end
As you can see, it doesn't set the Sec-WebSocket-Accept header. That code is in a module called Version76, and searching google for websockets version 76 yields an obsolete protocol(which contains an example of a request and response):
https://datatracker.ietf.org/doc/html/draft-hixie-thewebsocketprotocol-76
Here is the current websockets protocol(which also contains an example of a request and response):
https://www.rfc-editor.org/rfc/rfc6455
Conclusion: em-websockets-server is obsolete.