From ce61ac2afde4865a3718ada37d88031dd2f10d01 Mon Sep 17 00:00:00 2001 From: Yawar Amin Date: Sat, 25 Sep 2021 01:41:32 -0400 Subject: [PATCH 1/3] feat: allow socket activation This enables passing in an existing socket e.g. from systemd,[1] instead of creating a new socket when running the server. [1] https://github.com/juergenhoetzel/ocaml-systemd --- src/Tiny_httpd.ml | 24 +++++++++++++++++------- src/Tiny_httpd.mli | 6 +++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Tiny_httpd.ml b/src/Tiny_httpd.ml index eec0ccda..408f4c8c 100644 --- a/src/Tiny_httpd.ml +++ b/src/Tiny_httpd.ml @@ -809,6 +809,8 @@ type t = { port: int; + sock: Unix.file_descr option; + sem_max_connections: Sem_.t; (* semaphore to restrict the number of active concurrent connections *) @@ -947,10 +949,10 @@ let create ?(masksigpipe=true) ?(max_connections=32) ?(new_thread=(fun f -> ignore (Thread.create f () : Thread.t))) - ?(addr="127.0.0.1") ?(port=8080) () : t = + ?(addr="127.0.0.1") ?(port=8080) ?sock () : t = let handler _req = Response.fail ~code:404 "no top handler" in let max_connections = max 4 max_connections in - { new_thread; addr; port; masksigpipe; handler; + { new_thread; addr; port; sock; masksigpipe; handler; running= true; sem_max_connections=Sem_.create max_connections; path_handlers=[]; cb_encode_resp=[]; cb_decode_req=[]; @@ -1065,16 +1067,24 @@ let run (self:t) : (unit,_) result = if self.masksigpipe then ( ignore (Unix.sigprocmask Unix.SIG_BLOCK [Sys.sigpipe] : _ list); ); - let sock = - Unix.socket (if is_ipv6 self then Unix.PF_INET6 else Unix.PF_INET) - Unix.SOCK_STREAM 0 + let sock, should_bind = match self.sock with + | Some s -> + s, false (* Because we're getting a socket from systemd *) + | None -> + Unix.socket + (if is_ipv6 self then Unix.PF_INET6 else Unix.PF_INET) + Unix.SOCK_STREAM + 0, + true (* Because we're creating the socket ourselves *) in Unix.clear_nonblock sock; Unix.setsockopt sock Unix.SO_REUSEADDR true; Unix.setsockopt_optint sock Unix.SO_LINGER None; let inet_addr = Unix.inet_addr_of_string self.addr in - Unix.bind sock (Unix.ADDR_INET (inet_addr, self.port)); - Unix.listen sock (2 * self.sem_max_connections.Sem_.n); + begin if should_bind then + Unix.bind sock (Unix.ADDR_INET (inet_addr, self.port)); + Unix.listen sock (2 * self.sem_max_connections.Sem_.n) + end; while self.running do (* limit concurrency *) Sem_.acquire 1 self.sem_max_connections; diff --git a/src/Tiny_httpd.mli b/src/Tiny_httpd.mli index 462b1121..e193e144 100644 --- a/src/Tiny_httpd.mli +++ b/src/Tiny_httpd.mli @@ -437,6 +437,7 @@ val create : ?new_thread:((unit -> unit) -> unit) -> ?addr:string -> ?port:int -> + ?sock:Unix.file_descr -> unit -> t (** Create a new webserver. @@ -455,7 +456,10 @@ val create : @param max_connections maximum number of simultaneous connections. @param addr address (IPv4 or IPv6) to listen on. Default ["127.0.0.1"]. @param port to listen on. Default [8080]. - *) + @param sock an existing socket given to the server to listen on, e.g. by + systemd on Linux (or launchd on macOS). If passed in, this socket will be + used instead of the [addr] and [port]. If not passed in, those will be + used. *) val addr : t -> string (** Address on which the server listens. *) From 8414efc3e034b840c9ff9feef8848a9f22a333ab Mon Sep 17 00:00:00 2001 From: Yawar Amin Date: Sat, 25 Sep 2021 02:03:28 -0400 Subject: [PATCH 2/3] Adjust implementation Only do things that are relevant for binding a socket if we actually should. --- src/Tiny_httpd.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tiny_httpd.ml b/src/Tiny_httpd.ml index 408f4c8c..a7aebbf0 100644 --- a/src/Tiny_httpd.ml +++ b/src/Tiny_httpd.ml @@ -1078,10 +1078,10 @@ let run (self:t) : (unit,_) result = true (* Because we're creating the socket ourselves *) in Unix.clear_nonblock sock; - Unix.setsockopt sock Unix.SO_REUSEADDR true; Unix.setsockopt_optint sock Unix.SO_LINGER None; - let inet_addr = Unix.inet_addr_of_string self.addr in begin if should_bind then + let inet_addr = Unix.inet_addr_of_string self.addr in + Unix.setsockopt sock Unix.SO_REUSEADDR true; Unix.bind sock (Unix.ADDR_INET (inet_addr, self.port)); Unix.listen sock (2 * self.sem_max_connections.Sem_.n) end; From 0e05b8cac60060e0141d06842fd42b78f5854a01 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 25 Sep 2021 14:41:42 -0400 Subject: [PATCH 3/3] Update src/Tiny_httpd.ml --- src/Tiny_httpd.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tiny_httpd.ml b/src/Tiny_httpd.ml index a7aebbf0..38f11ccc 100644 --- a/src/Tiny_httpd.ml +++ b/src/Tiny_httpd.ml @@ -1069,7 +1069,7 @@ let run (self:t) : (unit,_) result = ); let sock, should_bind = match self.sock with | Some s -> - s, false (* Because we're getting a socket from systemd *) + s, false (* Because we're getting a socket from the caller (e.g. systemd) *) | None -> Unix.socket (if is_ipv6 self then Unix.PF_INET6 else Unix.PF_INET)