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
5 changes: 4 additions & 1 deletion http.carp
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,12 @@ attribute, whatever order the two appear in.")
(private target)
(hidden target)
; RFC 9110 §4.2.4: a request target never carries userinfo.
; RFC 9110 §7.1: the target URI excludes the fragment component.
; RFC 9112 §3.2.1: an origin-form target with an empty path is sent as `/`.
(defn target [u]
(let [v (URI.set-password (URI.set-user @u (Maybe.Nothing)) (Maybe.Nothing))
(let [v (URI.set-fragment
(URI.set-password (URI.set-user @u (Maybe.Nothing)) (Maybe.Nothing))
(Maybe.Nothing))
s (URI.str &v)]
(if (and (Maybe.nothing? (URI.path &v))
(Maybe.nothing? (URI.scheme &v))
Expand Down
45 changes: 45 additions & 0 deletions test/http.carp
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,51 @@
_ false)
"the credentials stay on the request’s URI")

(assert-equal test
"GET http://h.example/a/b?q=1 HTTP/1.1\r\n\r\n"
&(request-line "http://h.example/a/b?q=1#sec2")
"request-target drops a fragment and keeps the query")

(assert-equal test
"GET http://h.example/a/b HTTP/1.1\r\n\r\n"
&(request-line "http://h.example/a/b#sec2")
"request-target drops a fragment without a query")

(assert-equal test
"GET http://h.example/a/b HTTP/1.1\r\n\r\n"
&(request-line "http://h.example/a/b#")
"request-target drops an empty fragment")

(assert-equal test
"GET http://h.example/a/b HTTP/1.1\r\n\r\n"
&(request-line "http://h.example/a/b#frag?notquery")
"request-target drops a fragment that looks like a query")

(assert-equal test
"GET http://h.example/a/b?q=a%23b HTTP/1.1\r\n\r\n"
&(request-line "http://h.example/a/b?q=a%23b")
"request-target keeps an encoded # inside the query")

(assert-equal test
"GET /?q=a#b HTTP/1.1\r\n\r\n"
&(Request.str
&(Request.get (URI.set-query (URI.zero) (Maybe.Just @"q=a#b")) [] {} @""))
"request-target keeps a literal # inside the query")

(assert-equal test
"GET / HTTP/1.1\r\n\r\n"
&(Request.str
&(Request.get (URI.set-fragment (URI.zero) (Maybe.Just @"sec2")) [] {} @""))
"request-target of a fragment-only URI is /")

(assert-true test
(match (URI.parse "http://h.example/a/b#sec2")
(Result.Success u)
(= &(Maybe.Just @"sec2")
(URI.fragment (Request.uri &(Request.get u [] {} @""))))
_ false)
"the fragment stays on the request’s URI")

(assert-true test
(match (Cookie.parse "name=value")
(Result.Success c)
Expand Down