fukamachi / caveman

Lightweight web application framework for Common Lisp.
http://8arrow.org/caveman/
776 stars 62 forks source link

Errors on 'long' post data #44

Closed lucashpandolfo closed 9 years ago

lucashpandolfo commented 9 years ago

First, some code. I made new project and added some routes:

(defroute "/form" ()
    "<html><head></head><body><form action='post' method='post'><input type='text' name='input' placeholder='input'></input></form></body></html>")

(defroute "/form-multi" ()
    "<html><head></head><body><form action='post' method='post' enctype='multipart/form-data'><input type='text' name='input' placeholder='input'></input></form></body></html>")

(defroute ("/post" :method :post) (&key _parsed)
  (setf (headers *response* :content-type) "text/plain")
  (with-output-to-string (s)
    (let ((value (cdr (assoc "input" _parsed :test #'string-equal))))
      (typecase value
        (null (princ value s))
        (list (let ((v (make-array 1024 :initial-element 0)))
                  (loop :for read := (read-sequence v (car value))
                     :while (> read 0)
                     :do (write-sequence (map 'string #'code-char v) s :end read))))
        (string (princ value s))
        (otherwise (describe value s)))
      s)))

Then started the app and in the browser (http://localhost:8080/form) and type some text in the input. Now, if the input has more than 1018 bytes the response I get is NIL. For example for the input

0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567

The response is the same string, but if a character is added, then i get a NIL for response.

Using enctype='multipart/form-data' if the input is

012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567

Everything is allright (978 bytes), then for each new byte at the end, one from the beginning is lost and bad things start happening. For a 1021 bytes input I get (in chromium, in firefox the result is different but alo garbage)

-----WebKitFormBoundaryzhAtSPXFQIvMMB1Z-- 345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890

I couldn't try using wookie or toot (see #43) but tried doing the same using only hunchentoot:

(hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 8080))

(hunchentoot:define-easy-handler (serve-form :uri "/form" :default-request-type :get) ()
  (setf (hunchentoot:content-type*) "text/html")
  "<html><head></head><body><form action='post' method='post'><input type='text' name='input' placeholder='input'></input></form></body></html>"
  )

(hunchentoot:define-easy-handler (serve-form-multi :uri "/form-multi" :default-request-type :get) ()
  (setf (hunchentoot:content-type*) "text/html")
  "<html><head></head><body><form action='post' method='post' enctype='multipart/form-data'><input type='text' name='input' placeholder='input'></input></form></body></html>"
  )

(hunchentoot:define-easy-handler (show-post :uri "/post" :default-request-type :post) ()
  (setf (hunchentoot:content-type*) "text/plain")
  (with-output-to-string (s)
    (print (post-parameter "input" hunchentoot:*request*) s)
    s))

But it worked flawlessly even for bigger (500k) inputs, so it may be a problem with caveman itself (clack maybe?)

fukamachi commented 9 years ago

I could reproduce the behaviour on my machine.

It seems this is a problem of HTTP-Body (or XSubseq), Clack is using for parsing a body stream since its v1.0 release.

fukamachi commented 9 years ago

I think the above commit fixed this. Please try it again with the latest HTTP-Body.

lucashpandolfo commented 9 years ago

It's working properly now. Thanks for the fast fixes.