I ran a websocket server using socket.io:
var http = require('http'),
fs = require('fs'),
// NEVER use a Sync function except at start-up!
index = fs.readFileSync(__dirname + '/index.html');
// Send index.html to all requests
var app = http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(index);
});
// Socket.io server listens to our app
var io = require('socket.io').listen(app);
// Send current time to all connected clients
function sendTime() {
io.emit('time', { time: new Date().toJSON() });
}
// Send current time every 10 secs
setInterval(sendTime, 10000);
// Emit welcome message on connection
io.on('connection', function(socket) {
// Use socket to communicate with this particular client only, sending it it's own id
socket.emit('welcome', { message: 'Welcome!', id: socket.id });
socket.on('i am client', console.log);
});
app.listen(3000);
This server works fine withe a simple socket-io client:
var socket = require('socket.io-client')('http://127.0.0.1:1337');
socket.on('connect', function(){});
socket.on('time', function(data){console.log(data);});
socket.on('disconnect', function(){});
I tried to use cURL to send the connection request but it failed:
curl --verbose -i -N -H "Upgrade: websocket" -H "Connection: Upgrade" -H "Host: 127.0.0.1" -H "Origin: http://127.0.0.1" http://127.0.0.1:3000/
* Connected to 127.0.0.1 (127.0.0.1) port 3000 (#0)
> GET /socket.io HTTP/1.1
> User-Agent: curl/7.37.1
> Accept: */*
> Upgrade: websocket
> Connection: Upgrade
> Host: 127.0.0.1
> Origin: http://127.0.0.1
>
* Empty reply from server
* Connection #0 to host 127.0.0.1 left intact
curl: (52) Empty reply from server
If I removed the "Upgrade: websocket", then the server will send back the html page. The "Upgrade: websocket" is supposed to tell the server to upgrade the HTTP connection to websocket connection. Why it didn't work?
Related
I tried to convert the socket IO getting started example to https like this:
const fs = require('fs');
const app = require('https').createServer({
key: fs.readFileSync("privkey.pem"),
cert: fs.readFileSync("cert.pem"),
ca: fs.readFileSync("fullchain.pem"),
}, handler)
const io = require('socket.io')(app);
app.listen(443);
function handler (req, res) {
res.writeHead(200);
res.end();
}
io.on('connection', (socket) => {
socket.emit('news', { hello: 'world' });
socket.on('my other event', (data) => {
console.log(data);
});
});
But running the following in my browser does not work:
const ws = new WebSocket("wss://example.com");
But I received the following error:
Firefox can’t establish a connection to the server at wss://example.com/.
I tried to debug this by running this curl command:
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Host: example.com" -H "Origin: https://example.com " https://example.com
and the result was:
curl: (52) Empty reply from server
None of the console logging code is reached and there are no errors on the node script while doing all of this.
Why can't I connect to my websocket server?
According to doc:
Socket.IO is NOT a WebSocket implementation. Although Socket.IO indeed uses WebSocket as a transport when possible, it adds some metadata to each packet: the packet type, the namespace and the packet id when a message acknowledgement is needed. That is why a WebSocket client will not be able to successfully connect to a Socket.IO server, and a Socket.IO client will not be able to connect to a WebSocket server either.
So this is not correct:
const ws = new WebSocket("wss://example.com");
You should use:
const socket = io('https://your-server-address');
I have the following javascript app that receives data from GCDWebServer that runs on an OSX app.
var HttpClient = function() {
this.get = function(aUrl, aCallback) {
var anHttpRequest = new XMLHttpRequest();
anHttpRequest.open( "GET", aUrl, true );
anHttpRequest.onreadystatechange = function() {
if (anHttpRequest.readyState == 4 && anHttpRequest.status == 200) {
aCallback(anHttpRequest.responseText);
document.write (anHttpRequest.responseText);
}
else {
document.write ("Not Ok: ReadyState=" + anHttpRequest.readyState + " Status= " + anHttpRequest.status);
}
}
anHttpRequest.send( null );
}
}
var client = new HttpClient();
client.get('http://xxx.xxx.x.xx:8080?var=param', function(response) {
document.write ("Success");
});
The Cocoa app runs the following code:
[webServer addDefaultHandlerForMethod:#"GET"
requestClass:[GCDWebServerRequest class]
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithJSONObject:dict];
completionBlock(response);
});
I always get an anHttpRequest.readyState = 4 and anHttpRequest.status = 0. The anHttpRequest.responseText is empty.
However, when I open any browser and type http://xxx.xxx.x.xx:8080?var=param I get the correct results. So the server is sending the data properly.
Here is the log from GCDWebServer
[DEBUG] Did open connection on socket 13
[DEBUG] Did connect
[DEBUG] Connection received 312 bytes on socket 13
[DEBUG] Connection on socket 13 preflighting request "GET /" with 312 bytes body
[DEBUG] Connection on socket 13 processing request "GET /" with 312 bytes body
[DEBUG] Connection sent 175 bytes on socket 13
[DEBUG] Connection sent 107 bytes on socket 13
[DEBUG] Did close connection on socket 13
[VERBOSE] [xxx.xxx.x.xx:8080] xxx.xxx.x.xx:52401 200 "GET /" (312 | 282)
[DEBUG] Did disconnect
Any ideas?
Thanks in advance.
This isn't an issue with your GCDWebServer but is simply a CORS issue. If you make the same request against your server from something like curl then you will see the correct 200 HTTP status. To get the correct response from a browser you will need to have your server send the following header:
Access-Control-Allow-Origin: *
This tells the browser that it is allowed to pass back the full response it gets from your server to the JavaScript initiating the request. Without that response header the browser essentially drops all the data it received and should print out an error message in your browser's console.
Jersey 2.1.4, Java 8, Tomcat 8, Firefox 38.0.1
Server:
#GET
#Produces(SseFeature.SERVER_SENT_EVENTS)
public EventOutput listenToBroadcast() {
final EventOutput eventOutput = new EventOutput();
this.broadcaster.add(eventOutput);
return eventOutput;
}
Client:
var source = new EventSource('broadcast');
source.addEventListener('event', function(event) {
alert('event');
}, false);
source.onopen = function() {
alert('connection open');
};
Using Firefox, the connection open alert does not show up on page load.
Firefox shows the following error in the console: Firefox can't establish a connection to the server at http://localhost:8080/broadcast.
The onopen function DOES get called when the first event comes in. And in that case, only the onopen function is called, and not the event listener.
Chrome is working properly. Also, this demo is working with Firefox properly.
On page load, and before the server sends an event, The Network tab in Firefox shows that it received OK 200 for the /broadcast SSE endpoint, but no headers are present. Jersey log shows the following for the connection establishment:
o.glassfish.jersey.filter.LoggingFilter : 11 * Server has received a request on thread http-nio-8080-exec-3
11 > GET http://localhost:8080/broadcast
11 > accept: text/event-stream
11 > accept-encoding: gzip, deflate
11 > accept-language: en-US,en;q=0.5
11 > cache-control: no-cache
11 > connection: keep-alive
11 > host: localhost:8080
11 > pragma: no-cache
11 > referer: http://localhost:8080/test_sse.html
11 > user-agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0
11 * Server responded with a response on thread http-nio-8080-exec-3
11 < 200
11 < Content-Type: text/event-stream
My client was waiting on the EventSource.onOpen event after creating the EventSource connection (new EventSource()). Chrome invokes the onOpen callback right after the connection is open, but Firefox would only invoke it when the first event is sent from the server. To work around this, I am sending a comment event right after the server opens up the SSE connection. Firefox gets this event, which is meaningless, and calls the onOpen function.
Here is my server-side client subscription code:
#GET
#Produces(SseFeature.SERVER_SENT_EVENTS)
public EventOutput listenToBroadcast() {
final EventOutput eventOutput = new EventOutput();
this.broadcaster.add(eventOutput);
// firefox doesn't call the EventSource.onOpen callback when the connection is created, but it requires at least one event to be sent, so a
// meaningless comment event is used
OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder();
OutboundEvent event = eventBuilder.name("event")
.comment("")
.build();
broadcaster.broadcast(event);
return eventOutput;
}
However, FF still show an error on the console: The connection to http://localhost:8080/broadcast was interrupted while the page was loading.
You can see the error showing up using this demo
Probably a known bug
I am having issues with making an ExtJS AJAX request to the Nodejs server between two different domains within our network and will appreciate any help. Response fails when attempting from both http and https from ExtJS client side but a Curl from my local via http returns 200 OK with proper data. We are working with content type application/json.
ExtJS onReady function has enabled CORS:
Ext.onReady(function () {
Ext.Ajax.cors = true;
Ext.Ajax.useDefaultXhrHeader = false;
... (code removed)
})
A test from my ExtJS client side on a known working URL that will properly create the ExtJS datastore (brings back 200 OK):
Ext.create('Ext.data.Store', {
id : 'countryStore',
model : 'country',
autoLoad : true,
autoDestroy: true,
proxy: {
type: 'rest',
url : 'https://restcountries.eu/rest/v1/all',
},
reader: {
type : 'json',
headers: {'Accept': 'application/json'},
totalProperty : 'total',
successProperty: 'success',
messageProperty: 'message'
}
});
However, when attempting a request to our Nodejs server via
http:
Ext.create('Ext.data.Store', {
id : 'circuits',
model : 'circuit',
autoLoad : true,
autoDestroy: true,
proxy: {
type: 'rest',
url : 'http://ourNodeJsServerDomain:5500/v3/circuits',
},
reader: {
type : 'json',
headers: {'Accept': 'application/json'},
totalProperty : 'total',
successProperty: 'success',
messageProperty: 'message'
}
});
returns the following in Chrome's console:
Mixed Content: The page at 'https://ourExtJsDevClientSide' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://ourNodeJsServerDomain:5500/v3/circuits?_dc=1430149427032&page=1&start=0&limit=50'. This request has been blocked; the content must be served over HTTPS.
Now, when attempted over https:
Firefox shows:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://ourNodeJsServerDomain:5500/v3/circuits?_dc=1430151516741&page=1&start=0&limit=50. This can be fixed by moving the resource to the same domain or enabling CORS.
and the Request Header doesn't show "application/json", is this an issue?:
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
Host
ourNodeJsServerDomain:5500
Origin
https://ourExtJsDevClientSide
Referer
https://ourExtJsDevClientSide
User-Agent
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:37.0) Gecko/20100101 Firefox/37.0
I then tried with Curl to see what the responses were to help debug
on http gives a 200 OK response but Access-Control-Allow-Origin is undefined even when we are defining it as "*":
curl http://ourNodeJsServerDomain:5500/v3circuits?_limit=1 -v
> GET /v3/circuits?_limit=1 HTTP/1.1
> User-Agent: curl/7.37.1
> Host: ourNodeJsServerDomain:5500
> Accept: */*
>
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Access-Control-Allow-Origin: undefined
< Access-Control-Allow-Methods: GET
< Access-Control-Allow-Headers: Content-Type
< Content-Type: application/json; charset=utf-8
< Content-Length: 1126
< ETag: W/"MlbRIlFPCV6w7+PmPoVYiA=="
< Date: Mon, 27 Apr 2015 16:24:18 GMT
< Connection: keep-alive
<
[
{ *good json data returned here* } ]
then when I attempt to Curl via https
curl https://ourNodeJsServerDomain:5500/v3/circuits?_limit=1 -v
* Server aborted the SSL handshake
* Closing connection 0
curl: (35) Server aborted the SSL handshake
We have enabled CORS on our Nodejs server:
router
.all('*', function(req, res, next){
res.setHeader('Content-Type', 'application/json');
// res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
// console.log('\n\nreq.headers.origin ===================== ' + req.headers.origin);
//I have tried allowing all * via res.SetHeader and res.header and neither is defining the Access-Control-Allow-Origin properly when curling
//res.setHeader("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Origin", "*");
// res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Methods', 'GET');
res.header('Access-Control-Allow-Headers', 'Content-Type');
I have attempted to be detailed in my thought process and I am willing to try new ways to determine how to understand and solve this.
* SOLUTION *
The issue is mixed content from the browser. Our client UI is on https (secure) whereas we were requesting http (unsecure) content from the nodejs server. We needed to allow for our nodejs server to run on https
We generated SSL certifications and implemented them onto our nodejs server.
Within the nodejs code, we enabled CORS with the CORS module and are running both http and https servers:
// enable CORS for all requests
var cors = require('cors');
app.use(cors());
// for certifications
var credentials = {
key: fs.readFileSync('our.key'),
cert: fs.readFileSync('our.crt')
};
var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);
httpServer.listen(port, function() {
console.log('HTTP server listening on port ' + port);
});
httpsServer.listen(httpsPort, function() {
console.log('HTTPS server listening on port ' + httpsPort);
});
There seems to be issues with both CORS and HTTPS in your server... You should try this middleware for the CORS part, and make it work when accessing the page in raw HTTP first. As far as I know, you'll have to use different ports for HTTP and HTTPS. And you will also probably need to enable CORS credentials. As I said, I think you'd better make it work in HTTP first ;)
Then, on the Ext part, as already mentioned in comments, you should probably disable default headers (or you'll have to make all of them accepted by your server; see the first comment to this answer). But you need to do that on the proxy, because apparently it replaces the global setting in Ext.Ajax.
So, something like this:
Ext.create('Ext.data.Store', {
id : 'countryStore',
model : 'country',
autoLoad : true,
autoDestroy: true,
proxy: {
type: 'rest',
url : 'https://restcountries.eu/rest/v1/all',
useDefaultXhrHeader: false, // <= HERE
reader: {
type : 'json',
headers: {'Accept': 'application/json'},
totalProperty : 'total',
successProperty: 'success',
messageProperty: 'message'
}
} // <= and notice this change
});
Probably unrelated, but note that your indendation was incorrect and hid the fact that the reader option was applied to the store itself instead of the proxy (so it was ignored).
I would like to keep the websocket connection alive.
I am using RFC 6455 protocol as websocket protocol.
I tested this via Firefox 13 and Chrome 20.
===WEB SOCKET CLIENT===
GET / HTTP/1.1
Host: #.#.#.#:#
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/20100101 Firefox/13.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ko-kr,ko;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive, Upgrade
Sec-WebSocket-Version: 13
Origin: http://its.secret.com
Sec-WebSocket-Key: TbJqTlFtGocvRvk/9stMhg==
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
===WEB SOCKET SERVER===
HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: outIACgIETcILj0NjzA0MhwP7uc=
the newline in this websocket protocol is CR/LF.
the next is the part of websocket client source.
var host = "ws://#.#.#.#:#";
try {
Rsocket = new WebSocket(host);
Rsocket.onopen = function(msg) {
alert("Welcome - status " + this.readyState);
};
Rsocket.onmessage = function(msg) {...
};
Rsocket.onclose = function(msg) {
alert("Disconnected - status " + this.readyState);
};
}
catch (ex) {
log(ex);
}
First, the success alert appears in the web browser.
Just after pressing OK, the disconnect alert appears.
Although pressing OK when the success alert appears, I would like the disconnect alert not to appear.