WebSockets : Getting Safari to work with pywebsockets Apache extension - safari

I am trying to get WebSocket running on an Apache server with the help of pywebsocket.
The server is now setup and I am able to make a Websocket connection through Chrome. However, when I try to make a connection through Safari I am getting a "Unexpected response code: 404" and it doesn't appear that the WebSocket connection is able to be established with the server.
Any pointers here would be appreciated. Below is the client side JS code I am invoking to make a connection and the safari header tags vs the Chrome header tags.
function connect() {
if ('WebSocket' in window) {
socket = new WebSocket("ws://localhost/mystream");
} else if ('MozWebSocket' in window) {
socket = new MozWebSocket("ws://localhost/mystream");
} else {
return;
}
socket.onopen = function () {
showResult('Opened');
};
socket.onmessage = function (event) {
showResult(event.data);
};
socket.onerror = function () {
showResult('Error in connection');
};
socket.onclose = function (event) {
var logMessage = 'Closed (';
if ((arguments.length == 1) && ('CloseEvent' in window) && (event instanceof CloseEvent)) {
logMessage += 'wasClean = ' + event.wasClean;
if ('code' in event) {
logMessage += ', code = ' + event.code;
}
if ('reason' in event) {
logMessage += ', reason = ' + event.reason;
}
} else {
logMessage += 'CloseEvent is not available';
}
showResult(logMessage + ')');
};
showResult('Successfully Connected ');
}
Safari Headers :
Origin: http://192.168.1.8
Sec-WebSocket-Key1: 26 ~ 5 75G3 36< 0 U8T
Connection: Upgrade
Host: localhost
Sec-WebSocket-Key2: 1<A 9 4 4l865P5/6L5
Upgrade: WebSocket
Chrome Headers :
Connection:Upgrade
Host:localhost
Origin:http://192.168.1.8
Sec-WebSocket-Key:IAkX9XGWsCZHPQepzYjwxA==
Sec-WebSocket-Version:13
Upgrade:websocket
(Key3):00:00:00:00:00:00:00:00

Managed to get it working now. Safari (5.1) and mobile safari both require the Hixie-75 flag which has experimental support in pywebsockets. The issue was with the entry in the apache conf file, the entry is supposed to be in all lowercase (i.e on) but the sample entry had it in CamelCase (On) . Reverting to all lowercase has solved the issue.

Updated
Those Safari headers are for an older revision of the protocol: Hixie-76. Hixie-76 is a lot less friendly to integration with web servers because there is special data (key3) sent after the headers. I suspect Safari will be updated to the newer version of the protocol (HyBi) in the next release or two.
The HyBi-76 handshake happens in handshake/hybi00.py You might try adding some debug to try and figure out where it is failing. In particular make sure that _get_challenge is actually getting the final 8 bytes (key3) of the challenge that are sent after the headers (this is the part that makes it complicated to handle Hixie-76 in a web server).

Related

How to get API call origin in NextJS API endpoint

I have an API set up that receives a token, and I want to store that token in a database. But I also want to store the origin URL.
Let's say my API endpoint is located at https://myapp.com/api/connect
Now, I want to send a token from my website https://mywebsite.net
After I send a token, I want to be able to store the token and the website URL to the database in NextJS code.
My endpoint would store this info to the database:
{
token: someRandomToken
origin: https://mywebsite.net
}
I tried logging the whole req object from the handler to see if that info exist but the console log fills my terminal fast.
Inside Next's Server-Side environment you have access to req.headers.host as well as other headers set by Vercel's or other platforms' Reverse Proxies to tell the actual origin of the request, like this:
/pages/api/some-api-route.ts:
import { NextApiRequest } from "next";
const LOCAL_HOST_ADDRESS = "localhost:3000";
export default async function handler(req: NextApiRequest) {
let host = req.headers?.host || LOCAL_HOST_ADDRESS;
let protocol = /^localhost(:\d+)?$/.test(host) ? "http:" : "https:";
// If server sits behind reverse proxy/load balancer, get the "actual" host ...
if (
req.headers["x-forwarded-host"] &&
typeof req.headers["x-forwarded-host"] === "string"
) {
host = req.headers["x-forwarded-host"];
}
// ... and protocol:
if (
req.headers["x-forwarded-proto"] &&
typeof req.headers["x-forwarded-proto"] === "string"
) {
protocol = `${req.headers["x-forwarded-proto"]}:`;
}
let someRandomToken;
const yourTokenPayload = {
token: someRandomToken,
origin: protocol + "//" + host, // e.g. http://localhost:3000 or https://mywebsite.net
};
// [...]
}
Using Typescript is really helpful when digging for properties as in this case. I couldn't tell if you are using Typescript, but in case you don't, you'll have to remove NextApiRequest.

