Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Tell HN: Fiddler complains about Hacker News
42 points by barrydahlberg on Nov 9, 2010 | hide | past | favorite | 2 comments
Minor issue but I was debugging something else with Fiddler and it kept telling me this about responses from HN:

Fiddler has detected a protocol violation in session #122.

The Server did not return properly formatted HTTP Headers. HTTP headers should be terminated with CRLFCRLF. These were terminated with LFLF.

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?



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...)

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.





Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: