[racket] webserver : "chunked tail no NL" error in varnish

From: Guillaume Coré (g at fridim.org)
Date: Tue Mar 11 00:42:57 EDT 2014

Hi,

I'm using the racket webserver and I want to put a varnish directly in
front of it.

With the default config of varnish (just changing the host/port for the
backend to match my racket application) I got an error :

 FetchError   c chunked tail no NL

I think this refers to the following code in varnish :

http://goo.gl/Nr0IMv   (github varnish/Varnish-Cache)

This suggests a '\n' is missing after a '\r'.

Here is the racket code I use for the backend (first example in the
documentation):

#lang web-server/insta
(define (start request)
  (response/xexpr
     '(html
            (head (title "My Blog"))
                 (body (h1 "Under construction")))))


Here is the varnishlog :

13 BackendOpen  b default 127.0.0.1 40991 127.0.0.1 35281
   13 BackendXID   b 1949592090
   13 TxRequest    b GET
   13 TxURL        b /servlets/standalone.rkt
   13 TxProtocol   b HTTP/1.1
   13 TxHeader     b User-Agent: curl/7.34.0
   13 TxHeader     b Host: localhost
   13 TxHeader     b Accept: */*
   13 TxHeader     b X-Forwarded-For: ::1
   13 TxHeader     b X-Varnish: 1949592090
   13 TxHeader     b Accept-Encoding: gzip
   13 RxProtocol   b HTTP/1.1
   13 RxStatus     b 200
   13 RxResponse   b Okay
   13 RxHeader     b Date: Tue, 11 Mar 2014 03:58:33 GMT
   13 RxHeader     b Last-Modified: Tue, 11 Mar 2014 03:58:33 GMT
   13 RxHeader     b Server: Racket
   13 RxHeader     b Content-Type: text/html; charset=utf-8
   13 RxHeader     b Transfer-Encoding: chunked
   13 Fetch_Body   b 3(chunked) cls -1 mklen 1
   13 BackendClose b default
   11 SessionOpen  c ::1 41944 [::]:80
   11 ReqStart     c ::1 41944 1949592090
   11 RxRequest    c GET
   11 RxURL        c /servlets/standalone.rkt
   11 RxProtocol   c HTTP/1.1
   11 RxHeader     c User-Agent: curl/7.34.0
   11 RxHeader     c Host: localhost
   11 RxHeader     c Accept: */*
   11 VCL_call     c recv lookup
   11 VCL_call     c hash
   11 Hash         c /servlets/standalone.rkt
   11 Hash         c localhost
   11 VCL_return   c hash
   11 VCL_call     c miss fetch
   11 Backend      c 13 default default
   11 TTL          c 1949592090 RFC 120 -1 -1 1394510313 0 1394510313 0 0
   11 VCL_call     c fetch deliver
   11 ObjProtocol  c HTTP/1.1
   11 ObjResponse  c Okay
   11 ObjHeader    c Date: Tue, 11 Mar 2014 03:58:33 GMT
   11 ObjHeader    c Last-Modified: Tue, 11 Mar 2014 03:58:33 GMT
   11 ObjHeader    c Server: Racket
   11 ObjHeader    c Content-Type: text/html; charset=utf-8
   11 FetchError   c chunked tail no NL
   11 VCL_call     c error deliver
   11 VCL_call     c deliver deliver
   11 TxProtocol   c HTTP/1.1
   11 TxStatus     c 503
   11 TxResponse   c Service Unavailable
   11 TxHeader     c Server: Varnish
   11 TxHeader     c Content-Type: text/html; charset=utf-8
   11 TxHeader     c Retry-After: 5
   11 TxHeader     c Content-Length: 419
   11 TxHeader     c Accept-Ranges: bytes
   11 TxHeader     c Date: Tue, 11 Mar 2014 03:58:33 GMT
   11 TxHeader     c X-Varnish: 1949592090
   11 TxHeader     c Age: 0
   11 TxHeader     c Via: 1.1 varnish
   11 TxHeader     c Connection: close
   11 Length       c 419
   11 ReqEnd       c 1949592090 1394510313.440603495
1394510313.442929506 0.000077248 0.002285957 0.000040054
   11 SessionClose c error
   11 StatSess     c ::1 41944 0 1 1 0 0 0 257 419


Note: this racket code works fine as a backend for nginx and apache2.
^^^^^

I'm not an HTTP expert and I wonder if it is possible that varnish is
more « strict » on the protocol ? Is racket-webserver doing something
wrong or simply using a feature varnish does not support ?


The code in webserver-lib seems alright though :

(define (output-response-body/chunked conn bresp)
  (define-values (from-servlet to-chunker) (make-pipe))
  (define to-client (connection-o-port conn))
  (define to-chunker-t
    (thread (λ ()
              ((response-output bresp) to-chunker)
              (close-output-port to-chunker))))
  (define buffer (make-bytes 1024))
  (define total-size
    (let loop ([total-size 0])
      (define bytes-read-or-eof
        (read-bytes-avail! buffer from-servlet))
      (if (eof-object? bytes-read-or-eof)
        total-size
        (begin
          (fprintf to-client "~a\r\n" (number->string bytes-read-or-eof 16))
          (write-bytes buffer to-client 0 bytes-read-or-eof)
          (fprintf to-client "\r\n")
          (loop (+ total-size bytes-read-or-eof))))))
  (thread-wait to-chunker-t)
  (fprintf to-client "0\r\n")
  (print-headers
   to-client
   (list (header #"Content-Length"
                 (string->bytes/utf-8 (number->string total-size)))))
  (flush-output to-client))


Will appreciate any enlightenment :)

Thanks,
Guillaume



-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 815 bytes
Desc: OpenPGP digital signature
URL: <http://lists.racket-lang.org/users/archive/attachments/20140311/a01b0aae/attachment.sig>

Posted on the users mailing list.