popa.bogdanp
2018-11-9 16:53:33

AFAICT by reading its source, the web-server lib seems to support streaming, but I can’t get it to work. My request handler looks like this:


(define ((get-current-visitors cv) req)
  (response/output
   (lambda (out)
     (current-visitors-subscribe cv (current-thread))
     (let loop ()
       (write-bytes (string->bytes/utf-8 (number->string (thread-receive))) out)
       (flush-output out)
       (unless (port-closed? out)
         (loop))))))

Am I doing something obviously wrong here?


popa.bogdanp
2018-11-9 16:54:07

Requests stall until they time out and that’s when they finally print all the output at once.


popa.bogdanp
2018-11-9 16:54:29

I thought it might be a buffering issue which is why I threw that flush-output in there but I don’t think it should be necessary.


popa.bogdanp
2018-11-9 16:55:33

Output from curl:

$ curl -v <http://localhost:8000/v0/visitors>
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 8000 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
&gt; GET /v0/visitors HTTP/1.1
&gt; Host: localhost:8000
&gt; User-Agent: curl/7.54.0
&gt; Accept: */*
&gt;
* 60 seconds pass here *
&lt; HTTP/1.1 200 Okay
&lt; Date: Fri, 09 Nov 2018 16:51:59 GMT
&lt; Last-Modified: Fri, 09 Nov 2018 16:51:59 GMT
&lt; Server: Racket
&lt; Content-Type: text/html; charset=utf-8
&lt; Transfer-Encoding: chunked
&lt;
* transfer closed with outstanding read data remaining
* stopped the pause stream!
* Closing connection 0
curl: (18) transfer closed with outstanding read data remaining
00000000000000000000000000000000000000000000000000000000000

jaz
2018-11-9 16:56:14

You’re assuming the port will be closed at some iteration of the loop, but it’s not clear why.


popa.bogdanp
2018-11-9 16:56:39

The browser closes the connection (i.e. the user closes a tab or something)


popa.bogdanp
2018-11-9 17:04:27

It’s not clear from the curl output, but the response really only starts after about 60s, which is when curl seems to time out the request.


lexi.lambda
2018-11-9 17:10:02

Is it possible curl itself is buffering the output?


popa.bogdanp
2018-11-9 17:11:54

I don’t think so. The response headers aren’t returned until curl closes the connection either, from what I can tell.


popa.bogdanp
2018-11-9 17:14:25

Yeah, it looks like the server doesn’t even return the response headers immediately even though it should:

$ telnet 127.1 8000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /v0/visitors HTTP/1.1
Host: localhost


*nothing gets returned here*

popa.bogdanp
2018-11-9 17:20:59

Okay, I think there is some buffering going on inside the web server after all. If I append 4096 “a”’s to my output then the response gets streamed out.


popa.bogdanp
2018-11-9 17:27:38

https://github.com/racket/web-server/blob/master/web-server-lib/web-server/http/response.rkt#L111 looks like my handler is writing to a pipe, which, according to the docs, should not be buffered.


popa.bogdanp
2018-11-9 17:29:17

So I guess the data is being buffered on the OS socket, since the chunking loop (https://github.com/racket/web-server/blob/master/web-server-lib/web-server/http/response.rkt#L119) doesn’t explicitly flush after every chunk.


popa.bogdanp
2018-11-9 17:37:55

Yeah, patching that loop to (flush-output to-child) after every iteration fixes the problem.


popa.bogdanp
2018-11-9 18:11:36