Project

General

Profile

[NoResponse] Proxy websocket (how to proxy the node red gui)

Added by andreas almost 6 years ago

Hello all,

First a big thanks to all the devs of lighttpd, awesome work!

I am trying to deploy the node-red gui via the lighttpd server.
Lighty has quite a few tasks in my setup:
- deploying websites
- redirecting http to https
- proxy requests to a node js backend server (express)
- deploying the node red gui (hosted by express)
- deploying the node red dashboard (hosted by express)

Everything works so far despite the websocket comunication of the dashboard and the gui of node red when the proxy is active.

Here is my setup:

OS:Debian Stretch 9.4
Lighty: lighttpd/1.4.45 (ssl) Build-Date: Jan 14 2017 21:07:19
lighttpd.conf :

server.modules = (
    "mod_access",
    "mod_alias",
    "mod_compress",
    "mod_redirect",
    "mod_proxy" 
)

server.document-root        = "/var/www/html" 
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log" 
server.pid-file             = "/var/run/lighttpd.pid" 
server.username             = "www-data" 
server.groupname            = "www-data" 
server.port                 = 80

index-file.names            = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny             = ( "~", ".inc" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )

compress.cache-dir          = "/var/cache/lighttpd/compress/" 
compress.filetype           = ( "application/javascript", "text/css", "text/html", "text/plain" )

# default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
include_shell "/usr/share/lighttpd/create-mime.assign.pl" 
include_shell "/usr/share/lighttpd/include-conf-enabled.pl" 

$SERVER["socket"] == ":443" {
ssl.engine = "enable" 
ssl.pemfile = "/etc/lighttpd/certs/lighttpd.pem" 
}

$HTTP["scheme"] == "http" {
$HTTP["host"] =~ ".*" {
url.redirect = (".*" => "https://%0$0")
}
}

#Node
$HTTP["host"] =~ ".*" { 
proxy.server = ( "" => (( "host" => "127.0.0.1", "port" => 8000)))
proxy.header = ( "upgrade" => "enable" )
}

My express server:

var http = require('http');
var express = require("express");
var RED = require("node-red");
var path = require("path");

// Create an Express app
var app = express();

// Add a simple route for static content served from 'public'
app.use("/",express.static("public"));

// Create a server
var server = http.createServer(app);

// Create the settings object - see default settings.js file for other options
var settings = {
    httpAdminRoot:"/red",
    httpNodeRoot: "/api",
    userDir:"/home/nol/.nodered/",
    functionGlobalContext: { },    // enables global context
    editorTheme: {
        page: {
            title: "" 
        },
        header: {
            title: " " 
        }
    }
};

// Initialise the runtime with a server and settings
RED.init(server,settings);

// Serve the editor UI from /red
app.use(settings.httpAdminRoot,RED.httpAdmin);

// Serve the http nodes UI from /api
app.use(settings.httpNodeRoot,RED.httpNode);

// routes to serve the static HTML files
app.use(express.static(__dirname +'/'));

server.listen(8000);

// Start the runtime
RED.start();

I can access the node red dashboard under http://192.168.80.163/api/ui/
and the node red gui under https://192.168.80.163/red
Everything displays correctly but the websocket requests return error codes:

Gui:

Request URL: wss://192.168.80.163/red/comms
Request Method: GET
Status Code: 404 Not Found

Dashboard:

Request URL: wss://192.168.80.163/api/ui/socket.io/?EIO=3&transport=websocket&sid=VfgHCGXTGIzsJ1fwAAAF
Request Method: GET
Status Code: 400 Bad Request

If I bypass lighty and access everything directly with the ip and port (e.g. http://192.168.80.163:8000/api/ui/)
everything works fine including the websocket requests.
When I compare the two requests (proxy vs no proxy) they look like this:

Proxy:
wss://192.168.80.163/api/ui/socket.io/?EIO=3&transport=websocket&sid=z5P20xqPzt5LHK0VAAAH
No Proxy:
ws://192.168.80.163:8000/api/ui/socket.io/?EIO=3&transport=websocket&sid=cpd4eBbZri3kUHByAAAG

I am a bit of a noob with this so every bit of help is appreciated.

Thanks in advance!


Replies (5)

RE: Proxy websocket (how to proxy the node red gui) - Added by gstrauss almost 6 years ago

I do not know why your client is making direct wss:// or ws:// requests instead of https:// or http:// requests with Connection: upgrade and Upgrade: websocket headers. Please look into that and check the links that you are creating in your response. Are they https:// or wss://?

.

BTW:

$HTTP["host"] =~ ".*" { 
proxy.server = ( "" => (( "host" => "127.0.0.1", "port" => 8000)))
proxy.header = ( "upgrade" => "enable" )
}

is more clearly stated as:
proxy.server = ( "" => (( "host" => "127.0.0.1", "port" => 8000)))
proxy.header = ( "upgrade" => "enable" )

RE: Proxy websocket (how to proxy the node red gui) - Added by gstrauss almost 6 years ago

You probably want to (temporarily) set the following in your lighttpd.conf:

debug.log-request-handling = "enable" 
debug.log-request-header = "enable" 
debug.log-request-header-on-error = "enable" 

and take a look at what lighttpd sees as the request and which lighttpd handler picks up the request.

RE: Proxy websocket (how to proxy the node red gui) - Added by andreas almost 6 years ago

Hey,
Thanks for the reply.
The client which makes the ws/wss calls is the node-red gui, so I have no influence on this behavior.
Is it generally possible to get this working while node-red makes the direct ws/wss calls?

BTW:

$HTTP["host"] =~ ".*" { 
proxy.server = ( "" => (( "host" => "127.0.0.1", "port" => 8000)))
proxy.header = ( "upgrade" => "enable" )
}

is more clearly stated as:
proxy.server = ( "" => (( "host" => "127.0.0.1", "port" => 8000)))
proxy.header = ( "upgrade" => "enable" )

Is this because >> $HTTP["host"] =~ ".*" << applies to all possible urls so I can omit it?

RE: Proxy websocket (how to proxy the node red gui) - Added by gstrauss almost 6 years ago

Is it generally possible to get this working while node-red makes the direct ws/wss calls?

My understanding is that this is not correct behavior. Clients which see ws:// or wss:// should make http:// or https:// requests with Connection: upgrade and Upgrade: websocket .

https://www.websocket.org/quantum.html
"To establish a WebSocket connection, the client and server upgrade from the HTTP protocol to the WebSocket protocol during their initial handshake"

https://fetch.spec.whatwg.org/#concept-websocket-establish
"To establish a WebSocket connection, given a url, protocols, and client, run these steps:

Let requestURL be a copy of url, with its scheme set to "http", if url’s scheme is "ws", and to "https" otherwise.

This change of scheme is essential to integrate well with fetching. E.g., HSTS would not work without it. There is no real reason for WebSocket to have distinct schemes, it’s a legacy artefact. [HSTS]
[...]"

.

Is this because >> $HTTP["host"] =~ ".*" << applies to all possible urls so I can omit it?

Yes.

RE: Proxy websocket (how to proxy the node red gui) - Added by gstrauss almost 6 years ago

Everything works so far despite the websocket comunication of the dashboard and the gui of node red when the proxy is active.

You're probably misleading me with your post of the URLs used by node-red.

Please enable debugging in lighttpd and post (or add attachment with) the headers that lighttpd sees.

debug.log-request-header = "enable" 
debug.log-request-header-on-error = "enable" 
debug.log-response-header = "enable" 

    (1-5/5)