Proxying websockets via web-server

55 views
Skip to first unread message

Dominik Pantůček

unread,
Jan 9, 2020, 5:23:36 PM1/9/20
to Racket Users
Hello everyone,

I am working on an application that uses React.js[1] for front-end and
Racket as HTTP back-end server.

For production builds, the Javascript part is compiled using "npm build"
which generates a directory full of HTML and Javascript files which are
then included in the Racket application during syntax stage as an
immutable hash. For development builds, the Racket application runs "npm
start" in the front-end source directory and proxies all non-backend
requests to the managed node.js server.

When run in the development setup (that is with the node.js secondary
HTTP server), the proxying using serve/servlet and simple dispatch-case
works like a charm - each servlet uses http-sendrecv to get the data
from the secondary HTTP server and returns it as appropriate response body.

But the reason for this setup is that React.js can automatically reload
the webpage, if any of the source files change. To do this trick, it
uses an url "/sockjs-node". The browser sends GET request for this
resource and the node.js server responds with "HTTP/1.1 101 Switching
Protocols" like in [2].

Apparently, to make this work, I need to establish a bi-directional
connection after the 101 response code. This is impossible with
serve/servlet. I have done some experiments with plain (serve #:dispatch
...) and just cannot make it work all at once. With connection-i-port
and connection-o-port it should be (relatively) easy to implement. But
the documentation is virtually nonexistent and browsing the
web-server/private/ sources is a bit tricky if I do not know what I am
looking for.

If anyone can give me a hint how a proper setup for websocket
implementation should look like, I would really appreciate it. Of
course, I also want to use the dispatch-case with plain requests as it
automates most of the real work I need to perform there.



Cheers,
Dominik

[1] https://reactjs.org/
[2] https://en.wikipedia.org/wiki/WebSocket#Protocol_handshake

Jay McCarthy

unread,
Jan 10, 2020, 9:18:57 PM1/10/20
to Dominik Pantůček, Racket Users
I don't completely understand what you want to do. Is there a reason
you can't use the WebSocket implementation ---
https://docs.racket-lang.org/rfc6455/index.html --- and then use
normal inter-Racket communication like channels and stuff to work with
the rest of your Web application?

Jay

--
Jay McCarthy
Associate Professor @ CS @ UMass Lowell
http://jeapostrophe.github.io
Vincit qui se vincit.
> --
> You received this message because you are subscribed to the Google Groups "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to racket-users...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/a3834699-4ed6-f337-1409-9f175636c087%40trustica.cz.

Eli Barzilay

unread,
Jan 11, 2020, 11:17:48 PM1/11/20
to Jay McCarthy, Dominik Pantůček, Racket Users
On Fri, Jan 10, 2020 at 9:18 PM Jay McCarthy <jay.mc...@gmail.com> wrote:
>
> I don't completely understand what you want to do. Is there a reason
> you can't use the WebSocket implementation ---
> https://docs.racket-lang.org/rfc6455/index.html --- and then use
> normal inter-Racket communication like channels and stuff to work with
> the rest of your Web application?

It would be nice if the documentation for that would have some
examples, but after playing with it for a bit it looks like it's
broken, possibly because I don't use it properly. I keep getting
errors that look like "read-request: malformed request ..." with
binary goo in the error message, which usually displayed like random
unicode, sometimes it breaks the terminal due to weird escape
sequences. Even if I'm not using it properly, such errors shouldn't
happen.

And as a side note, one of the errors I saw was:

exception raised by error display handler: format: ill-formed pattern string
explanation: tag `~l' not allowed
pattern string: "read-request: malformed request
...junk...~l...more..."; original exception raised: read-request: ...

which looks like a bad error re-raising in the web server code. A
quick grep finds at least one suspicious line which erroneously uses a
random string as a format string:

(with-handlers ([exn:fail? (lambda (exn) (network-error
'output-file (exn-message exn)))]) ...

--
((x=>x(x))(x=>x(x))) Eli Barzilay:
http://barzilay.org/ Maze is Life!

Dominik Pantůček

unread,
Jan 12, 2020, 11:18:56 AM1/12/20
to racket...@googlegroups.com
Sigh... I forgot to reply to the list... Here we go:

I want a single HTTP server like serve/servlet (or similar) that handles
all requests by the standard dispatcher/c logic - ideally using
dispatch/servlet with dispatch-case to specify the mapping. And for one
specific URL path "/sockjs-node", I need it to be able to do the
websocket HTTP 101 protocol switch.

The React.js application in the browser just communicates using HTTP and
if you run its minimal node.js server with "npm start" its HTTP
implementation contains the selective websocket URL path and it works. I
just need a way to proxy this, as my Racket backend is the HTTP server.
For static data I produce response/full with that data, for API calls,
it is response/jsexpr and for this websocket endpoint, I would like to
run copy-port and be done with it - after the protocol switch, that is.

From what I read about rfc6455 package, it cannot be used with the
web-server package. I'll dive into the source later today.

Actually I can live without that, yet I was curious as it provides a
convenient way to reload the frontend after source changes in its code
(that is the Javascript part). For production builds, a plain
serve/servlet does all the job required.


Dominik

Jay McCarthy

unread,
Jan 14, 2020, 8:09:57 PM1/14/20
to Eli Barzilay, Dominik Pantůček, Racket Users
Thanks for noticing that problem with the format string, Eli.

As far as examples, there are some in the github repository ---
https://github.com/tonyg/racket-rfc6455/tree/master/net/rfc6455/examples

--
Jay McCarthy
Associate Professor @ CS @ UMass Lowell
http://jeapostrophe.github.io
Vincit qui se vincit.

Jay McCarthy

unread,
Jan 14, 2020, 8:13:43 PM1/14/20
to Dominik Pantůček, Racket Users
Hi Dominik,

The cited package is definitely what you want, but you'll have to
crack open the source code a little bit to access the underlying
pieces. It currently uses all of the existing `web-server` libraries
and sets up a dispatcher chain for Websockets. You can see the entry
point here --- https://github.com/tonyg/racket-rfc6455/blob/master/net/rfc6455/server.rkt#L46
and https://github.com/tonyg/racket-rfc6455/blob/master/net/rfc6455/dispatcher.rkt#L9
--- so I think you just need to customize those things and attach them
to your own dispatcher chain.

Jay

--
Jay McCarthy
Associate Professor @ CS @ UMass Lowell
http://jeapostrophe.github.io
Vincit qui se vincit.

> To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/8222caa5-0104-a0b4-2e08-53ec731ac2bb%40trustica.cz.

Eli Barzilay

unread,
Jan 15, 2020, 12:46:07 AM1/15/20
to Jay McCarthy, Dominik Pantůček, Racket Users
On Tue, Jan 14, 2020 at 8:09 PM Jay McCarthy <jay.mc...@gmail.com> wrote:
>
> As far as examples, there are some in the github repository ---
> https://github.com/tonyg/racket-rfc6455/tree/master/net/rfc6455/examples

Yeah, I saw them, but they're all examples of just starting a
websocket server. What's missing (and it's not even clear to me if
it's possible without tweaking the library code) is some example that
shows what Dominick asked: serving some pages together with a
websocket. This is something that I suspect is the much more common
case than starting just a websocket-only server. (And I basically
"solved" it by starting another webserver on a different subdomain
just for this.)
Reply all
Reply to author
Forward
0 new messages