Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TE header in client. Check TE header in server #941

Merged
merged 2 commits into from
Oct 11, 2022
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
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## Unreleased
- cohttp-eio: add TE header in client. Check TE header is server (bikallem #941)
- cohttp-eio: add User-Agent header to request from Client (bikallem #940)
- cohttp-eio: add Content-Length header to request/response (bikallem #929)
- cohttp-eio: add cohttp-eio client api - Cohttp_eio.Client (bikallem #879)
Expand Down
10 changes: 6 additions & 4 deletions cohttp-eio/src/body.ml
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ let read_chunked reader headers f =
| _ -> None

(* https://datatracker.ietf.org/doc/html/rfc7230#section-4.1 *)
let write_chunked writer chunk_writer =
let write_chunked ?(write_chunked_trailers = false) writer chunk_writer =
let write_extensions exts =
List.iter
(fun { name; value } ->
Expand All @@ -241,13 +241,15 @@ let write_chunked writer chunk_writer =
Buf_write.string writer "\r\n"
in
chunk_writer.body_writer write_body;
chunk_writer.trailer_writer (Rwer.write_headers writer);
if write_chunked_trailers then
chunk_writer.trailer_writer (Rwer.write_headers writer);
Buf_write.string writer "\r\n"

let write_body writer body =
let write_body ?write_chunked_trailers writer body =
match body with
| Fixed s -> Buf_write.string writer s
| Chunked chunk_writer -> write_chunked writer chunk_writer
| Chunked chunk_writer ->
write_chunked ?write_chunked_trailers writer chunk_writer
| Custom f -> f writer
| Empty -> ()

Expand Down
16 changes: 8 additions & 8 deletions cohttp-eio/src/client.ml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type 'a body_allowed_call =
response

(* Request line https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.1 *)
let write_request writer request body =
let write_request request writer body =
let headers =
Body.add_content_length
(Http.Request.requires_content_length request)
Expand All @@ -42,7 +42,7 @@ let write_request writer request body =
Buf_write.string writer "\r\n";
Rwer.write_headers writer headers;
Buf_write.string writer "\r\n";
Body.write_body writer body
Body.write_body ~write_chunked_trailers:true writer body

(* response parser *)

Expand Down Expand Up @@ -85,12 +85,12 @@ let call ?meth ?version ?(headers = Http.Header.init ()) ?(body = Body.Empty)
let headers =
Http.Header.add_unless_exists headers "User-Agent" "cohttp-eio"
in
let request = Http.Request.make ?meth ?version ~headers resource_path in
Buf_write.with_flow ~initial_size:0x1000 conn (fun writer ->
write_request writer request body;
let reader =
Eio.Buf_read.of_flow ~initial_size:0x1000 ~max_size:max_int conn
in
let initial_size = 0x1000 in
Buf_write.with_flow ~initial_size conn (fun writer ->
let request = Http.Request.make ?meth ?version ~headers resource_path in
let request = Http.Request.add_te_trailers request in
write_request request writer body;
let reader = Eio.Buf_read.of_flow ~initial_size ~max_size:max_int conn in
let response = response reader in
(response, reader))

Expand Down
1 change: 1 addition & 0 deletions cohttp-eio/src/rwer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ let http_headers r =
Http.Header.of_list (aux ())

let write_headers writer headers =
let headers = Http.Header.clean_dup headers in
Http.Header.iter
(fun k v ->
Buf_write.string writer k;
Expand Down
12 changes: 7 additions & 5 deletions cohttp-eio/src/server.ml
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ let internal_server_error_response =
let bad_request_response =
(Http.Response.make ~status:`Bad_request (), Body.Empty)

let write_response writer ?request_meth (response, body) =
let write_response ?request writer (response, body) =
let headers =
let request_meth = Option.map Http.Request.meth request in
Body.add_content_length
(Http.Response.requires_content_length ?request_meth response)
(Http.Response.headers response)
Expand All @@ -80,7 +81,10 @@ let write_response writer ?request_meth (response, body) =
Buf_write.string writer "\r\n";
Rwer.write_headers writer headers;
Buf_write.string writer "\r\n";
Body.write_body writer body
let write_chunked_trailers =
Option.map Http.Request.supports_chunked_trailers request
in
Body.write_body ?write_chunked_trailers writer body

(* request parsers *)

Expand Down Expand Up @@ -108,9 +112,7 @@ let rec handle_request client_addr reader writer flow handler =
match http_request reader with
| request ->
let response, body = handler (request, reader, client_addr) in
write_response writer
~request_meth:(Http.Request.meth request)
(response, body);
write_response ~request writer (response, body);
if Http.Request.is_keep_alive request then
handle_request client_addr reader writer flow handler
| (exception End_of_file) | (exception Eio.Net.Connection_reset _) -> ()
Expand Down
6 changes: 3 additions & 3 deletions cohttp-eio/tests/test_client.t
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Test Client.get
version: HTTP/1.1
headers: Header {
Accept = "application/json"; Host = "localhost:8082";
User-Agent = "cohttp-eio" }
User-Agent = "cohttp-eio"; TE = "trailers"; Connection = "TE" }

$ kill ${running_pid}

Expand All @@ -24,7 +24,7 @@ Test Client.post
version: HTTP/1.1
headers: Header {
Accept = "application/json"; Content-Length = "12"; Host = "localhost:8082";
User-Agent = "cohttp-eio" }
User-Agent = "cohttp-eio"; TE = "trailers"; Connection = "TE" }

hello world!

Expand All @@ -42,7 +42,7 @@ Test posting "chunked" data
headers: Header {
Content-Length = "23"; Header1 = "Header1 value text";
Content-Type = "text/plain"; Host = "localhost:8082";
User-Agent = "cohttp-eio" }
User-Agent = "cohttp-eio"; TE = "trailers"; Connection = "TE" }

size: 7
data: Mozilla
Expand Down
8 changes: 8 additions & 0 deletions http/src/http.ml
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,14 @@ module Request = struct

let content_length t = content_length (requires_content_length t) t.headers

let supports_chunked_trailers t =
Header.get_multi t.headers "TE" |> List.mem "trailers"

let add_te_trailers t =
let headers = Header.add t.headers "TE" "trailers" in
let headers = Header.add headers "Connection" "TE" in
{ t with headers }

(* Defined for method types in RFC7231 *)
let has_body req =
match req.meth with
Expand Down
8 changes: 8 additions & 0 deletions http/src/http.mli
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,14 @@ module Request : sig

See https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2 *)

val supports_chunked_trailers : t -> bool
(** [supports_chunked_trailers t] is [true] if [t] contains HTTP header "TE:
trailers". Otherwise it is [false]. *)

val add_te_trailers : t -> t
(** [add_te_trailers t] adds HTTP headers, 'TE' and 'Connection' to
indicate that a user-agent can handle HTTP chunked trailers headers. *)

val make :
?meth:Method.t ->
?version:Version.t ->
Expand Down