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
6 changes: 6 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ New features and bug fixes:
an override wasn't supplied, and to initialise a fresh Header value
if none is present.
* Do not override user-supplied headers in `post_form` or `redirect`.
* `Request.has_body` does not permit a body to be set for methods that
RFC7231 forbids from having one (`HEAD`, `GET` and `DELETE`).
* `Request.make` does not inject a `transfer-encoding` header if there
is no body present in the request (#246).
* `Server.respond` no longer overrides user-supplied headers that
specify the `content-length` or `transfer-encoding` headers (#268).

0.15.2 (2015-02-15):
* When transfer encoding is unknown, read until EOF when body size is unknown. (#241)
Expand Down
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@ generate:
clean:
ocamlbuild -clean
rm -f setup.data setup.log setup.bin

revdep:
opam switch system || true
opam switch remove -y cohttp-revdeps || true
opam switch -A system cohttp-revdeps
opam pin -y add cohttp .
for i in `opam list -s --rec --depends-on=cohttp`; do opam install -y -j 4 $$i; done
opam switch system
33 changes: 24 additions & 9 deletions bin/cohttp_proxy_lwt.ml
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,40 @@ open Cohttp_lwt_unix

let handler ~verbose (ch,conn) req body =
let uri = Cohttp.Request.uri req in
let path = Uri.path uri in
(* Log the request to the console *)
printf "%s %s %s\n%!"
if verbose then eprintf "--> %s %s %s\n%!"
(Cohttp.(Code.string_of_method (Request.meth req)))
(Uri.to_string uri)
(Sexplib.Sexp.to_string_hum (Conduit_lwt_unix.sexp_of_flow ch));
(* Fetch the URI *)
let headers = Request.headers req in
(Sexplib.Sexp.to_string_hum (Request.sexp_of_t req));
(* Strip out hop-by-hop connection headers *)
let headers =
Request.headers req |> fun h ->
Header.remove h "accept-encoding" |> fun h ->
Header.remove h "content-length" |> fun h ->
Header.remove h "transfer-encoding" |> fun h ->
Header.remove h "connection" |> fun h ->
Header.add h "accept-encoding" "identity"
in
(* Fetch the remote URI *)
let meth = Request.meth req in
Client.call ~headers meth uri >>= fun (resp, body) ->
let headers = Response.headers resp in
Client.call ~headers ~body meth uri >>= fun (resp, body) ->
if verbose then
eprintf "<-- %s %s\n%!"
(Uri.to_string (Request.uri req))
(Sexplib.Sexp.to_string_hum (Response.sexp_of_t resp));
let status = Response.status resp in
let headers =
Response.headers resp |> fun h ->
Header.remove h "transfer-encoding" |> fun h ->
Header.remove h "content-length" |> fun h ->
Header.remove h "connection"
in
Server.respond ~headers ~status ~body ()

let start_proxy port host verbose cert key () =
printf "Listening for HTTP request on: %s %d\n%!" host port;
let info = sprintf "Served by Cohttp/Lwt listening on %s:%d" host port in
let conn_closed (ch,conn) =
printf "connection %s closed\n%!"
printf "Connection %s closed\n%!"
(Sexplib.Sexp.to_string_hum (Conduit_lwt_unix.sexp_of_flow ch)) in
let callback = handler ~verbose in
let config = Server.make ~callback ~conn_closed () in
Expand Down
11 changes: 9 additions & 2 deletions lib/request.ml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,11 @@ module Make(IO : S.IO) = struct
let encoding = Header.get_transfer_encoding headers in
return (`Ok { headers; meth; uri; version; encoding })

let has_body req = Transfer.has_body req.encoding
(* 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

let make_body_reader req ic = Transfer_IO.make_reader req.encoding ic
let read_body_chunk = Transfer_IO.read
Expand All @@ -136,7 +140,10 @@ module Make(IO : S.IO) = struct
| Some p -> ":" ^ string_of_int p
| None -> ""
) in
let headers = Header.add_transfer_encoding headers req.encoding in
let headers =
match has_body req with
| `Yes | `Unknown -> Header.add_transfer_encoding headers req.encoding
| `No -> headers in
IO.write oc fst_line >>= fun _ ->
iter (IO.write oc) (Header.to_lines headers) >>= fun _ ->
IO.write oc "\r\n"
Expand Down
9 changes: 8 additions & 1 deletion lwt/cohttp_lwt.ml
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,14 @@ module Make_server(IO:IO)
Filename.concat docroot rel_path

let respond ?headers ?(flush=true) ~status ~body () =
let encoding = Cohttp_lwt_body.transfer_encoding body in
let encoding =
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't mind seeing this in its own function for testing purposes. Although I wouldn't want the user to be able to use it...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably better to test the expected behaviour of respond isnt it? With and without user-specified headers and a few different body formats.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little more work but you're right that it's better from a rigor perspective.

I did originally think that this logic could be reused between async/lwt. Not the case however.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a very good point though. There's quite a bit of subtle logic that could be pulled into Cohttp itself. Am putting together an Async proxy now

match headers with
| None -> Cohttp_lwt_body.transfer_encoding body
| Some headers ->
match Header.get_transfer_encoding headers with
| Transfer.Unknown -> Cohttp_lwt_body.transfer_encoding body
| t -> t
in
let res = Response.make ~status ~flush ~encoding ?headers () in
return (res, body)

Expand Down
6 changes: 5 additions & 1 deletion lwt/cohttp_lwt.mli
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,11 @@ module type Server = sig
(** [respond ?headers ?flush ~status ~body] will respond to an HTTP
request with the given [status] code and response [body]. If
[flush] is true, then every response chunk will be flushed to
the network rather than being buffered. [flush] is true by default. *)
the network rather than being buffered. [flush] is true by default.
The transfer encoding will be detected from the [body] value and
set to chunked encoding if it cannot be determined immediately.
You can override the encoding by supplying an appropriate [Content-length]
or [Transfer-encoding] in the [headers] parameter. *)
val respond :
?headers:Cohttp.Header.t ->
?flush:bool ->
Expand Down
2 changes: 1 addition & 1 deletion opam
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ depends: [
"ocamlfind" {build}
"cmdliner" {build & >= "0.9.4"}
"re"
"uri" {>= "1.5.0"}
"uri" {>= "1.8.0"}
"fieldslib" {>= "109.20.00"}
"sexplib" {>= "109.53.00"}
"conduit" {>= "0.7.0"}
Expand Down