http4s / blaze

Blazing fast NIO microframework and Http Parser

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Incorrect HTTP/1.1 501 Not Implemented log messge for websocket connection

mcenkar opened this issue · comments

Hi,

when connecting to BlazeWebSocketExample.scala if I change line 85 to:

.withHttpApp(org.http4s.server.middleware.Logger.httpApp(logHeaders = false, logBody = false)(routes.orNotFound))

then when I connect to WS instead of actual 101 Switching Protocols code:

image

in logs it prints:

14:45:18.748 [io-compute-3] INFO  org.http4s.server.middleware.Logger - HTTP/1.1 GET /ws?encoding=text
14:45:18.748 [io-compute-3] INFO  org.http4s.server.middleware.Logger - HTTP/1.1 501 Not Implemented

and also afterwards after disconnecting it prints incorrect Unknown type: Close or Unknown type: Close(Array(2)) (but this is example issue)

Ooh, this is tricky. An implementation detail is that websockets are implemented as an HTTP fallback response (the 501) with an attribute that contains the websocket. Backends that support web sockets look for this attribute and perform the connection upgrade instead of rendering the fallback response. But the Logger middleware doesn't know about any of this trickery.

We could:

  1. Special case web sockets in the logging middleware. (Yuck!)
  2. Send back web sockets as a 101 response, and figure out some other way to gracefully degrade them to a 501. (Yuck!)
  3. ???

/cc @ChristopherDavenport for logger expertise
/cc @RaasAhsan for recent work on web sockets in Ember

Yeah this is definitely a tricky one. This may just be a fundamental flaw in how http4s models middleware and web sockets. You see similar problems when isolated to middleware too. Imagine you position a logger middleware to execute before a retry middleware. Whenever an error happens, the logger is going to log the failed response, but then the retry middleware will throw that response away and perform the action again.

Obviously the solution for that example is to position the logger middleware after every other middleware, but it highlights the problem: middleware generally has no post facto context about what happens to a response. Similarly, any manipulation performed on a response by a backend will be hidden to a middleware

Another yucky solution to the problem is to expose backend-level middleware that runs right before a response is written to the socket. Its not composable and its backend-specific, but its general enough that there may be other use cases for it

Yes, I agree with Raas. A long time ago, I tried to create a general event stream to peer deeper into these things that happen in the backends, but the backends are by nature not very general, and the event system was not particularly functional. Our logging and metrics approach sacrifice a small slice of accuracy for general composition. Servlets have their own metrics and logging, but Ember and Blaze, our two most popular, don't.

It has never been my itch, because I've always had some sidecar or cloud gateway in front that does know the "post facto context." But that's a sorry answer.