Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 76 additions & 78 deletions http.carp
Original file line number Diff line number Diff line change
Expand Up @@ -282,48 +282,47 @@ it will return a `(Success Request)`.")
; Parse headers
(while (< pos len)
(let [eol (String.find-crlf txt pos len)]
(if (= eol -1)
(cond
(= eol -1)
(do
(set! body (String.byte-slice txt pos len))
(break))
(if (= eol pos)
; Empty line = end of headers, rest is body
(do
(let [body-start (+ pos 2)]
(when (< body-start len)
(set! body
(String.byte-slice txt
body-start
len))))
(break))
; Parse header line
(let [line &(String.byte-slice txt pos eol)
colon (String.index-of line \:)]
(if (= colon -1)
(do (set! failed @line) (break))
(let-do [k &(String.byte-slice line
0
colon)
lk &(String.ascii-to-lower k)
v &(String.trim
&(String.byte-slice line
(Int.inc colon)
(String.length line)))]
(if (= lk "cookie")
(match (Cookie.parse-many v)
(Result.Error err)
(do (set! failed err) (break))
(Result.Success arr)
(set! cookies
(Array.concat &[cookies arr])))
(set! headers
(Map.update-with-default headers
k
&(fn [h]
(Array.push-back h
@v))
[])))
(set! pos (+ eol 2)))))))))
(= eol pos)
; Empty line = end of headers, rest is body
(do
(let [body-start (+ pos 2)]
(when (< body-start len)
(set! body
(String.byte-slice txt
body-start
len))))
(break))
; Parse header line
(let [line &(String.byte-slice txt pos eol)
colon (String.index-of line \:)]
(if (= colon -1)
(do (set! failed @line) (break))
(let-do [k &(String.byte-slice line 0 colon)
lk &(String.ascii-to-lower k)
v &(String.trim
&(String.byte-slice line
(Int.inc colon)
(String.length line)))]
(if (= lk "cookie")
(match (Cookie.parse-many v)
(Result.Error err)
(do (set! failed err) (break))
(Result.Success arr)
(set! cookies
(Array.concat &[cookies arr])))
(set! headers
(Map.update-with-default headers
k
&(fn [h]
(Array.push-back h
@v))
[])))
(set! pos (+ eol 2))))))))
(if (/= &failed "")
(Result.Error
(fmt "Malformed request: found header '%s'"
Expand All @@ -337,11 +336,11 @@ Returns `(Maybe String)`.")
(let [lower-name &(String.ascii-to-lower name)]
(Map.kv-reduce
&(fn [acc k v]
(if (Maybe.just? &acc)
acc
(if (= &(String.ascii-to-lower k) lower-name)
(cond
(Maybe.just? &acc) acc
(= &(String.ascii-to-lower k) lower-name)
(Maybe.Just @(Array.unsafe-first v))
acc)))
acc))
(the (Maybe String) (Maybe.Nothing))
(headers r)))))

Expand Down Expand Up @@ -437,11 +436,12 @@ it will return a `(Success Response)`.")
failed @""]
(while (< pos len)
(let [eol (String.find-crlf txt pos len)]
(if (= eol -1)
(do
(set! body (String.byte-slice txt pos len))
(break))
(if (= eol pos)
(cond
(= eol -1)
(do
(set! body (String.byte-slice txt pos len))
(break))
(= eol pos)
(do
(let [body-start (+ pos 2)]
(when (< body-start len)
Expand All @@ -450,32 +450,30 @@ it will return a `(Success Response)`.")
body-start
len))))
(break))
(let [line &(String.byte-slice txt pos eol)
colon (String.index-of line \:)]
(if (= colon -1)
(do (set! failed @line) (break))
(let-do [k &(String.byte-slice line
0
colon)
lk &(String.ascii-to-lower k)
v &(String.trim
&(String.byte-slice line
(Int.inc colon)
(String.length line)))]
(if (= lk "set-cookie")
(match (Cookie.parse-set v)
(Result.Error err)
(do (set! failed err) (break))
(Result.Success c)
(Array.push-back! &cookies c))
(set! headers
(Map.update-with-default headers
k
&(fn [h]
(Array.push-back h
@v))
[])))
(set! pos (+ eol 2)))))))))
(let [line &(String.byte-slice txt pos eol)
colon (String.index-of line \:)]
(if (= colon -1)
(do (set! failed @line) (break))
(let-do [k &(String.byte-slice line 0 colon)
lk &(String.ascii-to-lower k)
v &(String.trim
&(String.byte-slice line
(Int.inc colon)
(String.length line)))]
(if (= lk "set-cookie")
(match (Cookie.parse-set v)
(Result.Error err)
(do (set! failed err) (break))
(Result.Success c)
(Array.push-back! &cookies c))
(set! headers
(Map.update-with-default headers
k
&(fn [h]
(Array.push-back h
@v))
[])))
(set! pos (+ eol 2))))))))
(if (/= &failed "")
(Result.Error
(fmt "Malformed response: found header '%s'"
Expand All @@ -494,11 +492,11 @@ Returns `(Maybe String)`.")
(let [lower-name &(String.ascii-to-lower name)]
(Map.kv-reduce
&(fn [acc k v]
(if (Maybe.just? &acc)
acc
(if (= &(String.ascii-to-lower k) lower-name)
(cond
(Maybe.just? &acc) acc
(= &(String.ascii-to-lower k) lower-name)
(Maybe.Just @(Array.unsafe-first v))
acc)))
acc))
(the (Maybe String) (Maybe.Nothing))
(headers r)))))

Expand Down
56 changes: 55 additions & 1 deletion test/http.carp
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,58 @@
&(match (the (Result (Map String String) String) (Form.parse "flag"))
(Result.Success m) (Map.get &m "flag")
_ @"FAIL")
"Form.parse handles key without value"))
"Form.parse handles key without value")

; ---------------------------------------------------------------------------
; Request.ignore-body?
; ---------------------------------------------------------------------------

(assert-true test
(Request.ignore-body? &(Request.request @"HEAD" (URI.zero) [] {} @""))
"ignore-body? true for HEAD")

(assert-false test
(Request.ignore-body? &(Request.request @"GET" (URI.zero) [] {} @""))
"ignore-body? false for GET")

(assert-false test
(Request.ignore-body? &(Request.request @"POST" (URI.zero) [] {} @"body"))
"ignore-body? false for POST")

; ---------------------------------------------------------------------------
; Request.form-data
; ---------------------------------------------------------------------------

(assert-equal test
"bar"
&(match (the (Result (Map String String) String)
(Request.form-data
&(Request.post (URI.zero) [] {} @"foo=bar&baz=qux")))
(Result.Success m) (Map.get &m "foo")
_ @"")
"form-data extracts key from POST body")

(assert-equal test
"qux"
&(match (the (Result (Map String String) String)
(Request.form-data
&(Request.post (URI.zero) [] {} @"foo=bar&baz=qux")))
(Result.Success m) (Map.get &m "baz")
_ @"")
"form-data extracts second key from POST body")

(assert-equal test
"hello world"
&(match (the (Result (Map String String) String)
(Request.form-data
&(Request.post (URI.zero) [] {} @"msg=hello%20world")))
(Result.Success m) (Map.get &m "msg")
_ @"")
"form-data decodes percent-encoded values")

(assert-true test
(match (the (Result (Map String String) String)
(Request.form-data &(Request.get (URI.zero) [] {} @"")))
(Result.Success m) (= 0 (Map.length &m))
_ false)
"form-data on empty body returns empty map"))
Loading