Blazor WebAssembly MQTT over websockets not working

I'm trying to implement an mqtt over websocket client subscriber in Blazor using Paho. The problem is it insists on using wss instead of ws and throws an ERR_SSL_PROTOCOL_ERROR error upon connection.
Here's a simplified code block:
var mqtt;
var host = "api.mydomainexample.com";
var port = 1884;
function onConnect(){
console.log("connected ! Now listening for messages ..");
mqtt.subscribe("someTopic");
}
function onFailure(message){
console.log("connection to host failed: " + message);
}
function onMessageArrived(msg){
var message = "Message received on topic '"+ msg.destinationName +"': "+ msg.payloadString;
console.log(message);
}
function mqttConnect() {
console.log("connecting to " + host + " ..");
mqtt = new Paho.MQTT.Client(host, port, clientid);
var options = {
timeout: 3,
onSuccess: onConnect,
onFailure: onFailure,
useSSL: false
};
mqtt.onMessageArrived = onMessageArrived;
mqtt.connect(options);
}
I copied this code into an html page created in notepad, called the function from the html body and ran the file in browser. It worked and subscribed well.
Also I added useSSL: false in the connection options although I didnt have it before but still didnt work.
here's the error I'm having from console:
WebSocket connection to 'wss://api.mydomainexample:1884/mqtt' failed: Error in connection establishment: net::ERR_SSL_PROTOCOL_ERROR
I also changed my projects launch settings so that it launches as http and not https because based on this answer, I cannot use a ws from a page loaded through https.
Any ideas ? Can't I just connect to a websocket without certificate in blazor?
Ok it turns out that when creating the blazor application, there is an option to 'configure on https' where this option causes requests redirection from http to https and consequently asks for secure wss instead of ws.
Hope this helps someone!

hapi 18 eventsourcing not working without stream.end()

Try to archive:
I try to use the HTML5 EventSourcing API https://developer.mozilla.org/de/docs/Web/API/EventSource to push events to my client application (javascript).
working example code with plain node http:
With a plain example node implementation it works perfectly and as expected. Example code: https://www.html5rocks.com/en/tutorials/eventsource/basics/
Problem:
When i try to integrate EventSourcing (or SSE) into my API endpoint which is based on hapi (currently using latest - 18.1.0) it does not work.
My route handler code mixed with some code i found:
const Stream = require('stream');
class ResponseStream extends Stream.PassThrough {
setCompressor (compressor) {
this._compressor = compressor;
}
}
const stream = new ResponseStream();
let data = 0;
setInterval(() => {
data++;
stream.write('event: message\n');
stream.write('data:' + data + '\n\n');
console.log('write data...', data);
// stream.end();
}, 1000);
return h
.response(stream)
.type('text/event-stream')
.header('Connection', 'keep-alive')
.header('Cache-Control', 'no-cache')
Findings:
I already searched and it seems since hapi 17.x there they exposed the flush method for the compressor < https://github.com/hapijs/hapi/issues/3658 >, section features.
But it still does not working.
They only way it sends a message is to uncomment the stream.end() line after sending the data. The problem obviously is that i cant send further data if i close the stream :/.
If i kill the server (with stream.end() line commented) the data gets transmitted to the client in a "single transmission". I think the problem is is still somewhere with the gzip buffering even when flushing the stream.
There are some code examples in the hapi github but i got none working with hapi 17 or 18 (all exmaples where hapi =< 16) :/
Someone know how to solve the problem or has a working EventSource example with latest hapi? I would kindly appreciate any help or suggestions.
Edit - Solution
The solution from the post below does work but i had also an nginx reverse proxy in front of my api endpoint it seems the main problem was not my code it was the nginx which had also buffered the eventsource messages.
To avoid this sort of problem add in your hapi: X-Accel-Buffering: no; and it works flawless
Well I just tested with Hapi 18.1.0 and managed to create a working example.
This is my handler code:
handler: async (request, h) => {
class ResponseStream extends Stream.PassThrough {
setCompressor(compressor) {
this._compressor = compressor;
}
}
const stream = new ResponseStream();
let data = 0;
setInterval(() => {
data++;
stream.write('event: message\n');
stream.write('data:' + data + '\n\n');
console.log('write data...', data);
stream._compressor.flush();
}, 1000);
return h.response(stream)
.type('text/event-stream')
}
and this is client code just to test
var evtSource = new EventSource("http://localhost/");
evtSource.onmessage = function(e) {
console.log("Data", + e.data);
};
evtSource.onerror = function(e) {
console.log("EventSource failed.", e);
};
These are the resources that where I found my way to working example
https://github.com/hapijs/hapi/blob/70f777bd2fbe6e2462847f05ee10a7206571e280/test/transmit.js#L1816
https://github.com/hapijs/hapi/issues/3599#issuecomment-485190525

