Skip to content

Commit b84a161

Browse files
committed
Ocsigen_response: Eio-compatible Body
This changes the representation of the body of the response to be compatible with Cohttp-eio. This now uses 'Cohttp_lwt_unix.Server.make_expert', which expect a representation for the body that is closer to direct-style instead of using the same Body type as for requests. The setting of cookie headers is moved into 'Ocsigen_response', with the goal of building a safer API.
1 parent 395125c commit b84a161

File tree

4 files changed

+116
-58
lines changed

4 files changed

+116
-58
lines changed

src/server/dune

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
(libraries
66
xml-light
77
cohttp-lwt-unix
8+
http
89
polytables
910
ocsigen_cookie_map
1011
baselib

src/server/ocsigen_cohttp.ml

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -54,32 +54,6 @@ module Cookie = struct
5454
Ocsigen_cookie_map.Map_path.fold serialize_cookies cookies headers
5555
end
5656

57-
(* FIXME: secure *)
58-
let make_cookies_header path exp name c _secure =
59-
Format.sprintf "%s=%s%s%s" name c
60-
(*VVV encode = true? *)
61-
("; path=/" ^ Ocsigen_lib.Url.string_of_url_path ~encode:true path)
62-
(* (if secure && slot.sl_ssl then "; secure" else "")^ *)
63-
""
64-
^
65-
match exp with
66-
| Some s -> "; expires=" ^ Ocsigen_lib.Date.to_string s
67-
| None -> ""
68-
69-
let make_cookies_headers path t hds =
70-
Ocsigen_cookie_map.Map_inner.fold
71-
(fun name c h ->
72-
let open Ocsigen_cookie_map in
73-
let exp, v, secure =
74-
match c with
75-
| OUnset -> Some 0., "", false
76-
| OSet (t, v, secure) -> t, v, secure
77-
in
78-
Cohttp.Header.add h
79-
Ocsigen_header.Name.(to_string set_cookie)
80-
(make_cookies_header path exp name v secure))
81-
t hds
82-
8357
let handler ~ssl ~address ~port ~connector (flow, conn) request body =
8458
let filenames = ref [] in
8559
let edn = Conduit_lwt_unix.endp_of_flow flow in
@@ -133,6 +107,7 @@ let handler ~ssl ~address ~port ~connector (flow, conn) request body =
133107
Cohttp_lwt_unix.Server.respond_error ?headers
134108
~status:(ret_code :> Cohttp.Code.status_code)
135109
~body ()
110+
>>= fun resp -> Lwt.return (Ocsigen_response.of_cohttp resp)
136111
in
137112
(* TODO: equivalent of Ocsigen_range *)
138113
let request =
@@ -155,32 +130,17 @@ let handler ~ssl ~address ~port ~connector (flow, conn) request body =
155130
Ocsigen_header.Name.x_forwarded_for))
156131
(Uri.path (Ocsigen_request.uri request)));
157132
Lwt.catch
158-
(fun () ->
159-
connector request >>= fun response ->
160-
let response, body = Ocsigen_response.to_cohttp response
161-
and cookies = Ocsigen_response.cookies response in
162-
let response =
163-
let headers =
164-
Cohttp.Header.add_unless_exists
165-
(Cohttp.Header.add_unless_exists
166-
(Ocsigen_cookie_map.Map_path.fold make_cookies_headers
167-
cookies
168-
(Cohttp.Response.headers response))
169-
"server" Ocsigen_config.server_name)
170-
"date"
171-
(Ocsigen_lib.Date.to_string (Unix.time ()))
172-
in
173-
{response with Cohttp.Response.headers}
174-
in
175-
Lwt.return (response, body))
133+
(fun () -> connector request)
176134
(function
177135
| Ocsigen_is_dir fun_request ->
178136
let headers =
179137
fun_request request |> Uri.to_string
180138
|> Cohttp.Header.init_with "location"
181139
and status = `Moved_permanently in
182-
Cohttp_lwt_unix.Server.respond ~headers ~status ~body:`Empty ()
183-
| exn -> handle_error exn))
140+
Lwt.return (Ocsigen_response.respond ~headers ~status ())
141+
| exn -> handle_error exn)
142+
>>= fun response ->
143+
Lwt.return (Ocsigen_response.to_response_expert response))
184144
(fun () ->
185145
if !filenames <> []
186146
then
@@ -235,7 +195,7 @@ let service ?ssl ~address ~port ~connector () =
235195
and ssl = match ssl with Some _ -> true | None -> false in
236196
handler ~ssl ~address ~port ~connector
237197
in
238-
let config = Cohttp_lwt_unix.Server.make ~conn_closed ~callback () in
198+
let config = Cohttp_lwt_unix.Server.make_expert ~conn_closed ~callback () in
239199
let mode =
240200
match tls_own_key with
241201
| `None -> `TCP (`Port port)

src/server/ocsigen_response.ml

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,37 @@
1+
module Body = struct
2+
type t = ((string -> unit Lwt.t) -> unit Lwt.t) * Cohttp.Transfer.encoding
3+
4+
let empty : t = (fun _write -> Lwt.return_unit), Fixed 0L
5+
let make encoding writer = writer, encoding
6+
7+
let of_cohttp body =
8+
( (fun write -> Cohttp_lwt.Body.write_body write body)
9+
, Cohttp_lwt.Body.transfer_encoding body )
10+
11+
let write (w, _) = w
12+
let transfer_encoding = snd
13+
end
14+
115
type t =
216
{ a_response : Cohttp.Response.t
3-
; a_body : Cohttp_lwt.Body.t
17+
; a_body : Body.t
418
; a_cookies : Ocsigen_cookie_map.t }
519

6-
let make
7-
?(body = Cohttp_lwt.Body.empty)
8-
?(cookies = Ocsigen_cookie_map.empty)
9-
a_response
10-
=
20+
let make ?(body = Body.empty) ?(cookies = Ocsigen_cookie_map.empty) a_response =
1121
{a_response; a_body = body; a_cookies = cookies}
1222

23+
let respond ?headers ~status ?(body = Body.empty) () =
24+
let encoding =
25+
match headers with
26+
| None -> Body.transfer_encoding body
27+
| Some headers -> (
28+
match Cohttp.Header.get_transfer_encoding headers with
29+
| Cohttp.Transfer.Unknown -> Body.transfer_encoding body
30+
| t -> t)
31+
in
32+
let response = Cohttp.Response.make ~status ~encoding ?headers () in
33+
make ~body response
34+
1335
let update ?response ?body ?cookies {a_response; a_body; a_cookies} =
1436
let a_response =
1537
match response with Some response -> response | None -> a_response
@@ -19,10 +41,53 @@ let update ?response ?body ?cookies {a_response; a_body; a_cookies} =
1941
in
2042
{a_response; a_body; a_cookies}
2143

22-
let of_cohttp ?(cookies = Ocsigen_cookie_map.empty) (a_response, a_body) =
44+
let of_cohttp ?(cookies = Ocsigen_cookie_map.empty) (a_response, body) =
45+
let a_body = Body.of_cohttp body in
2346
{a_response; a_body; a_cookies = cookies}
2447

25-
let to_cohttp {a_response; a_body; _} = a_response, a_body
48+
(* FIXME: secure *)
49+
let make_cookies_header path exp name c _secure =
50+
Format.sprintf "%s=%s%s%s" name c
51+
(*VVV encode = true? *)
52+
("; path=/" ^ Ocsigen_lib.Url.string_of_url_path ~encode:true path)
53+
(* (if secure && slot.sl_ssl then "; secure" else "")^ *)
54+
""
55+
^
56+
match exp with
57+
| Some s -> "; expires=" ^ Ocsigen_lib.Date.to_string s
58+
| None -> ""
59+
60+
let make_cookies_headers path t hds =
61+
Ocsigen_cookie_map.Map_inner.fold
62+
(fun name c h ->
63+
let open Ocsigen_cookie_map in
64+
let exp, v, secure =
65+
match c with
66+
| OUnset -> Some 0., "", false
67+
| OSet (t, v, secure) -> t, v, secure
68+
in
69+
Cohttp.Header.add h
70+
Ocsigen_header.Name.(to_string set_cookie)
71+
(make_cookies_header path exp name v secure))
72+
t hds
73+
74+
let to_cohttp_response {a_response; a_cookies; _} =
75+
let headers =
76+
Cohttp.Header.add_unless_exists
77+
(Cohttp.Header.add_unless_exists
78+
(Ocsigen_cookie_map.Map_path.fold make_cookies_headers a_cookies
79+
(Cohttp.Response.headers a_response))
80+
"server" Ocsigen_config.server_name)
81+
"date"
82+
(Ocsigen_lib.Date.to_string (Unix.time ()))
83+
in
84+
{a_response with Cohttp.Response.headers}
85+
86+
let to_response_expert t =
87+
to_cohttp_response t, fun _ic oc -> fst t.a_body (Lwt_io.write oc)
88+
89+
let response t = t.a_response
90+
let body t = t.a_body
2691

2792
let status {a_response = {Cohttp.Response.status; _}; _} =
2893
match status with

src/server/ocsigen_response.mli

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,40 @@
11
type t
22

3+
module Body : sig
4+
type t
5+
6+
val empty : t
7+
8+
val make :
9+
Cohttp.Transfer.encoding
10+
-> ((string -> unit Lwt.t) -> unit Lwt.t)
11+
-> t
12+
(** [make writer] makes a reponse body whose content is generated by
13+
[writer write]. [write str] blocks until [str] is fully written. *)
14+
15+
val of_cohttp : Cohttp_lwt.Body.t -> t
16+
val write : t -> (string -> unit Lwt.t) -> unit Lwt.t
17+
val transfer_encoding : t -> Cohttp.Transfer.encoding
18+
end
19+
320
val make :
4-
?body:Cohttp_lwt.Body.t
21+
?body:Body.t
522
-> ?cookies:Ocsigen_cookie_map.t
623
-> Cohttp.Response.t
724
-> t
825

26+
val respond :
27+
?headers:Cohttp.Header.t
28+
-> status:Http.Status.t
29+
-> ?body:Body.t
30+
-> unit
31+
-> t
32+
(** Like [make] but with an interface similar to
33+
[Cohttp_lwt_unix.Server.respond]. *)
34+
935
val update :
1036
?response:Cohttp.Response.t
11-
-> ?body:Cohttp_lwt.Body.t
37+
-> ?body:Body.t
1238
-> ?cookies:Ocsigen_cookie_map.t
1339
-> t
1440
-> t
@@ -18,7 +44,13 @@ val of_cohttp :
1844
-> Cohttp.Response.t * Cohttp_lwt.Body.t
1945
-> t
2046

21-
val to_cohttp : t -> Cohttp.Response.t * Cohttp_lwt.Body.t
47+
val to_response_expert :
48+
t
49+
-> Cohttp.Response.t * ('ic -> Lwt_io.output_channel -> unit Lwt.t)
50+
(** Response for [Cohttp_lwt_unix.Server.make_expert]. *)
51+
52+
val response : t -> Cohttp.Response.t
53+
val body : t -> Body.t
2254
val status : t -> Cohttp.Code.status
2355
val set_status : t -> Cohttp.Code.status -> t
2456
val cookies : t -> Ocsigen_cookie_map.t

0 commit comments

Comments
 (0)