3 OCaml HTTP - do it yourself (fully OCaml) HTTP daemon
5 Copyright (C) <2002-2005> Stefano Zacchiroli <zack@cs.unibo.it>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Library General Public License as
9 published by the Free Software Foundation, version 2.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 (** raised when a client timeouts *)
28 (** if timeout is given (Some _) @return a new callback which establish
29 timeout_callback as callback for signal Sys.sigalrm and register an alarm
30 (expiring after timeout seconds) before invoking the real callback given. If
31 timeout is None, callback is returned unchanged. *)
32 let wrap_callback_w_timeout ~callback ~timeout ~timeout_callback =
35 | Some timeout -> (* wrap callback setting an handler for ALRM signal and an
36 alarm that ring after timeout seconds *)
37 (fun inchan outchan ->
38 ignore (Sys.signal Sys.sigalrm (Sys.Signal_handle timeout_callback));
39 ignore (Unix.alarm timeout);
40 callback inchan outchan)
42 (* try to close nicely a socket *)
43 let shutdown_socket suck =
45 Unix.shutdown suck Unix.SHUTDOWN_ALL
46 with Unix.Unix_error(_, "shutdown", "") -> ()
48 let nice_unix_accept suck =
51 with e -> (* clean up socket before exit *)
55 let init_socket sockaddr =
56 let suck = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
57 (* shutdown socket on SIGTERM *)
58 ignore (Sys.signal Sys.sigterm
60 (fun _ -> shutdown_socket suck; exit 17)));
61 Unix.setsockopt suck Unix.SO_REUSEADDR true;
62 Unix.bind suck sockaddr;
63 Unix.listen suck backlog;
66 let init_callback callback timeout =
67 let timeout_callback signo =
68 if signo = Sys.sigalrm then
71 wrap_callback_w_timeout ~callback ~timeout ~timeout_callback
73 (** try to close an outchannel connected to a socket, ignore Sys_error since
74 * this probably means that socket is already closed (e.g. on sigpipe) *)
75 let try_close_out ch = try close_out ch with Sys_error _ -> ()
77 (** like Unix.establish_server, but shutdown sockets when receiving SIGTERM
78 and before exiting for an uncaught exception *)
79 let my_establish_server server_fun sockaddr =
80 let suck = init_socket sockaddr in
82 let (s, caller) = nice_unix_accept suck in
83 (** "double fork" trick, see {!Unix.establish_server} implementation *)
84 match Unix.fork() with
87 if Unix.fork () <> 0 then
88 exit 0; (* The son exits, the grandson works *)
89 let inchan = Unix.in_channel_of_descr s in
90 let outchan = Unix.out_channel_of_descr s in
91 server_fun inchan outchan;
92 try_close_out outchan; (* closes also inchan: socket is the same *)
95 shutdown_socket suck; (* clean up socket before exit *)
97 | child when (child > 0) -> (* child *)
99 ignore (Unix.waitpid [] child) (* Reclaim the son *)
101 failwith "Can't fork"
104 (** tcp_server which forks a new process for each request *)
105 let fork ~sockaddr ~timeout callback =
106 let timeout_callback signo =
107 if signo = Sys.sigalrm then
111 (wrap_callback_w_timeout ~callback ~timeout ~timeout_callback)
114 (** tcp_server which doesn't fork, requests are server sequentially and in the
115 same address space of the calling process *)
116 let simple ~sockaddr ~timeout callback =
117 let suck = init_socket sockaddr in
118 let callback = init_callback callback timeout in
121 let (client, _) = Unix.accept suck in
122 (* client is now connected *)
123 let (inchan, outchan) =
124 (Unix.in_channel_of_descr client, Unix.out_channel_of_descr client)
127 callback inchan outchan;
128 ignore (Unix.alarm 0) (* reset alarm *)
130 try_close_out outchan (* this close also inchan: socket is the same *)
132 with e -> (* clean up socket before exit *)
133 shutdown_socket suck;
136 (** tcp_server which creates a new thread for each request to be served *)
137 let thread ~sockaddr ~timeout callback =
138 let suck = init_socket sockaddr in
139 let callback = init_callback callback timeout in
140 let callback (i, o) =
151 let (client, _) = nice_unix_accept suck in
152 (* client is now connected *)
153 let (inchan, outchan) =
154 (Unix.in_channel_of_descr client, Unix.out_channel_of_descr client)
156 Http_threaded_tcp_server.serve callback (inchan, outchan)
159 (** @param server an Http_types.tcp_server
160 * @return an Http_types.tcp_server which takes care of ignoring SIGPIPE during
161 * server execution and restoring previous handler when (if ever) the server
163 let handle_sigpipe server =
164 fun ~sockaddr ~timeout callback ->
165 let old_sigpipe_behavior = Sys.signal Sys.sigpipe Sys.Signal_ignore in
166 server ~sockaddr ~timeout callback;
167 ignore (Sys.signal Sys.sigpipe old_sigpipe_behavior)
169 let simple = handle_sigpipe simple
170 let thread = handle_sigpipe thread
171 let fork = handle_sigpipe fork