store http version in request, and close connections when needed

cases include:
- http 1.0 and no "connection: keep-alive"
- http 1.1 and "connection: close"
This commit is contained in:
Simon Cruanes 2021-12-12 20:51:45 -05:00
parent 2d2ffc722a
commit a953f0799b
No known key found for this signature in database
GPG key ID: 4AC01D0849AA62B6
2 changed files with 21 additions and 7 deletions

View file

@ -363,6 +363,7 @@ module Request = struct
meth: Meth.t; meth: Meth.t;
host: string; host: string;
headers: Headers.t; headers: Headers.t;
http_version: int*int;
path: string; path: string;
path_components: string list; path_components: string list;
query: (string*string) list; query: (string*string) list;
@ -384,6 +385,13 @@ module Request = struct
| None -> None | None -> None
let set_header self k v = {self with headers=Headers.set k v self.headers} let set_header self k v = {self with headers=Headers.set k v self.headers}
(** Should we close the connection after this request? *)
let close_after_req (self:_ t) : bool =
match self.http_version with
| 1, 1 -> get_header self "connection" =Some"close"
| 1, 0 -> not (get_header self "connection"=Some"keep-alive")
| _ -> false
let pp_comp_ out comp = let pp_comp_ out comp =
Format.fprintf out "[%s]" Format.fprintf out "[%s]"
(String.concat ";" @@ List.map (Printf.sprintf "%S") comp) (String.concat ";" @@ List.map (Printf.sprintf "%S") comp)
@ -483,11 +491,11 @@ module Request = struct
let parse_req_start ~buf (bs:byte_stream) : unit t option resp_result = let parse_req_start ~buf (bs:byte_stream) : unit t option resp_result =
try try
let line = Byte_stream.read_line ~buf bs in let line = Byte_stream.read_line ~buf bs in
let meth, path = let meth, path, version =
try try
let m, p, v = Scanf.sscanf line "%s %s HTTP/1.%d\r" (fun x y z->x,y,z) in let meth, path, version = Scanf.sscanf line "%s %s HTTP/1.%d\r" (fun x y z->x,y,z) in
if v != 0 && v != 1 then raise Exit; if version != 0 && version != 1 then raise Exit;
m, p meth, path, version
with _ -> with _ ->
_debug (fun k->k "invalid request line: `%s`" line); _debug (fun k->k "invalid request line: `%s`" line);
raise (Bad_req (400, "Invalid request line")) raise (Bad_req (400, "Invalid request line"))
@ -507,8 +515,11 @@ module Request = struct
| Ok l -> l | Ok l -> l
| Error e -> bad_reqf 400 "invalid query: %s" e | Error e -> bad_reqf 400 "invalid query: %s" e
in in
Ok (Some {meth; query; host; path; path_components; let req = {
headers; body=()}) meth; query; host; path; path_components;
headers; http_version=(1, version); body=()
} in
Ok (Some req)
with with
| End_of_file | Sys_error _ -> Ok None | End_of_file | Sys_error _ -> Ok None
| Bad_req (c,s) -> Error (c,s) | Bad_req (c,s) -> Error (c,s)
@ -978,6 +989,8 @@ let handle_client_ (self:t) (client_sock:Unix.file_descr) : unit =
| Ok (Some req) -> | Ok (Some req) ->
_debug (fun k->k "req: %s" (Format.asprintf "@[%a@]" Request.pp_ req)); _debug (fun k->k "req: %s" (Format.asprintf "@[%a@]" Request.pp_ req));
if Request.close_after_req req then continue := false;
try try
(* is there a handler for this path? *) (* is there a handler for this path? *)
let handler = let handler =

View file

@ -219,10 +219,11 @@ end
Requests are sent by a client, e.g. a web browser or cURL. *) Requests are sent by a client, e.g. a web browser or cURL. *)
module Request : sig module Request : sig
type 'body t = { type 'body t = private {
meth: Meth.t; meth: Meth.t;
host: string; host: string;
headers: Headers.t; headers: Headers.t;
http_version: int*int;
path: string; path: string;
path_components: string list; path_components: string list;
query: (string*string) list; query: (string*string) list;