From 20dff0d50d9ce9ab7f267f859e450ec292e15ccc Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 14:26:38 +0100 Subject: [PATCH 01/23] Add more request URI tests for URI components --- lib_test/test_request.ml | 80 ++++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 12 deletions(-) diff --git a/lib_test/test_request.ml b/lib_test/test_request.ml index fd9bfe4476..8e873af08f 100644 --- a/lib_test/test_request.ml +++ b/lib_test/test_request.ml @@ -52,31 +52,76 @@ let parse_request_uri _ = let uri = Uri.of_string "/" in parse_request_uri_ r uri "parse_request_uri" -let parse_request_uri_double_slash _ = - let r = "GET // HTTP/1.1\r\n\r\n" in - let uri = Uri.with_path (Uri.of_string "") "//" in - parse_request_uri_ r uri "parse_request_uri_double_slash" - -let parse_request_uri_triple_slash _ = - let r = "GET /// HTTP/1.1\r\n\r\n" in - let uri = Uri.with_path (Uri.of_string "") "///" in - parse_request_uri_ r uri "parse_request_uri_triple_slash" - let parse_request_uri_host _ = let r = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n" in let uri = Uri.of_string "//example.com/" in parse_request_uri_ r uri "parse_request_uri_host" +let parse_request_uri_double_slash _ = + let r = "GET // HTTP/1.1\r\n\r\n" in + let uri = Uri.with_path (Uri.of_string "") "//" in + parse_request_uri_ r uri "parse_request_uri_double_slash" + let parse_request_uri_host_double_slash _ = let r = "GET // HTTP/1.1\r\nHost: example.com\r\n\r\n" in let uri = Uri.of_string "//example.com//" in parse_request_uri_ r uri "parse_request_uri_host_double_slash" +let parse_request_uri_triple_slash _ = + let r = "GET /// HTTP/1.1\r\n\r\n" in + let uri = Uri.with_path (Uri.of_string "") "///" in + parse_request_uri_ r uri "parse_request_uri_triple_slash" + let parse_request_uri_host_triple_slash _ = let r = "GET /// HTTP/1.1\r\nHost: example.com\r\n\r\n" in let uri = Uri.of_string "//example.com///" in parse_request_uri_ r uri "parse_request_uri_host_triple_slash" +let parse_request_uri_no_slash _ = + let r = "GET foo HTTP/1.1\r\n\r\n" in + let uri = Uri.with_path (Uri.of_string "") "foo" in + parse_request_uri_ r uri "parse_request_uri_no_slash" + +let parse_request_uri_host_no_slash _ = + let r = "GET foo HTTP/1.1\r\nHost: example.com\r\n\r\n" in + let uri = Uri.of_string "//example.com/foo" in + parse_request_uri_ r uri "parse_request_uri_host_no_slash" + +let parse_request_uri_path_like_scheme _ = + let r = "GET http://example.net HTTP/1.1\r\n\r\n" in + let uri = Uri.with_path (Uri.of_string "") "http://example.net" in + parse_request_uri_ r uri "parse_request_uri_path_like_scheme" + +let parse_request_uri_host_path_like_scheme _ = + let path = "http://example.net" in + let r = "GET "^path^" HTTP/1.1\r\nHost: example.com\r\n\r\n" in + let uri = Uri.with_path (Uri.of_string "//example.com") path in + parse_request_uri_ r uri "parse_request_uri_host_path_like_scheme" + +let parse_request_uri_path_like_host_port _ = + let path = "//example.net:8080" in + let r = "GET "^path^" HTTP/1.1\r\n\r\n" in + let uri = Uri.with_path (Uri.of_string "") path in + parse_request_uri_ r uri "parse_request_uri_path_like_host_port" + +let parse_request_uri_host_path_like_host_port _ = + let path = "//example.net:8080" in + let r = "GET "^path^" HTTP/1.1\r\nHost: example.com\r\n\r\n" in + let uri = Uri.with_path (Uri.of_string "//example.com") path in + parse_request_uri_ r uri "parse_request_uri_host_path_like_host_port" + +let parse_request_uri_query _ = + let pqs = "/?foo" in + let r = "GET "^pqs^" HTTP/1.1\r\n\r\n" in + let uri = Uri.of_string pqs in + parse_request_uri_ r uri "parse_request_uri_query" + +let parse_request_uri_host_query _ = + let pqs = "/?foo" in + let r = "GET "^pqs^" HTTP/1.1\r\nHost: example.com\r\n\r\n" in + let uri = Uri.of_string ("//example.com"^pqs) in + parse_request_uri_ r uri "parse_request_uri_host_query" + let _ = ("Request" >::: [ "Test header has auth" >:: header_has_auth @@ -84,11 +129,22 @@ let _ = ; "Auth from Uri - do not override" >:: auth_uri_no_override ; "Auth from Uri" >:: auth_uri ; "Parse simple request URI" >:: parse_request_uri - ; "Parse request URI double slash" >:: parse_request_uri_double_slash - ; "Parse request URI triple slash" >:: parse_request_uri_triple_slash ; "Parse request URI with host" >:: parse_request_uri_host + ; "Parse request URI double slash" >:: parse_request_uri_double_slash ; "Parse request URI double slash with host" >:: parse_request_uri_host_double_slash + ; "Parse request URI triple slash" >:: parse_request_uri_triple_slash ; "Parse request URI triple slash with host" >:: parse_request_uri_host_triple_slash + ; "Parse request URI no slash" >:: parse_request_uri_no_slash + ; "Parse request URI no slash with host" >:: parse_request_uri_host_no_slash + ; "Parse request URI path like scheme" >:: parse_request_uri_path_like_scheme + ; "Parse request URI path like scheme with host" + >:: parse_request_uri_host_path_like_scheme + ; "Parse request URI path like host:port" + >:: parse_request_uri_path_like_host_port + ; "Parse request URI path like host:port with host" + >:: parse_request_uri_host_path_like_host_port + ; "Parse request URI with query string" >:: parse_request_uri_query + ; "Parse request URI with query with host" >:: parse_request_uri_host_query ]) |> run_test_tt_main From 66ef98e3205e6c75b3349e76776b0cbcb627bfed Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 14:54:25 +0100 Subject: [PATCH 02/23] Improve request URI test error printing; correct 2 test cases --- lib_test/test_request.ml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib_test/test_request.ml b/lib_test/test_request.ml index 8e873af08f..40efe19ab6 100644 --- a/lib_test/test_request.ml +++ b/lib_test/test_request.ml @@ -34,14 +34,23 @@ let auth_uri _ = (r |> Request.headers |> Header.get_authorization) (Some (`Basic ("foo", "bar"))) +let opt_default default = function + | None -> default + | Some v -> v + let parse_request_uri_ r uri name = String_io.M.( StringRequest.read (String_io.open_in r) >>= function | `Ok { Request.uri = ruri } -> - let msg = - Printf.sprintf "expected path %s got %s" - (Uri.path uri) (Uri.path ruri) + let msg = Uri.(Printf.sprintf "expected %s %d %s %s\ngot %s %d %s %s" + (opt_default "_" (host uri)) + (opt_default (-1) (port uri)) + (path uri) (encoded_of_query (query uri)) + (opt_default "_" (host ruri)) + (opt_default (-1) (port uri)) + (path ruri) (encoded_of_query (query ruri)) + ) in assert_equal ~msg ruri uri | _ -> assert_failure (name^" parse failed") @@ -79,7 +88,7 @@ let parse_request_uri_host_triple_slash _ = let parse_request_uri_no_slash _ = let r = "GET foo HTTP/1.1\r\n\r\n" in - let uri = Uri.with_path (Uri.of_string "") "foo" in + let uri = Uri.of_string "/foo" in parse_request_uri_ r uri "parse_request_uri_no_slash" let parse_request_uri_host_no_slash _ = @@ -89,7 +98,7 @@ let parse_request_uri_host_no_slash _ = let parse_request_uri_path_like_scheme _ = let r = "GET http://example.net HTTP/1.1\r\n\r\n" in - let uri = Uri.with_path (Uri.of_string "") "http://example.net" in + let uri = Uri.of_string "/http://example.net" in parse_request_uri_ r uri "parse_request_uri_path_like_scheme" let parse_request_uri_host_path_like_scheme _ = From e6f2812f5ebe4b92dce8d07365993daf29428150 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 14:55:59 +0100 Subject: [PATCH 03/23] Fix request URI parsing with query strings Fixes #308. Fixes #317. --- lib/request.ml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/request.ml b/lib/request.ml index 629db8e12b..8b37491938 100644 --- a/lib/request.ml +++ b/lib/request.ml @@ -106,16 +106,22 @@ module Make(IO : S.IO) = struct parse_request_fst_line ic >>= function | `Eof -> return `Eof | `Invalid reason as r -> return r - | `Ok (meth, path, version) -> + | `Ok (meth, path_and_qs, version) -> Header_IO.parse ic >>= fun headers -> - let empty = Uri.of_string "" in + let empty_base = Uri.of_string "///" in + let pqs = match Stringext.split ~max:2 path_and_qs ~on:'?' with + | [] -> empty_base + | [path] -> Uri.with_path empty_base path + | path::qs::_ -> + let path_base = Uri.with_path empty_base path in + Uri.with_query path_base (Uri.query_of_encoded qs) + in let uri = match Header.get headers "host" with - | None -> Uri.with_path empty path + | None -> Uri.(with_scheme (with_host pqs None) None) | Some host -> let host_uri = Uri.of_string ("//"^host) in - let uri = Uri.with_path empty path in - let uri = Uri.with_host uri (Uri.host host_uri) in + let uri = Uri.with_host pqs (Uri.host host_uri) in Uri.with_port uri (Uri.port host_uri) in let encoding = Header.get_transfer_encoding headers in From de29d2a003f50c41d0c42eaff5a6b369f0f296e6 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 15:31:28 +0100 Subject: [PATCH 04/23] Fix Request-URI tests for Sec 5.1.2 compliance --- lib_test/test_request.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib_test/test_request.ml b/lib_test/test_request.ml index 40efe19ab6..c7d9184edc 100644 --- a/lib_test/test_request.ml +++ b/lib_test/test_request.ml @@ -98,13 +98,13 @@ let parse_request_uri_host_no_slash _ = let parse_request_uri_path_like_scheme _ = let r = "GET http://example.net HTTP/1.1\r\n\r\n" in - let uri = Uri.of_string "/http://example.net" in + let uri = Uri.of_string "http://example.net" in parse_request_uri_ r uri "parse_request_uri_path_like_scheme" let parse_request_uri_host_path_like_scheme _ = let path = "http://example.net" in let r = "GET "^path^" HTTP/1.1\r\nHost: example.com\r\n\r\n" in - let uri = Uri.with_path (Uri.of_string "//example.com") path in + let uri = Uri.of_string path in parse_request_uri_ r uri "parse_request_uri_host_path_like_scheme" let parse_request_uri_path_like_host_port _ = From c5a0d8493a85a78fd8270be89b6ec5cc851d779d Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 15:51:31 +0100 Subject: [PATCH 05/23] Test empty Request-URI (='/' by Sec 5.1) --- lib_test/test_request.ml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib_test/test_request.ml b/lib_test/test_request.ml index c7d9184edc..d8a1d9350a 100644 --- a/lib_test/test_request.ml +++ b/lib_test/test_request.ml @@ -96,6 +96,16 @@ let parse_request_uri_host_no_slash _ = let uri = Uri.of_string "//example.com/foo" in parse_request_uri_ r uri "parse_request_uri_host_no_slash" +let parse_request_uri_empty _ = + let r = "GET HTTP/1.1\r\n\r\n" in + let uri = Uri.of_string "/" in + parse_request_uri_ r uri "parse_request_uri_empty" + +let parse_request_uri_host_empty _ = + let r = "GET HTTP/1.1\r\nHost: example.com\r\n\r\n" in + let uri = Uri.of_string "//example.com/" in + parse_request_uri_ r uri "parse_request_uri_host_empty" + let parse_request_uri_path_like_scheme _ = let r = "GET http://example.net HTTP/1.1\r\n\r\n" in let uri = Uri.of_string "http://example.net" in @@ -147,6 +157,8 @@ let _ = >:: parse_request_uri_host_triple_slash ; "Parse request URI no slash" >:: parse_request_uri_no_slash ; "Parse request URI no slash with host" >:: parse_request_uri_host_no_slash + ; "Parse request URI empty" >:: parse_request_uri_empty + ; "Parse request URI empty with host" >:: parse_request_uri_host_empty ; "Parse request URI path like scheme" >:: parse_request_uri_path_like_scheme ; "Parse request URI path like scheme with host" >:: parse_request_uri_host_path_like_scheme From 76ae9a495c46c7db5a5405a96c1edb6d0eb5fd5f Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 15:53:59 +0100 Subject: [PATCH 06/23] Make request URI parsing compliant with RFC 2616 Sec 5.2 --- lib/request.ml | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/request.ml b/lib/request.ml index 8b37491938..b1faf69d6f 100644 --- a/lib/request.ml +++ b/lib/request.ml @@ -106,23 +106,26 @@ module Make(IO : S.IO) = struct parse_request_fst_line ic >>= function | `Eof -> return `Eof | `Invalid reason as r -> return r - | `Ok (meth, path_and_qs, version) -> + | `Ok (meth, request_uri_s, version) -> Header_IO.parse ic >>= fun headers -> - let empty_base = Uri.of_string "///" in - let pqs = match Stringext.split ~max:2 path_and_qs ~on:'?' with - | [] -> empty_base - | [path] -> Uri.with_path empty_base path - | path::qs::_ -> - let path_base = Uri.with_path empty_base path in - Uri.with_query path_base (Uri.query_of_encoded qs) - in - let uri = - match Header.get headers "host" with - | None -> Uri.(with_scheme (with_host pqs None) None) - | Some host -> - let host_uri = Uri.of_string ("//"^host) in - let uri = Uri.with_host pqs (Uri.host host_uri) in - Uri.with_port uri (Uri.port host_uri) + let uri = Uri.of_string request_uri_s in + let uri = match Uri.scheme uri with + | Some _ -> uri (* we have an absoluteURI *) + | None -> + let empty_base = Uri.of_string "///" in + let pqs = match Stringext.split ~max:2 request_uri_s ~on:'?' with + | [] -> empty_base + | [path] -> Uri.with_path empty_base path + | path::qs::_ -> + let path_base = Uri.with_path empty_base path in + Uri.with_query path_base (Uri.query_of_encoded qs) + in + match Header.get headers "host" with + | None -> Uri.(with_scheme (with_host pqs None) None) + | Some host -> + let host_uri = Uri.of_string ("//"^host) in + let uri = Uri.with_host pqs (Uri.host host_uri) in + Uri.with_port uri (Uri.port host_uri) in let encoding = Header.get_transfer_encoding headers in return (`Ok { headers; meth; uri; version; encoding }) From b28e9f39b63306089a74c853753ac92ab70e23a4 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 17:15:12 +0100 Subject: [PATCH 07/23] Add CONNECT and TRACE methods --- CHANGES | 4 ++++ lib/code.ml | 17 ++++++++++++++++- lib/code.mli | 13 ++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index b0f9198fe1..f2481f9863 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +0.17.0 (trunk): +New features and bug fixes: +* CONNECT and TRACE methods added + 0.16.1 (2015-04-09): New features and bug fixes: * Fix handling of request paths starting with multiple slashes (#308) diff --git a/lib/code.ml b/lib/code.ml index ddd105676d..a69d5eb638 100644 --- a/lib/code.ml +++ b/lib/code.ml @@ -3,7 +3,18 @@ open Sexplib.Std type version = [ `HTTP_1_0 | `HTTP_1_1 | `Other of string ] with sexp -type meth = [ `GET | `POST | `HEAD | `DELETE | `PATCH | `PUT | `OPTIONS | `Other of string ] with sexp +type meth = [ + | `GET + | `POST + | `HEAD + | `DELETE + | `PATCH + | `PUT + | `OPTIONS + | `CONNECT + | `TRACE + | `Other of string +] with sexp type informational_status = [ `Continue @@ -121,6 +132,8 @@ let string_of_method: meth -> string = function | `PATCH -> "PATCH" | `PUT -> "PUT" | `OPTIONS -> "OPTIONS" + | `CONNECT -> "CONNECT" + | `TRACE -> "TRACE" | `Other s -> s let method_of_string: string -> meth = function @@ -131,6 +144,8 @@ let method_of_string: string -> meth = function | "PATCH" -> `PATCH | "PUT" -> `PUT | "OPTIONS" -> `OPTIONS + | "CONNECT" -> `CONNECT + | "TRACE" -> `TRACE | s -> `Other s let compare_method a b = diff --git a/lib/code.mli b/lib/code.mli index 37d532fcde..fd814cabbc 100644 --- a/lib/code.mli +++ b/lib/code.mli @@ -3,7 +3,18 @@ open Sexplib.Std type version = [ `HTTP_1_0 | `HTTP_1_1 | `Other of string ] with sexp -type meth = [ `GET | `POST | `HEAD | `DELETE | `PATCH | `PUT | `OPTIONS | `Other of string ] with sexp +type meth = [ + | `GET + | `POST + | `HEAD + | `DELETE + | `PATCH + | `PUT + | `OPTIONS + | `CONNECT + | `TRACE + | `Other of string +] with sexp type informational_status = [ `Continue (** Client should continue with request *) From c663c6a73585519f443fef61b786259cdc99f777 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 17:16:42 +0100 Subject: [PATCH 08/23] Add CONNECT and OPTIONS request URI tests; test request URI path non-emptiness --- lib_test/test_request.ml | 47 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/lib_test/test_request.ml b/lib_test/test_request.ml index d8a1d9350a..1c8b5f577a 100644 --- a/lib_test/test_request.ml +++ b/lib_test/test_request.ml @@ -108,13 +108,12 @@ let parse_request_uri_host_empty _ = let parse_request_uri_path_like_scheme _ = let r = "GET http://example.net HTTP/1.1\r\n\r\n" in - let uri = Uri.of_string "http://example.net" in + let uri = Uri.of_string "http://example.net/" in parse_request_uri_ r uri "parse_request_uri_path_like_scheme" let parse_request_uri_host_path_like_scheme _ = - let path = "http://example.net" in - let r = "GET "^path^" HTTP/1.1\r\nHost: example.com\r\n\r\n" in - let uri = Uri.of_string path in + let r = "GET http://example.net HTTP/1.1\r\nHost: example.com\r\n\r\n" in + let uri = Uri.of_string "http://example.net/" in parse_request_uri_ r uri "parse_request_uri_host_path_like_scheme" let parse_request_uri_path_like_host_port _ = @@ -141,6 +140,38 @@ let parse_request_uri_host_query _ = let uri = Uri.of_string ("//example.com"^pqs) in parse_request_uri_ r uri "parse_request_uri_host_query" +let parse_request_uri_query_no_slash _ = + let r = "GET ?foo HTTP/1.1\r\n\r\n" in + let uri = Uri.of_string "/?foo" in + parse_request_uri_ r uri "parse_request_uri_query_no_slash" + +let parse_request_uri_host_query_no_slash _ = + let r = "GET ?foo HTTP/1.1\r\nHost: example.com\r\n\r\n" in + let uri = Uri.of_string "//example.com/?foo" in + parse_request_uri_ r uri "parse_request_uri_host_query_no_slash" + +let parse_request_connect _ = + let r = "CONNECT vpn.example.net:443 HTTP/1.1\r\n" in + let uri = Uri.of_string "//vpn.example.net:443" in + parse_request_uri_ r uri "parse_request_connect" + +let parse_request_connect_host _ = + let r = + "CONNECT vpn.example.net:443 HTTP/1.1\r\nHost: vpn.example.com:443\r\n\r\n" + in + let uri = Uri.of_string "//vpn.example.net:443" in + parse_request_uri_ r uri "parse_request_connect_host" + +let parse_request_options _ = + let r = "OPTIONS * HTTP/1.1\r\n\r\n" in + let uri = Uri.of_string "*" in + parse_request_uri_ r uri "parse_request_options" + +let parse_request_options_host _ = + let r = "OPTIONS * HTTP/1.1\r\nHost: example.com:443\r\n\r\n" in + let uri = Uri.of_string "//example.com/*" in + parse_request_uri_ r uri "parse_request_options_host" + let _ = ("Request" >::: [ "Test header has auth" >:: header_has_auth @@ -168,4 +199,12 @@ let _ = >:: parse_request_uri_host_path_like_host_port ; "Parse request URI with query string" >:: parse_request_uri_query ; "Parse request URI with query with host" >:: parse_request_uri_host_query + ; "Parse request URI no slash with query string" + >:: parse_request_uri_query_no_slash + ; "Parse request URI no slash with query with host" + >:: parse_request_uri_host_query_no_slash + ; "Parse CONNECT request URI" >:: parse_request_connect + ; "Parse CONNECT request URI with host" >:: parse_request_connect_host + ; "Parse OPTIONS request URI" >:: parse_request_options + ; "Parse OPTIONS request URI with host" >:: parse_request_options_host ]) |> run_test_tt_main From 6b9ab30873aca3c009d46a345a0e20acfb793429 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 17:21:52 +0100 Subject: [PATCH 09/23] Add request URI host:port test; fix parse_request_options_host test --- lib_test/test_request.ml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib_test/test_request.ml b/lib_test/test_request.ml index 1c8b5f577a..837b484526 100644 --- a/lib_test/test_request.ml +++ b/lib_test/test_request.ml @@ -66,6 +66,11 @@ let parse_request_uri_host _ = let uri = Uri.of_string "//example.com/" in parse_request_uri_ r uri "parse_request_uri_host" +let parse_request_uri_host_port _ = + let r = "GET / HTTP/1.1\r\nHost: example.com:8080\r\n\r\n" in + let uri = Uri.of_string "//example.com:8080/" in + parse_request_uri_ r uri "parse_request_uri_host_port" + let parse_request_uri_double_slash _ = let r = "GET // HTTP/1.1\r\n\r\n" in let uri = Uri.with_path (Uri.of_string "") "//" in @@ -169,7 +174,7 @@ let parse_request_options _ = let parse_request_options_host _ = let r = "OPTIONS * HTTP/1.1\r\nHost: example.com:443\r\n\r\n" in - let uri = Uri.of_string "//example.com/*" in + let uri = Uri.of_string "//example.com:443/*" in parse_request_uri_ r uri "parse_request_options_host" let _ = @@ -180,6 +185,7 @@ let _ = ; "Auth from Uri" >:: auth_uri ; "Parse simple request URI" >:: parse_request_uri ; "Parse request URI with host" >:: parse_request_uri_host + ; "Parse request URI with host and port" >:: parse_request_uri_host_port ; "Parse request URI double slash" >:: parse_request_uri_double_slash ; "Parse request URI double slash with host" >:: parse_request_uri_host_double_slash From 6b617466dfd87efc2eb93cc7a86d0d1a4e7fc01d Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 17:23:09 +0100 Subject: [PATCH 10/23] Fix request-URI of authority when CONNECT method used --- lib/request.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/request.ml b/lib/request.ml index b1faf69d6f..46a97e68fa 100644 --- a/lib/request.ml +++ b/lib/request.ml @@ -106,6 +106,11 @@ module Make(IO : S.IO) = struct parse_request_fst_line ic >>= function | `Eof -> return `Eof | `Invalid reason as r -> return r + | `Ok (`CONNECT as meth, authority, version) -> + Header_IO.parse ic >>= fun headers -> + let uri = Uri.of_string ("//"^authority) in + let encoding = Header.get_transfer_encoding headers in + return (`Ok { headers; meth; uri; version; encoding }) | `Ok (meth, request_uri_s, version) -> Header_IO.parse ic >>= fun headers -> let uri = Uri.of_string request_uri_s in From 59616fd0002ebddddd786aa49a0df91a0f4a86ff Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 17:31:56 +0100 Subject: [PATCH 11/23] Fix OPTIONS * no Host test --- lib_test/test_request.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_test/test_request.ml b/lib_test/test_request.ml index 837b484526..a1a7bf960c 100644 --- a/lib_test/test_request.ml +++ b/lib_test/test_request.ml @@ -169,7 +169,7 @@ let parse_request_connect_host _ = let parse_request_options _ = let r = "OPTIONS * HTTP/1.1\r\n\r\n" in - let uri = Uri.of_string "*" in + let uri = Uri.of_string "/*" in parse_request_uri_ r uri "parse_request_options" let parse_request_options_host _ = From 595dfd6fd6a2a04af13e0cf3d360421bcd086c76 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 17:32:25 +0100 Subject: [PATCH 12/23] Fix request URI empty paths --- lib/request.ml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/request.ml b/lib/request.ml index 46a97e68fa..5723373ce0 100644 --- a/lib/request.ml +++ b/lib/request.ml @@ -115,14 +115,18 @@ module Make(IO : S.IO) = struct Header_IO.parse ic >>= fun headers -> let uri = Uri.of_string request_uri_s in let uri = match Uri.scheme uri with - | Some _ -> uri (* we have an absoluteURI *) + | Some _ -> (* we have an absoluteURI *) + Uri.(match path uri with "" -> with_path uri "/" | _ -> uri) | None -> + let empty = Uri.of_string "" in let empty_base = Uri.of_string "///" in let pqs = match Stringext.split ~max:2 request_uri_s ~on:'?' with | [] -> empty_base - | [path] -> Uri.with_path empty_base path + | [path] -> Uri.resolve "http" empty_base (Uri.with_path empty path) | path::qs::_ -> - let path_base = Uri.with_path empty_base path in + let path_base = + Uri.resolve "http" empty_base (Uri.with_path empty path) + in Uri.with_query path_base (Uri.query_of_encoded qs) in match Header.get headers "host" with From d444d6c8d71cff232cf105a6def05cc49364e34e Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 17:48:59 +0100 Subject: [PATCH 13/23] CHANGES --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index f2481f9863..02eb40c953 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,7 @@ 0.17.0 (trunk): New features and bug fixes: * CONNECT and TRACE methods added +* Fix handling of request URI for query strings, proxies, CONNECT and more 0.16.1 (2015-04-09): New features and bug fixes: From a6d153c38faa6eebb86f4819052b077fa9563231 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 17:51:47 +0100 Subject: [PATCH 14/23] Revert "Add CONNECT and TRACE methods" This reverts commit b28e9f39b63306089a74c853753ac92ab70e23a4. --- lib/code.ml | 17 +---------------- lib/code.mli | 13 +------------ 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/lib/code.ml b/lib/code.ml index a69d5eb638..ddd105676d 100644 --- a/lib/code.ml +++ b/lib/code.ml @@ -3,18 +3,7 @@ open Sexplib.Std type version = [ `HTTP_1_0 | `HTTP_1_1 | `Other of string ] with sexp -type meth = [ - | `GET - | `POST - | `HEAD - | `DELETE - | `PATCH - | `PUT - | `OPTIONS - | `CONNECT - | `TRACE - | `Other of string -] with sexp +type meth = [ `GET | `POST | `HEAD | `DELETE | `PATCH | `PUT | `OPTIONS | `Other of string ] with sexp type informational_status = [ `Continue @@ -132,8 +121,6 @@ let string_of_method: meth -> string = function | `PATCH -> "PATCH" | `PUT -> "PUT" | `OPTIONS -> "OPTIONS" - | `CONNECT -> "CONNECT" - | `TRACE -> "TRACE" | `Other s -> s let method_of_string: string -> meth = function @@ -144,8 +131,6 @@ let method_of_string: string -> meth = function | "PATCH" -> `PATCH | "PUT" -> `PUT | "OPTIONS" -> `OPTIONS - | "CONNECT" -> `CONNECT - | "TRACE" -> `TRACE | s -> `Other s let compare_method a b = diff --git a/lib/code.mli b/lib/code.mli index fd814cabbc..37d532fcde 100644 --- a/lib/code.mli +++ b/lib/code.mli @@ -3,18 +3,7 @@ open Sexplib.Std type version = [ `HTTP_1_0 | `HTTP_1_1 | `Other of string ] with sexp -type meth = [ - | `GET - | `POST - | `HEAD - | `DELETE - | `PATCH - | `PUT - | `OPTIONS - | `CONNECT - | `TRACE - | `Other of string -] with sexp +type meth = [ `GET | `POST | `HEAD | `DELETE | `PATCH | `PUT | `OPTIONS | `Other of string ] with sexp type informational_status = [ `Continue (** Client should continue with request *) From e9d99f32421c017c486d0cf646c8e6a26c5e447f Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 17:58:13 +0100 Subject: [PATCH 15/23] Update the Code generate.ml script --- scripts/generate.ml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/scripts/generate.ml b/scripts/generate.ml index c8f0d73fd1..1504498bc2 100644 --- a/scripts/generate.ml +++ b/scripts/generate.ml @@ -1656,14 +1656,14 @@ let output_type oc ~mli t = let output_status_types oc ~mli t = List.iter (output_type oc ~mli) t; - append oc "type status ="; + append oc "type status = ["; List.iteri (fun i t -> if i = 0 then - append oc " [ %s_status" t.section + append oc " | %s_status" t.section else append oc " | %s_status" t.section ) t; - append oc " ] with sexp"; + append oc "] with sexp"; append oc ""; append oc "type status_code = [`Code of int | status ] with sexp"; append oc "" @@ -1754,8 +1754,10 @@ type gen = { let g constr string = { constr; string } let output_gen_types oc ~mli (name, typ, gens) = - append oc "type %s = [ %s | `Other of string ] with sexp" typ - (String.concat " | " (List.map (fun g -> g.constr) gens)); + append oc "type %s = [" typ; + List.iter (fun { constr } -> append oc " | %s" constr) gens; + append oc " | `Other of string"; + append oc "] with sexp"; append oc "" let output_gen_convert oc ~mli (name, typ, gens) = @@ -1810,6 +1812,8 @@ let known_methods = [ g "`PATCH" "PATCH"; g "`PUT" "PUT"; g "`OPTIONS" "OPTIONS"; + g "`TRACE" "TRACE"; + g "`CONNECT" "CONNECT"; ] let meth = ("method", "meth", known_methods) @@ -1830,8 +1834,8 @@ let gen oc ~mli = output_is_code oc ~mli t let () = - let ml = open_out "../cohttp/code.ml" in - let mli = open_out "../cohttp/code.mli" in + let ml = open_out "../lib/code.ml" in + let mli = open_out "../lib/code.mli" in gen ml ~mli:false; gen mli ~mli:true; close_out ml; From f5c51102f3ea36680edee839cc2af0910d15acc1 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 17:58:25 +0100 Subject: [PATCH 16/23] Regenerate Code --- lib/code.ml | 29 ++++++++++++++++++++++++----- lib/code.mli | 33 +++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/lib/code.ml b/lib/code.ml index ddd105676d..ed36512bc6 100644 --- a/lib/code.ml +++ b/lib/code.ml @@ -1,9 +1,24 @@ (* Auto-Generated by 'ocaml generate.ml' *) open Sexplib.Std -type version = [ `HTTP_1_0 | `HTTP_1_1 | `Other of string ] with sexp +type version = [ + | `HTTP_1_0 + | `HTTP_1_1 + | `Other of string +] with sexp -type meth = [ `GET | `POST | `HEAD | `DELETE | `PATCH | `PUT | `OPTIONS | `Other of string ] with sexp +type meth = [ + | `GET + | `POST + | `HEAD + | `DELETE + | `PATCH + | `PUT + | `OPTIONS + | `TRACE + | `CONNECT + | `Other of string +] with sexp type informational_status = [ `Continue @@ -89,13 +104,13 @@ type server_error_status = | `Network_connect_timeout_error ] with sexp -type status = - [ informational_status +type status = [ + | informational_status | success_status | redirection_status | client_error_status | server_error_status - ] with sexp +] with sexp type status_code = [`Code of int | status ] with sexp @@ -121,6 +136,8 @@ let string_of_method: meth -> string = function | `PATCH -> "PATCH" | `PUT -> "PUT" | `OPTIONS -> "OPTIONS" + | `TRACE -> "TRACE" + | `CONNECT -> "CONNECT" | `Other s -> s let method_of_string: string -> meth = function @@ -131,6 +148,8 @@ let method_of_string: string -> meth = function | "PATCH" -> `PATCH | "PUT" -> `PUT | "OPTIONS" -> `OPTIONS + | "TRACE" -> `TRACE + | "CONNECT" -> `CONNECT | s -> `Other s let compare_method a b = diff --git a/lib/code.mli b/lib/code.mli index 37d532fcde..326276c33c 100644 --- a/lib/code.mli +++ b/lib/code.mli @@ -1,9 +1,24 @@ (* Auto-Generated by 'ocaml generate.ml' *) open Sexplib.Std -type version = [ `HTTP_1_0 | `HTTP_1_1 | `Other of string ] with sexp - -type meth = [ `GET | `POST | `HEAD | `DELETE | `PATCH | `PUT | `OPTIONS | `Other of string ] with sexp +type version = [ + | `HTTP_1_0 + | `HTTP_1_1 + | `Other of string +] with sexp + +type meth = [ + | `GET + | `POST + | `HEAD + | `DELETE + | `PATCH + | `PUT + | `OPTIONS + | `TRACE + | `CONNECT + | `Other of string +] with sexp type informational_status = [ `Continue (** Client should continue with request *) @@ -29,10 +44,8 @@ type success_status = type redirection_status = [ `Multiple_choices (** multiple options for the resource delivered *) - | `Moved_permanently (** this and all future requests directed to the - URI provided as the "Location" header. *) - | `Found (** temporary response to request found via alternative URI - provided by the "Location" header. *) + | `Moved_permanently (** this and all future requests directed to the given URI *) + | `Found (** temporary response to request found via alternative URI *) | `See_other (** permanent response to request found via alternative URI *) | `Not_modified (** resource has not been modified since last requested *) | `Use_proxy (** content located elsewhere, retrieve from there *) @@ -96,13 +109,13 @@ type server_error_status = ] with sexp (** Server_error *) -type status = - [ informational_status +type status = [ + | informational_status | success_status | redirection_status | client_error_status | server_error_status - ] with sexp +] with sexp type status_code = [`Code of int | status ] with sexp From 383c838d6834c1a79575003010e8ff3b0acc56d8 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Tue, 14 Apr 2015 18:39:09 +0100 Subject: [PATCH 17/23] Remove unnecessary test in generate.ml --- scripts/generate.ml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/scripts/generate.ml b/scripts/generate.ml index 1504498bc2..ae70b01842 100644 --- a/scripts/generate.ml +++ b/scripts/generate.ml @@ -1657,12 +1657,7 @@ let output_type oc ~mli t = let output_status_types oc ~mli t = List.iter (output_type oc ~mli) t; append oc "type status = ["; - List.iteri (fun i t -> - if i = 0 then - append oc " | %s_status" t.section - else - append oc " | %s_status" t.section - ) t; + List.iteri (fun i t -> append oc " | %s_status" t.section) t; append oc "] with sexp"; append oc ""; append oc "type status_code = [`Code of int | status ] with sexp"; From a091734b75a9488f4216bdaed99471bc4d6370de Mon Sep 17 00:00:00 2001 From: David Sheets Date: Wed, 15 Apr 2015 11:46:53 +0100 Subject: [PATCH 18/23] Update CHANGES --- CHANGES | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 02eb40c953..f9519f43a4 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,12 @@ 0.17.0 (trunk): + +Compatibility breaking interface changes: +* CONNECT and TRACE methods added to Code. Exhaustive matches will need updating. + New features and bug fixes: -* CONNECT and TRACE methods added -* Fix handling of request URI for query strings, proxies, CONNECT and more +* Fix handling of request URI for query strings and CONNECT proxies +* Fix precedence of Host header when request-URI is absolute URI +* Fix request URI path to be non-empty except for * requests (e.g. OPTIONS *) 0.16.1 (2015-04-09): New features and bug fixes: From 39dfa059544040bce8e6d0945b6c1fb789f5b47c Mon Sep 17 00:00:00 2001 From: David Sheets Date: Wed, 15 Apr 2015 11:47:35 +0100 Subject: [PATCH 19/23] Handle requests for * correctly --- lib/request.ml | 20 ++++++++++++++++---- lib_test/test_request.ml | 4 ++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/request.ml b/lib/request.ml index 5723373ce0..887c296f28 100644 --- a/lib/request.ml +++ b/lib/request.ml @@ -102,15 +102,28 @@ module Make(IO : S.IO) = struct end | None -> return `Eof + let return_request headers meth uri version = + let encoding = Header.get_transfer_encoding headers in + return (`Ok { headers; meth; uri; version; encoding }) + let read ic = parse_request_fst_line ic >>= function | `Eof -> return `Eof | `Invalid reason as r -> return r + | `Ok (meth, "*", version) -> + Header_IO.parse ic >>= fun headers -> + let uri = match Header.get headers "host" with + | None -> Uri.of_string "" + | Some host -> + let host_uri = Uri.of_string ("//"^host) in + let uri = Uri.(with_host (of_string "") (host host_uri)) in + Uri.(with_port uri (port host_uri)) + in + return_request headers meth uri version | `Ok (`CONNECT as meth, authority, version) -> Header_IO.parse ic >>= fun headers -> let uri = Uri.of_string ("//"^authority) in - let encoding = Header.get_transfer_encoding headers in - return (`Ok { headers; meth; uri; version; encoding }) + return_request headers meth uri version | `Ok (meth, request_uri_s, version) -> Header_IO.parse ic >>= fun headers -> let uri = Uri.of_string request_uri_s in @@ -136,8 +149,7 @@ module Make(IO : S.IO) = struct let uri = Uri.with_host pqs (Uri.host host_uri) in Uri.with_port uri (Uri.port host_uri) in - let encoding = Header.get_transfer_encoding headers in - return (`Ok { headers; meth; uri; version; encoding }) + return_request headers meth uri version (* Defined for method types in RFC7231 *) let has_body req = diff --git a/lib_test/test_request.ml b/lib_test/test_request.ml index a1a7bf960c..f96260c973 100644 --- a/lib_test/test_request.ml +++ b/lib_test/test_request.ml @@ -169,12 +169,12 @@ let parse_request_connect_host _ = let parse_request_options _ = let r = "OPTIONS * HTTP/1.1\r\n\r\n" in - let uri = Uri.of_string "/*" in + let uri = Uri.of_string "" in parse_request_uri_ r uri "parse_request_options" let parse_request_options_host _ = let r = "OPTIONS * HTTP/1.1\r\nHost: example.com:443\r\n\r\n" in - let uri = Uri.of_string "//example.com:443/*" in + let uri = Uri.of_string "//example.com:443" in parse_request_uri_ r uri "parse_request_options_host" let _ = From dbd6147d85d43470c14ccbe88c73bc0f70ec93ff Mon Sep 17 00:00:00 2001 From: David Sheets Date: Wed, 15 Apr 2015 11:53:47 +0100 Subject: [PATCH 20/23] Turn on some useful warnings as errors --- _tags | 1 + 1 file changed, 1 insertion(+) diff --git a/_tags b/_tags index 32f177d910..664767ca27 100644 --- a/_tags +++ b/_tags @@ -656,3 +656,4 @@ true: annot, bin_annot : custom # OASIS_STOP true: principal, strict_sequence, debug +true: warn(@5@8@10@11@12@14@23@24@26@29) From 2c2f93abb73d3cae3487f9246680a7d222a3f9f2 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Wed, 15 Apr 2015 11:54:07 +0100 Subject: [PATCH 21/23] Mark CONNECT and TRACE requests as without body --- lib/request.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/request.ml b/lib/request.ml index 887c296f28..2066a93507 100644 --- a/lib/request.ml +++ b/lib/request.ml @@ -154,8 +154,9 @@ module Make(IO : S.IO) = struct (* Defined for method types in RFC7231 *) let has_body req = match req.meth with - | `GET | `HEAD | `DELETE -> `No - | `POST | `PUT | `PATCH | `OPTIONS | `Other _ -> Transfer.has_body req.encoding + | `GET | `HEAD | `DELETE | `CONNECT | `TRACE -> `No + | `POST | `PUT | `PATCH | `OPTIONS | `Other _ -> + Transfer.has_body req.encoding let make_body_reader req ic = Transfer_IO.make_reader req.encoding ic let read_body_chunk = Transfer_IO.read From 586d1b320236dd360e3402a54eadc8a6235508a6 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Wed, 15 Apr 2015 13:49:46 +0100 Subject: [PATCH 22/23] modify parse request URI tests to enable invalid request testing Also, introduce directory traversal tests --- lib_test/test_request.ml | 79 +++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/lib_test/test_request.ml b/lib_test/test_request.ml index f96260c973..69fc5f6c59 100644 --- a/lib_test/test_request.ml +++ b/lib_test/test_request.ml @@ -38,145 +38,155 @@ let opt_default default = function | None -> default | Some v -> v -let parse_request_uri_ r uri name = +let parse_request_uri_ r expected name = String_io.M.( StringRequest.read (String_io.open_in r) - >>= function - | `Ok { Request.uri = ruri } -> + >>= fun result -> match result, expected with + | `Ok { Request.uri = ruri }, `Ok uri -> let msg = Uri.(Printf.sprintf "expected %s %d %s %s\ngot %s %d %s %s" (opt_default "_" (host uri)) (opt_default (-1) (port uri)) (path uri) (encoded_of_query (query uri)) (opt_default "_" (host ruri)) - (opt_default (-1) (port uri)) + (opt_default (-1) (port ruri)) (path ruri) (encoded_of_query (query ruri)) ) in - assert_equal ~msg ruri uri - | _ -> assert_failure (name^" parse failed") + assert_equal ~msg uri ruri + | `Invalid rmsg, `Invalid msg -> + assert_equal rmsg msg + | _ -> assert_failure (name^" unexpected request parse result") ) +let bad_request = `Invalid "bad request URI" + let parse_request_uri _ = let r = "GET / HTTP/1.1\r\n\r\n" in - let uri = Uri.of_string "/" in + let uri = `Ok (Uri.of_string "/") in parse_request_uri_ r uri "parse_request_uri" let parse_request_uri_host _ = let r = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n" in - let uri = Uri.of_string "//example.com/" in + let uri = `Ok (Uri.of_string "//example.com/") in parse_request_uri_ r uri "parse_request_uri_host" let parse_request_uri_host_port _ = let r = "GET / HTTP/1.1\r\nHost: example.com:8080\r\n\r\n" in - let uri = Uri.of_string "//example.com:8080/" in + let uri = `Ok (Uri.of_string "//example.com:8080/") in parse_request_uri_ r uri "parse_request_uri_host_port" let parse_request_uri_double_slash _ = let r = "GET // HTTP/1.1\r\n\r\n" in - let uri = Uri.with_path (Uri.of_string "") "//" in + let uri = `Ok (Uri.with_path (Uri.of_string "") "//") in parse_request_uri_ r uri "parse_request_uri_double_slash" let parse_request_uri_host_double_slash _ = let r = "GET // HTTP/1.1\r\nHost: example.com\r\n\r\n" in - let uri = Uri.of_string "//example.com//" in + let uri = `Ok (Uri.of_string "//example.com//") in parse_request_uri_ r uri "parse_request_uri_host_double_slash" let parse_request_uri_triple_slash _ = let r = "GET /// HTTP/1.1\r\n\r\n" in - let uri = Uri.with_path (Uri.of_string "") "///" in + let uri = `Ok (Uri.with_path (Uri.of_string "") "///") in parse_request_uri_ r uri "parse_request_uri_triple_slash" let parse_request_uri_host_triple_slash _ = let r = "GET /// HTTP/1.1\r\nHost: example.com\r\n\r\n" in - let uri = Uri.of_string "//example.com///" in + let uri = `Ok (Uri.of_string "//example.com///") in parse_request_uri_ r uri "parse_request_uri_host_triple_slash" let parse_request_uri_no_slash _ = let r = "GET foo HTTP/1.1\r\n\r\n" in - let uri = Uri.of_string "/foo" in - parse_request_uri_ r uri "parse_request_uri_no_slash" + parse_request_uri_ r bad_request "parse_request_uri_no_slash" let parse_request_uri_host_no_slash _ = let r = "GET foo HTTP/1.1\r\nHost: example.com\r\n\r\n" in - let uri = Uri.of_string "//example.com/foo" in - parse_request_uri_ r uri "parse_request_uri_host_no_slash" + parse_request_uri_ r bad_request "parse_request_uri_host_no_slash" let parse_request_uri_empty _ = let r = "GET HTTP/1.1\r\n\r\n" in - let uri = Uri.of_string "/" in + let uri = `Ok (Uri.of_string "/") in parse_request_uri_ r uri "parse_request_uri_empty" let parse_request_uri_host_empty _ = let r = "GET HTTP/1.1\r\nHost: example.com\r\n\r\n" in - let uri = Uri.of_string "//example.com/" in + let uri = `Ok (Uri.of_string "//example.com/") in parse_request_uri_ r uri "parse_request_uri_host_empty" let parse_request_uri_path_like_scheme _ = let r = "GET http://example.net HTTP/1.1\r\n\r\n" in - let uri = Uri.of_string "http://example.net/" in + let uri = `Ok (Uri.of_string "http://example.net/") in parse_request_uri_ r uri "parse_request_uri_path_like_scheme" let parse_request_uri_host_path_like_scheme _ = let r = "GET http://example.net HTTP/1.1\r\nHost: example.com\r\n\r\n" in - let uri = Uri.of_string "http://example.net/" in + let uri = `Ok (Uri.of_string "http://example.net/") in parse_request_uri_ r uri "parse_request_uri_host_path_like_scheme" let parse_request_uri_path_like_host_port _ = let path = "//example.net:8080" in let r = "GET "^path^" HTTP/1.1\r\n\r\n" in - let uri = Uri.with_path (Uri.of_string "") path in + let uri = `Ok (Uri.with_path (Uri.of_string "") path) in parse_request_uri_ r uri "parse_request_uri_path_like_host_port" let parse_request_uri_host_path_like_host_port _ = let path = "//example.net:8080" in let r = "GET "^path^" HTTP/1.1\r\nHost: example.com\r\n\r\n" in - let uri = Uri.with_path (Uri.of_string "//example.com") path in + let uri = `Ok (Uri.with_path (Uri.of_string "//example.com") path) in parse_request_uri_ r uri "parse_request_uri_host_path_like_host_port" let parse_request_uri_query _ = let pqs = "/?foo" in let r = "GET "^pqs^" HTTP/1.1\r\n\r\n" in - let uri = Uri.of_string pqs in + let uri = `Ok (Uri.of_string pqs) in parse_request_uri_ r uri "parse_request_uri_query" let parse_request_uri_host_query _ = let pqs = "/?foo" in let r = "GET "^pqs^" HTTP/1.1\r\nHost: example.com\r\n\r\n" in - let uri = Uri.of_string ("//example.com"^pqs) in + let uri = `Ok (Uri.of_string ("//example.com"^pqs)) in parse_request_uri_ r uri "parse_request_uri_host_query" let parse_request_uri_query_no_slash _ = let r = "GET ?foo HTTP/1.1\r\n\r\n" in - let uri = Uri.of_string "/?foo" in - parse_request_uri_ r uri "parse_request_uri_query_no_slash" + parse_request_uri_ r bad_request "parse_request_uri_query_no_slash" let parse_request_uri_host_query_no_slash _ = let r = "GET ?foo HTTP/1.1\r\nHost: example.com\r\n\r\n" in - let uri = Uri.of_string "//example.com/?foo" in - parse_request_uri_ r uri "parse_request_uri_host_query_no_slash" + parse_request_uri_ r bad_request "parse_request_uri_host_query_no_slash" let parse_request_connect _ = let r = "CONNECT vpn.example.net:443 HTTP/1.1\r\n" in - let uri = Uri.of_string "//vpn.example.net:443" in + let uri = `Ok (Uri.of_string "//vpn.example.net:443") in parse_request_uri_ r uri "parse_request_connect" let parse_request_connect_host _ = let r = "CONNECT vpn.example.net:443 HTTP/1.1\r\nHost: vpn.example.com:443\r\n\r\n" in - let uri = Uri.of_string "//vpn.example.net:443" in + let uri = `Ok (Uri.of_string "//vpn.example.net:443") in parse_request_uri_ r uri "parse_request_connect_host" let parse_request_options _ = let r = "OPTIONS * HTTP/1.1\r\n\r\n" in - let uri = Uri.of_string "" in + let uri = `Ok (Uri.of_string "") in parse_request_uri_ r uri "parse_request_options" let parse_request_options_host _ = let r = "OPTIONS * HTTP/1.1\r\nHost: example.com:443\r\n\r\n" in - let uri = Uri.of_string "//example.com:443" in + let uri = `Ok (Uri.of_string "//example.com:443") in parse_request_uri_ r uri "parse_request_options_host" +let parse_request_uri_traversal _ = + let r = "GET /../../../../etc/shadow HTTP/1.1\r\n\r\n" in + let uri = `Ok (Uri.of_string "/etc/shadow") in + parse_request_uri_ r uri "parse_request_uri_traversal" + +let parse_request_uri_host_traversal _ = + let r = "GET /../../../../etc/shadow HTTP/1.1\r\nHost: example.com\r\n\r\n" in + let uri = `Ok (Uri.of_string "//example.com/etc/shadow") in + parse_request_uri_ r uri "parse_request_uri_host_traversal" + let _ = ("Request" >::: [ "Test header has auth" >:: header_has_auth @@ -213,4 +223,7 @@ let _ = ; "Parse CONNECT request URI with host" >:: parse_request_connect_host ; "Parse OPTIONS request URI" >:: parse_request_options ; "Parse OPTIONS request URI with host" >:: parse_request_options_host + ; "Parse request URI parent traversal" >:: parse_request_uri_traversal + ; "Parse request URI parent traversal with host" + >:: parse_request_uri_host_traversal ]) |> run_test_tt_main From 7a639f38d57d33610b88f2bf05c448c040a77894 Mon Sep 17 00:00:00 2001 From: David Sheets Date: Wed, 15 Apr 2015 13:50:29 +0100 Subject: [PATCH 23/23] Return invalid request message for bad request URIs --- lib/request.ml | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/lib/request.ml b/lib/request.ml index 2066a93507..c9edc633a4 100644 --- a/lib/request.ml +++ b/lib/request.ml @@ -127,29 +127,37 @@ module Make(IO : S.IO) = struct | `Ok (meth, request_uri_s, version) -> Header_IO.parse ic >>= fun headers -> let uri = Uri.of_string request_uri_s in - let uri = match Uri.scheme uri with + match Uri.scheme uri with | Some _ -> (* we have an absoluteURI *) - Uri.(match path uri with "" -> with_path uri "/" | _ -> uri) + let uri = Uri.( + match path uri with "" -> with_path uri "/" | _ -> uri + ) in + return_request headers meth uri version | None -> - let empty = Uri.of_string "" in - let empty_base = Uri.of_string "///" in - let pqs = match Stringext.split ~max:2 request_uri_s ~on:'?' with - | [] -> empty_base - | [path] -> Uri.resolve "http" empty_base (Uri.with_path empty path) - | path::qs::_ -> - let path_base = + let len = String.length request_uri_s in + if len > 0 && String.get request_uri_s 0 <> '/' + then return (`Invalid "bad request URI") + else + let empty = Uri.of_string "" in + let empty_base = Uri.of_string "///" in + let pqs = match Stringext.split ~max:2 request_uri_s ~on:'?' with + | [] -> empty_base + | [path] -> Uri.resolve "http" empty_base (Uri.with_path empty path) - in - Uri.with_query path_base (Uri.query_of_encoded qs) - in - match Header.get headers "host" with - | None -> Uri.(with_scheme (with_host pqs None) None) - | Some host -> - let host_uri = Uri.of_string ("//"^host) in - let uri = Uri.with_host pqs (Uri.host host_uri) in - Uri.with_port uri (Uri.port host_uri) - in - return_request headers meth uri version + | path::qs::_ -> + let path_base = + Uri.resolve "http" empty_base (Uri.with_path empty path) + in + Uri.with_query path_base (Uri.query_of_encoded qs) + in + let uri = match Header.get headers "host" with + | None -> Uri.(with_scheme (with_host pqs None) None) + | Some host -> + let host_uri = Uri.of_string ("//"^host) in + let uri = Uri.with_host pqs (Uri.host host_uri) in + Uri.with_port uri (Uri.port host_uri) + in + return_request headers meth uri version (* Defined for method types in RFC7231 *) let has_body req =