

Tell HN: Fiddler complains about Hacker News - barrydahlberg

Minor issue but I was debugging something else with Fiddler and it kept telling me this about responses from HN:<p><i>Fiddler has detected a protocol violation in session #122.<p>The Server did not return properly formatted HTTP Headers. HTTP headers
should be terminated with CRLFCRLF. These were terminated with LFLF.</i><p>Presumably this is an issue in the Arc code outputting the headers. Also, is this where I should report this or is there a support email or something I can address?
======
jgrahamc
The problem appears to be in srv.arc where there's the following:

    
    
      (def gen-type-header (ctype)
        (+ "HTTP/1.0 200 OK
      Content-Type: "
           ctype
           "
      Connection: close"))
    

hexl-mode on the file shows that those line endings are 0x0A:

    
    
      00001630: 2874 6162 6c65 2929 0a0a 2864 6566 2067 (table))..(def g
      00001640: 656e 2d74 7970 652d 6865 6164 6572 2028  en-type-header (
      00001650: 6374 7970 6529 0a20 2028 2b20 2248 5454  ctype).  (+ "HTT
      00001660: 502f 312e 3020 3230 3020 4f4b 0a43 6f6e  P/1.0 200 OK.Con
      00001670: 7465 6e74 2d54 7970 653a 2022 0a20 2020  tent-Type: ".
      00001680: 2020 6374 7970 650a 2020 2020 2022 0a43    ctype.     ".C
      00001690: 6f6e 6e65 6374 696f 6e3a 2063 6c6f 7365  onnection: close
      000016a0: 2229 290a 0a28 6d61 7020 2866 6e20 2828  "))..(map (fn ((
    

Eventually the headers get printed to the socket via prn:

    
    
              (let filetype (static-filetype op)
                 (aif (and filetype (file-exists (string staticdir* op)))
                      (do (prn (type-header* filetype))
    

prn is defined in arc.arc

    
    
      (def pr args
        (map1 disp args)
        (car args))
    
      (def prn args
        (do1 (apply pr args)
             (pr #\newline))) ; writec doesn't implicitly flush
    

which is using disp which is defined in ac.scm

    
    
      (define (printwith f args)u  (let ((port (if (> (length args) 1)
                        (cadr args)
                        (current-output-port))))
          (when (pair? args)
            (f (ac-denil (car args)) port))
          (unless (ar-bflag 'explicit-flush)
            (flush-output port)))
        'nil)
    
      (defarc write (arc-write . args) (printwith write args))
      (xdef disp  (lambda args (printwith display args)))
    

That's using the MzScheme display method which appears to just do writing of
bytes and doesn't do any CRLF translation ([http://docs.racket-
lang.org/reference/Writing.html?q=display...](http://docs.racket-
lang.org/reference/Writing.html?q=display#\(def._\(\(quote._~23~25kernel\)._display\)\)))

Quick verification of that:

    
    
      $ cat test.scm
      (display "Hello, World!")
      (display #\return)
      (display  #\newline)
      $ mzscheme -r test.scm | hexdump -C
      00000000  48 65 6c 6c 6f 2c 20 57  6f 72 6c 64 21 0d 0a     |Hello, World!..|
    

So, there are a couple of possible solutions: explicitly specify CRLF as line
endings when needed (i.e. insert #\return), or have a special version of pr
for network communication that does LF -> CRLF translation).

Within the respond function there are other instances of using prn to send to
the socket that need to be fixed:

    
    
      (def respond (str op args cooks clen ctype in ip)
        (w/stdout str
          (iflet f (srvops* op)
                 (let req (inst 'request 'args args 'cooks cooks 'ctype ctype
                 'clen clen 'in in 'ip ip)
                   (if (redirector* op)
                       (do (prn rdheader*)
                           (prn "Location: " (f str req))
                           (prn))
                       (do (prn header*)
                           (awhen (max-age* op)
                             (prn "Cache-Control: max-age=" it))
                           (f str req))))
                 (let filetype (static-filetype op)
                   (aif (and filetype (file-exists (string staticdir* op)))
                        (do (prn (type-header* filetype))
                            (awhen static-max-age*
                              (prn "Cache-Control: max-age=" it)) 
                            (prn)
                            (w/infile i it
                              (whilet b (readb i)
                                (writeb b str))))
                        (respond-err str unknown-msg*))))))
    

I'm guessing that altering gen-type-header and creating a special prcrlf
function is the way to go.

    
    
      (def prcrlf args
        (do1 (apply pr args)
             (pr #\return)
             (pr #\newline)))
    
      (def gen-type-header (ctype)
        (+ "HTTP/1.0 200 OK#\return
      Content-Type: "
           ctype
           "#\return
      Connection: close"))
    

Although, personally, I don't like the assumption that the editor is inserting
0x0A for line-endings and would rather be totally certain with something like:

    
    
      (def gen-type-header (ctype)
        (+ "HTTP/1.0 200 OK"
           #\return #\newline
           "Content-Type: "
           ctype
           #\return #\newline
           "Connection: close"))
    

Since you'll need to do this sort of thing a lot a little helper would
probably make sense (and I'd add the CRLF on the last line explicitly):

    
    
      (def crlf (a)
        (+ a #\return #\newline))
    
      (def gen-type-header (ctype)
        (+ (crlf "HTTP/1.0 200 OK")
           (crlf (+ "Content-Type: " ctype))
           (crlf "Connection: close")))
    

And change every instance of prn in respond to prcrlf. Except for (prn (type-
header* filetype)) which becomes (pr (type-header* filetype)).

Note: I have not tested this change. Probably some other small gotchas.

------
plaes
<https://bugzilla.gnome.org/show_bug.cgi?id=571283> ;)