Node.js http response end event?

I was using this piece of code to send some http requests and get data, and it was working fine. I recently updated apache and php to latest versions, as well as node.
And 'close' event stopped firing. I also tried 'end' and 'finish' none of this seems to be working.
I need to know when response is ended so I can start processing data, usually it comes in several chunks. Can you guys help?
var req = http.request(options, function(res) {
res.on('data', function (chunk) {
if(chunk != null && chunk != "") {
dataString += chunk; c
}
});
});
req.on('close', function () {
//tadaa it is finished, so we can process dataString
});
req.write(post_data);
req.end();
Current versions: Apache 2.4, PHP 5.4 node 0.10.9
Maybe there is some particular config settings of Apache that prevents it from closing connection?
P.S. I do not think it is Apache though.. I tried google.com with same result.. pretty strange... Anyone have a working code example? (load big data, and know when it ended)
You should be waiting for the end event of the response, not the request.
e.g.
res.on('end', function () {
// now I can process the data
});

Server Sent Event connection not staying open with Apache/Thin/Sinatra

I'm trying to setup basic server side event ability using Apache/Thin/Sinatra. Everything works as exepcted when I run the Thin server directly. When I run the Thin server through Apache using the RackBaseURI config setting, everything still works, but the connection is not persisted. It goes through a cycle of opening, writing some data to the browser and immediately closing. Seems like an Apache configuration issue?
I've gone through the Apache config and don't see anything that seems like it would prevent an open connection. Because I'm not sure where the error is, I don't want to post endless configuration data, so I can include more if I'm missing something...
sinatra (1.3.4),
thin (1.5.0),
Apache/2.2.22 (Ubuntu),
ruby 1.8.7
The JavaScript...
$j(function(){
console.log("Starting...");
var es = new EventSource("/stream_event");
es.addEventListener('message', function(e) {
console.log(e.data);
}, false);
es.addEventListener('open', function(e) {
console.log("Connection Open");
}, false);
es.addEventListener('error', function(e) {
console.log("error = " + e.eventPhase)
if (e.eventPhase == EventSource.CLOSED) {
console.log("Connection Closed");
}
}, false);
}
The server side sinatra/ruby..
set :server, :thin
connections = []
get '/' do
content_type 'text/event-stream'
stream(:keep_open) { |out|
connections << out
out << "data: hello\n\n"
}
end
get '/post_message' do
connections.each { |out| out << params[:message] << "\n" }
"message sent"
end
EDIT:
And here's the output I see in the browser console ...
Connection Open
hello
error = 2
Connection Closed
Connection Open
hello
error = 2
Connection Closed
Connection Open
hello
error = 2
Connection Closed
Connection Open
hello
error = 2
Connection Closed
Connection Open
hello
error = 2
Connection Closed
Seems to be something related to the RackBaseURI configuration setting. I was able to get things working by removing that attribute and directing the traffic to my Sinatra app using Apache's proxy abilities...
ProxyPass /stream_event http://127.0.0.1:9292
ProxyPassReverse /stream_event http://127.0.0.1:9292
The primary disadvantage here is that I need to startup and monitor the running Sinatra app manually using some other process.