2
* http://www.ocsigen.org
5
* Vincent Balat, Denis Berthod, Nataliya Guts, J�r�me Vouillon
6
* Laboratoire PPS - CNRS Universit� Paris Diderot
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU Lesser General Public License as published by
10
* the Free Software Foundation, with linking exception;
11
* either version 2.1 of the License, or (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU Lesser General Public License for more details.
18
* You should have received a copy of the GNU Lesser General Public License
19
* along with this program; if not, write to the Free Software
20
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
open Ocsigen_extensions
27
open Ocsigen_http_frame
32
open Ocsigen_parseconfig
36
exception Ocsigen_unsupported_media
37
exception Ssl_Exception
38
exception Ocsigen_upload_forbidden
40
let () = Random.self_init ()
42
(* Without the following line, it stops with "Broken Pipe" without raising
44
let _ = Sys.set_signal Sys.sigpipe Sys.Signal_ignore
46
(* Initialize exception handler for Lwt timeouts: *)
48
Lwt_timeout.set_exn_handler
49
(fun e -> Ocsigen_messages.errlog ("Uncaught Exception after lwt timeout: "^
50
Ocsigen_lib.string_of_exn e))
52
external disable_nagle : Unix.file_descr -> unit = "disable_nagle"
54
let option_get_default x d = match x with Some x -> x | None -> d
55
let local_addr addr num =
56
Unix.ADDR_INET (option_get_default addr Unix.inet_addr_any, num)
57
let local_addr6 addr num =
58
Unix.ADDR_INET (option_get_default addr Unix.inet6_addr_any, num)
60
let sslctx = Ocsigen_http_client.sslcontext
63
let ip_of_sockaddr = function
64
| Unix.ADDR_INET (ip, port) -> ip
65
| _ -> raise (Ocsigen_Internal_Error "ip of unix socket")
67
let port_of_sockaddr = function
68
| Unix.ADDR_INET (ip, port) -> port
69
| _ -> raise (Ocsigen_Internal_Error "port of unix socket")
72
let get_boundary ctparams = List.assoc "boundary" ctparams
74
let (_, res) = Netstring_pcre.search_forward
75
(Netstring_pcre.regexp "boundary=([^;]*);?") cont_enc 0 in
76
Netstring_pcre.matched_group res 1 cont_enc
79
let find_field field content_disp =
80
let (_, res) = Netstring_pcre.search_forward
81
(Netstring_pcre.regexp (field^"=.([^\"]*).;?")) content_disp 0 in
82
Netstring_pcre.matched_group res 1 content_disp
85
No_File of string * Buffer.t
86
| A_File of (string * string * string * Unix.file_descr)
88
let counter = let c = ref (Random.int 1000000) in fun () -> c := !c + 1 ; !c
91
let ip = Unix.string_of_inet_addr (ip_of_sockaddr sockaddr) in
92
Ocsigen_messages.warning ("While talking to " ^ ip ^ ": " ^ s)
95
Ocsigen_messages.debug
97
let ip = Unix.string_of_inet_addr (ip_of_sockaddr sockaddr) in
98
"While talking to " ^ ip ^ ": " ^ s)
100
(* reading the request *)
101
let get_request_infos
102
meth clientproto url http_frame filenames sockaddr
108
let (headerhost, _, url, parsed_url, path, params, get_params) =
109
Ocsigen_lib.parse_url url
113
match headerhost with
114
| None -> get_host_from_host_header http_frame
119
1. If Request-URI is an absoluteURI, the host is part of the Request-URI. Any Host header field value in the request MUST be ignored.
120
2. If the Request-URI is not an absoluteURI, and the request includes a Host header field, the host is determined by the Host header field value.
121
3. If the host as determined by rule 1 or 2 is not a valid host on the server, the response MUST be a 400 (Bad Request) error message.
123
(* Here we don't trust the port information given by the request.
124
We use the port we are listening on. *)
125
Ocsigen_messages.debug
127
"- host="^(match headerhost with None -> "<none>" | Some h -> h));
129
(* Servers MUST report a 400 (Bad Request) error if an HTTP/1.1
130
request does not include a Host request-header. *)
132
if clientproto = Ocsigen_http_frame.Http_header.HTTP11 && headerhost = None
133
then raise Ocsigen_Bad_Request;
135
let useragent = get_user_agent http_frame in
137
let cookies_string = lazy (get_cookie_string http_frame) in
140
lazy (match (Lazy.force cookies_string) with
141
| None -> Ocsigen_http_frame.Cookievalues.empty
142
| Some s -> parse_cookies s)
145
let ifmodifiedsince = get_if_modified_since http_frame in
147
let ifunmodifiedsince = get_if_unmodified_since http_frame in
149
let ifnonematch = get_if_none_match http_frame in
151
let ifmatch = get_if_match http_frame in
153
let client_inet_addr = ip_of_sockaddr sockaddr in
155
let ct_string = get_content_type http_frame in
157
let ct = Ocsigen_headers.parse_content_type ct_string in
159
let cl = get_content_length http_frame in
161
let referer = lazy (get_referer http_frame) in
163
let accept = lazy (get_accept http_frame) in
165
let accept_charset = lazy (get_accept_charset http_frame) in
167
let accept_encoding = lazy (get_accept_encoding http_frame) in
169
let accept_language = lazy (get_accept_language http_frame) in
173
let find_post_params =
175
(if meth = Http_header.GET || meth = Http_header.HEAD then
178
match http_frame.Ocsigen_http_frame.content with
179
| None -> return ([], [])
182
let ((ct, cst), ctparams) = match ct with
183
| None -> (("application", "octet-stream"), [])
184
| Some (c, p) -> (c, p)
186
let body = Ocsigen_stream.get body_gen in
189
let ctlow = String.lowercase ct in
190
let cstlow = String.lowercase cst in
191
if ctlow = "application" &&
192
cstlow = "x-www-form-urlencoded"
196
Ocsigen_stream.string_of_stream body >>= fun r ->
198
((Netencoding.Url.dest_url_encoded_parameters r),
201
| Ocsigen_stream.String_too_large ->
202
fail Input_is_too_large
205
if not (ctlow = "multipart" && cstlow = "form-data")
206
then fail Ocsigen_unsupported_media
208
let bound = get_boundary ctparams in
209
let params = ref [] in
210
let files = ref [] in
212
let cd = List.assoc "content-disposition" hs in
214
Some (find_field "filename" cd)
215
with Not_found -> None in
216
let p_name = find_field "name" cd in
218
| None -> No_File (p_name, Buffer.create 1024)
223
(Unix.gettimeofday ()) (counter ())
225
match ((Ocsigen_config.get_uploaddir ())) with
227
let fname = dname^"/"^now in
228
let fd = Unix.openfile fname
232
Unix.O_NONBLOCK] 0o666 in
233
(* Ocsigen_messages.debug "file opened"; *)
234
filenames := fname::!filenames;
235
A_File (p_name, fname, store, fd)
236
| None -> raise Ocsigen_upload_forbidden
238
let rec add where s =
240
| No_File (p_name, to_buf) ->
241
Buffer.add_string to_buf s;
243
| A_File (_,_,_,wh) ->
244
let len = String.length s in
245
let r = Unix.write wh s 0 len in
247
(*XXXX Inefficient if s is long *)
248
add where (String.sub s r (len - r))
252
let stop size = function
253
| No_File (p_name, to_buf) ->
256
[(p_name, Buffer.contents to_buf)])
258
| A_File (p_name,fname,oname,wh) ->
259
(* Ocsigen_messages.debug "closing file"; *)
261
!files@[(p_name, {tmp_filename=fname;
263
raw_original_filename=oname;
264
original_basename=(Ocsigen_lib.basename oname)})];
268
Multipart.scan_multipart_body_from_stream
269
body bound create add stop >>= fun () ->
271
Does scan_multipart_body_from_stream read
272
until the end or only what it needs?
273
If we do not consume here,
274
the following request will be read only when
275
this one is finished ...
277
Ocsigen_stream.consume body_gen >>= fun () ->
278
Lwt.return (!params, !files))
279
(fun e -> (*XXX??? Ocsigen_stream.consume body >>= fun _ ->*) fail e)
282
(* AEFF *) (* IN-MEMORY STOCKAGE *)
283
(* let bdlist = Mimestring.scan_multipart_body_and_decode s 0
284
* (String.length s) bound in
285
* Ocsigen_messages.debug (fun () -> string_of_int (List.length bdlist));
286
* let simplify (hs,b) =
287
* ((find_field "name"
288
* (List.assoc "content-disposition" hs)),b) in
289
* List.iter (fun (hs,b) ->
290
* List.iter (fun (h,v) -> Ocsigen_messages.debug (fun () -> h^"=="^v)) hs) bdlist;
291
* List.map simplify bdlist *)
293
let ipstring = Unix.string_of_inet_addr client_inet_addr in
295
{ri_url_string = url;
298
ri_protocol = http_frame.Ocsigen_http_frame.header.Ocsigen_http_frame.Http_header.proto;
299
ri_ssl = Lwt_ssl.is_ssl (Ocsigen_http_com.connection_fd receiver);
300
ri_full_path_string = string_of_url_path path;
303
ri_sub_path_string = string_of_url_path path;
304
ri_get_params_string = params;
305
ri_host = headerhost;
306
ri_get_params = get_params;
307
ri_initial_get_params = get_params;
308
ri_post_params = lazy (force find_post_params >>= fun (a, b) ->
310
ri_files = lazy (force find_post_params >>= fun (a, b) ->
312
ri_remote_inet_addr = client_inet_addr;
313
ri_remote_ip = ipstring;
314
ri_remote_ip_parsed = lazy (fst (Ocsigen_lib.parse_ip ipstring));
315
ri_remote_port = port_of_sockaddr sockaddr;
316
ri_server_port = port;
317
ri_user_agent = useragent;
318
ri_cookies_string = cookies_string;
319
ri_cookies = cookies;
320
ri_ifmodifiedsince = ifmodifiedsince;
321
ri_ifunmodifiedsince = ifunmodifiedsince;
322
ri_ifnonematch = ifnonematch;
323
ri_ifmatch = ifmatch;
324
ri_content_type = ct;
325
ri_content_type_string = ct_string;
326
ri_content_length = cl;
327
ri_referer = referer;
329
ri_accept_charset = accept_charset;
330
ri_accept_encoding = accept_encoding;
331
ri_accept_language = accept_language;
332
ri_http_frame = http_frame;
333
ri_extension_info = [];
334
ri_client = Ocsigen_extensions.client_of_connection receiver;
338
Ocsigen_messages.debug (fun () -> "~~~ Exn during get_request_infos : "^
353
(* sender_slot is here for pipelining:
354
we must wait before sending the page,
355
because the previous one may not be sent *)
357
let head = meth = Http_header.HEAD in
358
let clientproto = Http_header.get_proto request.Ocsigen_http_frame.header in
360
let handle_service_errors e =
361
(* Exceptions during page generation *)
362
Ocsigen_messages.debug
363
(fun () -> "~~~ Exception during generation/sending: " ^ string_of_exn e);
365
(* EXCEPTIONS WHILE COMPUTING A PAGE *)
366
| Ocsigen_http_error (cookies_to_set, i) ->
367
Ocsigen_messages.debug
368
(fun () -> "-> Sending HTTP error "^(string_of_int i)^" "^
369
Ocsigen_http_frame.Http_error.expl_of_code i);
374
~cookies:cookies_to_set
377
~sender:Ocsigen_http_com.default_sender
379
| Ocsigen_stream.Interrupted Ocsigen_stream.Already_read ->
380
Ocsigen_messages.warning
381
"Cannot read the request twice. You probably have \
382
two incompatible options in <site> configuration, \
383
or the order of the options in the config file is wrong.";
384
send_error ~exn:e sender_slot ~clientproto ~head
385
~code:500 ~sender:Ocsigen_http_com.default_sender () (* Internal error *)
386
| Ocsigen_extensions.Ocsigen_malformed_url
387
| Unix.Unix_error (Unix.EACCES,_,_)
388
| Ocsigen_upload_forbidden ->
389
Ocsigen_messages.debug2 "-> Sending 403 Forbidden";
390
send_error ~exn:e sender_slot ~clientproto ~head
391
~code:403 ~sender:Ocsigen_http_com.default_sender ()
392
| Http_error.Http_exception (_,_,_) ->
393
send_error sender_slot ~clientproto ~head (* ~keep_alive:false *)
394
~exn:e ~sender:Ocsigen_http_com.default_sender ()
395
| Ocsigen_Bad_Request ->
396
Ocsigen_messages.debug2 "-> Sending 400";
397
send_error ~exn:e sender_slot ~clientproto ~head (* ~keep_alive:false *)
398
~code:400 ~sender:Ocsigen_http_com.default_sender ()
399
| Ocsigen_unsupported_media ->
400
Ocsigen_messages.debug2 "-> Sending 415";
401
send_error ~exn:e sender_slot ~clientproto ~head (* ~keep_alive:false *)
402
~code:415 ~sender:Ocsigen_http_com.default_sender ()
403
| Neturl.Malformed_URL ->
404
Ocsigen_messages.debug2 "-> Sending 400 (Malformed URL)";
405
send_error ~exn:e sender_slot ~clientproto ~head (* ~keep_alive:false *)
406
~code:400 ~sender:Ocsigen_http_com.default_sender () (* Malformed URL *)
408
Ocsigen_messages.warning
409
("Exn during page generation: " ^ string_of_exn e ^" (sending 500)");
410
Ocsigen_messages.debug2 "-> Sending 500";
411
send_error ~exn:e sender_slot ~clientproto ~head
412
~code:500 ~sender:Ocsigen_http_com.default_sender ()
414
let finish_request () =
415
(* We asynchronously finish to read the request contents if this
416
is not done yet so that:
417
- we can handle the next request
418
- there is no dead-lock with the client writing the request and
419
the server writing the response.
420
We need to do this once the request has been handled before sending
421
any reply to the client. *)
422
match request.Ocsigen_http_frame.content with
427
Ocsigen_stream.finalize f (* will consume the stream and
429
if not already done *)
435
Ocsigen_http_com.Lost_connection _ ->
436
warn sockaddr "connection abruptly closed by peer \
437
while reading contents"
438
| Ocsigen_http_com.Timeout ->
439
warn sockaddr "timeout while reading contents"
440
| Ocsigen_http_com.Aborted ->
441
warn sockaddr "reading thread aborted"
442
| Http_error.Http_exception (code, mesg, _) ->
443
warn sockaddr (Http_error.string_of_http_exception e)
445
Ocsigen_messages.unexpected_exception
446
e "Server.finish_request"
448
Ocsigen_http_com.abort receiver;
449
(* We unlock the receiver in order to resume the
450
reading loop. As the connection has been aborted,
451
the next read will fail and the connection will be
453
Ocsigen_http_com.unlock_receiver receiver;
459
(* body of service *)
460
if meth <> Http_header.GET &&
461
meth <> Http_header.POST &&
462
meth <> Http_header.HEAD
464
(* VVV Warning: This must be done once and only once.
465
Put this somewhere else to ensure that?
467
warn sockaddr ("Bad request: \""^url^"\"");
468
Ocsigen_http_com.wakeup_next_request receiver;
470
(* RFC 2616, sect 5.1.1 *)
472
sender_slot ~clientproto ~head ~code:501
473
~sender:Ocsigen_http_com.default_sender ()
475
let filenames = ref [] (* All the files sent by the request *) in
477
Lwt.finalize (fun () ->
478
(* *** First of all, we read the whole the request
479
(that will possibly create files) *)
483
meth clientproto url request filenames sockaddr
486
(* *** Now we generate the page and send it *)
490
"connection for %s from %s (%s): %s"
491
(match ri.ri_host with
492
| None -> "<host not specified in the request>"
498
(* Generation of pages is delegated to extensions: *)
501
Ocsigen_extensions.do_for_site_matching
502
ri.ri_host ri.ri_server_port ri)
506
An HTTP/1.1 origin server, upon receiving a conditional request
507
that includes both a Last-Modified date (e.g., in an
508
If-Modified-Since or If-Unmodified-Since header field) and one or
509
more entity tags (e.g., in an If-Match, If-None-Match, or If-Range
510
header field) as cache validators, MUST NOT return a response
511
status of 304 (Not Modified) unless doing so is consistent with all
512
of the conditional header fields in the request.
514
The result of a request having both an If-Unmodified-Since header
515
field and either an If-None-Match or an If-Modified-Since header
516
fields is undefined by this specification.
519
let etagalreadyknown =
520
match res.res_etag with
522
| Some e -> List.mem e ri.ri_ifnonematch
524
match res.res_lastmodified, ri.ri_ifmodifiedsince with
525
| Some l, Some i when l <= i ->
526
ri.ri_ifnonematch = [] || etagalreadyknown
534
res.res_lastmodified, ri.ri_ifunmodifiedsince
536
| Some l, Some i -> i < l
540
begin match ri.ri_ifmatch, res.res_etag with
542
| Some _, None -> true
543
| Some l, Some e -> not (List.mem e l)
546
if not_modified then begin
547
Ocsigen_messages.debug2 "-> Sending 304 Not modified ";
548
Ocsigen_stream.finalize res.res_stream >>= fun () ->
549
let empty_result = Ocsigen_http_frame.empty_result () in
554
~sender:Ocsigen_http_com.default_sender
555
{empty_result with res_code = 304 (* Not modified *)}
556
end else if precond_failed then begin
557
Ocsigen_messages.debug2
558
"-> Sending 412 Precondition Failed \
559
(if-unmodified-since header)";
560
Ocsigen_stream.finalize res.res_stream >>= fun () ->
561
let empty_result = Ocsigen_http_frame.empty_result () in
566
~sender:Ocsigen_http_com.default_sender
568
with res_code = 412 (* Precondition failed *)}
574
~sender:Ocsigen_http_com.default_sender
579
| Ocsigen_Is_a_directory ->
580
Ocsigen_messages.debug2 "-> Sending 301 Moved permanently";
581
let empty_result = Ocsigen_http_frame.empty_result () in
586
~sender:Ocsigen_http_com.default_sender
588
res_code = 301 (* Moved permanently *);
589
res_location = Some ((Neturl.string_of_url
590
(Neturl.undefault_url
591
~path:("/"::(ri.ri_full_path))
595
handle_service_errors e))
597
warn sockaddr ("Bad request: \""^url^"\"");
598
Ocsigen_http_com.wakeup_next_request receiver;
600
handle_service_errors e))
602
(* We remove all the files created by the request
603
(files sent by the client) *)
604
if !filenames <> [] then
605
Ocsigen_messages.debug2 "** Removing files";
610
with Unix.Unix_error _ as e ->
611
Ocsigen_messages.warning
612
(Format.sprintf "Error while removing file %s: %s"
613
a (string_of_exn e)))
618
let linger in_ch receiver =
621
(* We wait for 30 seconds at most and close the connection
622
after 2 seconds without receiving data from the client *)
623
let abort_fun () = Lwt_ssl.abort in_ch Exit in
624
let long_timeout = Lwt_timeout.create 30 abort_fun in
625
let short_timeout = Lwt_timeout.create 2 abort_fun in
626
Lwt_timeout.start long_timeout;
627
let s = String.create 1024 in
629
let rec linger_aux () =
630
Lwt_ssl.wait_read in_ch >>= fun () ->
633
Lwt_timeout.start short_timeout;
634
Lwt_ssl.read in_ch s 0 1024)
636
if len > 0 then linger_aux () else Lwt.return ())
639
Unix.Unix_error(Unix.ECONNRESET,_,_)
640
| Ssl.Read_error (Ssl.Error_syscall | Ssl.Error_ssl)
647
(* We start the lingering reads before waiting for the
648
senders to terminate in order to avoid a deadlock *)
649
let linger_thread = linger_aux () in
650
Ocsigen_http_com.wait_all_senders receiver >>= fun () ->
651
Ocsigen_messages.debug2 "** SHUTDOWN";
652
Lwt_ssl.ssl_shutdown in_ch >>= fun () ->
653
Lwt_ssl.shutdown in_ch Unix.SHUTDOWN_SEND;
654
linger_thread >>= fun () ->
655
Lwt_timeout.stop long_timeout;
656
Lwt_timeout.stop short_timeout;
659
Ocsigen_messages.unexpected_exception e "Server.linger"; Lwt.return ())
661
let try_bind' f g h = Lwt.try_bind f h g
663
let handle_connection port in_ch sockaddr =
665
Ocsigen_http_com.create_receiver (Ocsigen_config.get_client_timeout ()) Query in_ch
668
let handle_write_errors e =
670
Lost_connection e' ->
671
warn sockaddr ("connection abruptly closed by peer ("
672
^ string_of_exn e' ^ ")")
673
| Ocsigen_http_com.Timeout ->
674
warn sockaddr "timeout"
675
| Ocsigen_http_com.Aborted ->
676
warn sockaddr "writing thread aborted"
677
| Ocsigen_stream.Interrupted e' ->
678
warn sockaddr ("interrupted content stream (" ^ string_of_exn e' ^ ")")
680
Ocsigen_messages.unexpected_exception e "Server.handle_write_errors"
682
Ocsigen_http_com.abort receiver;
683
Lwt.fail Ocsigen_http_com.Aborted
686
let handle_read_errors e =
688
| Ocsigen_http_com.Connection_closed ->
689
(* This is the clean way to terminate the connection *)
690
dbg sockaddr "connection closed by peer";
691
Ocsigen_http_com.abort receiver;
692
Ocsigen_http_com.wait_all_senders receiver
693
| Ocsigen_http_com.Keepalive_timeout ->
694
dbg sockaddr "keepalive timeout";
695
Ocsigen_http_com.abort receiver;
696
Ocsigen_http_com.wait_all_senders receiver
697
| Ocsigen_http_com.Lost_connection _ ->
698
warn sockaddr "connection abruptly closed by peer";
699
Ocsigen_http_com.abort receiver;
700
Ocsigen_http_com.wait_all_senders receiver
701
| Ocsigen_http_com.Timeout ->
702
warn sockaddr "timeout";
703
Ocsigen_http_com.abort receiver;
704
Ocsigen_http_com.wait_all_senders receiver
705
| Ocsigen_http_com.Aborted ->
706
warn sockaddr "reading thread aborted";
707
Ocsigen_http_com.wait_all_senders receiver
708
| Http_error.Http_exception (code, mes, _) ->
709
warn sockaddr (Http_error.string_of_http_exception e);
710
Ocsigen_http_com.start_processing receiver (fun slot ->
711
(*XXX We should use the right information for clientproto
714
~clientproto:Ocsigen_http_frame.Http_header.HTTP10
716
(* ~keep_alive:false *)
718
~sender:Ocsigen_http_com.default_sender ());
719
linger in_ch receiver
721
Ocsigen_messages.unexpected_exception e "Server.handle_read_errors";
722
Ocsigen_http_com.abort receiver;
723
Ocsigen_http_com.wait_all_senders receiver
727
let rec handle_request () =
730
Ocsigen_messages.debug2 "** Receiving HTTP message";
731
(if Ocsigen_config.get_respect_pipeline () then
732
(* if we lock this mutex, requests from a same connection will be sent
733
to extensions in the same order they are received on pipeline.
734
It is locked only in server. Ocsigen_http_client has its own mutex.
735
(*VVV use the same? *)
737
Ocsigen_http_com.block_next_request receiver
741
Ocsigen_http_com.get_http_frame receiver)
745
match Http_header.get_firstline request.Ocsigen_http_frame.header with
746
| Http_header.Query a -> a
748
(*XXX Should be checked in [get_http_frame] *)
750
Ocsigen_http_com.start_processing receiver (fun slot ->
753
(*XXX Why do we need the port but not the host name? *)
754
service receiver slot request meth url port sockaddr in_ch)
755
handle_write_errors);
756
if get_keepalive request.Ocsigen_http_frame.header then
758
else (* No keep-alive => no pipeline *)
759
(* We wait for the query to be entirely read and for
760
the reply to be sent *)
761
Ocsigen_http_com.lock_receiver receiver >>= fun () ->
762
Ocsigen_http_com.wait_all_senders receiver)
764
in (* body of handle_connection *)
767
let rec wait_connection use_ssl port socket =
769
(fun () -> Lwt_unix.accept socket)
771
Ocsigen_messages.debug
772
(fun () -> Format.sprintf "Accept failed: %s" (string_of_exn e));
773
wait_connection use_ssl port socket)
774
(fun (s, sockaddr) ->
775
Ocsigen_messages.debug2
776
"\n__________________NEW CONNECTION__________________________";
778
let relaunch_at_once =
779
get_number_of_connected () < get_max_number_of_connections () in
780
if relaunch_at_once then
781
ignore (wait_connection use_ssl port socket)
784
(Ocsigen_messages.warning
785
(Format.sprintf "Max simultaneous connections (%d) reached."
786
(get_max_number_of_connections ())));
789
Lwt_unix.set_close_on_exec s;
790
disable_nagle (Lwt_unix.unix_file_descr s);
791
begin if use_ssl then
792
Lwt_ssl.ssl_accept s !sslctx
794
Lwt.return (Lwt_ssl.plain s)
796
handle_connection port in_ch sockaddr)
798
Ocsigen_messages.unexpected_exception e
799
"Server.wait_connection (handle connection)";
800
return ()) >>= fun () ->
801
Ocsigen_messages.debug2 "** CLOSE";
804
with Unix.Unix_error _ as e ->
805
Ocsigen_messages.unexpected_exception e "Server.wait_connection (close)"
808
if not relaunch_at_once then
810
debug2 "Ok releasing one connection";
811
ignore (wait_connection use_ssl port socket)
820
(** Thread waiting for events on a the listening port *)
821
let listen use_ssl (addr, port) wait_end_init =
822
let listening_socket =
826
let socket = Lwt_unix.socket Unix.PF_INET6 Unix.SOCK_STREAM 0 in
827
Lwt_unix.set_close_on_exec socket;
828
Lwt_unix.setsockopt socket Unix.SO_REUSEADDR true;
829
Lwt_unix.bind socket (local_addr6 addr port);
832
(*VVV CATCH only the IPv6 exception.
834
| ENOPROTOOPT (* Protocol not available *) ?
835
| EPROTONOSUPPORT (* Protocol not supported *)???
838
Ocsigen_messages.warning
839
("Exception while creating IPv6 socket: "^Ocsigen_lib.string_of_exn e);
840
let socket = Lwt_unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
841
Lwt_unix.set_close_on_exec socket;
842
Lwt_unix.setsockopt socket Unix.SO_REUSEADDR true;
843
Lwt_unix.bind socket (local_addr addr port);
846
Lwt_unix.listen socket 1024;
849
| Unix.Unix_error (Unix.EACCES, _, _) ->
851
(Format.sprintf "Fatal - You are not allowed to use port %d." port)
853
| Unix.Unix_error (Unix.EADDRINUSE, _, _) ->
854
stop (Format.sprintf "Fatal - The port %d is already in use." port) 8
856
stop ("Fatal - Uncaught exception: " ^ string_of_exn exn) 100
858
wait_end_init >>= fun () ->
859
wait_connection use_ssl port listening_socket
861
(* fatal errors messages *)
862
let errmsg = function
864
(("Fatal - Dynamic linking error: "^(Dynlink.error_message e)),
866
| (Unix.Unix_error _) as e ->
867
(("Fatal - "^(string_of_exn e)),
869
| Ssl.Private_key_error ->
870
(("Fatal - bad password"),
872
| Ocsigen_config.Config_file_error msg ->
873
(("Fatal - Error in configuration file: "^msg),
875
| Simplexmlparser.Xml_parser_error s ->
876
(("Fatal - Error in configuration file: "^s),
878
| Ocsigen_loader.Dynlink_error (s, exn) ->
879
(("Fatal - While loading "^s^": "^(string_of_exn exn)),
883
((Ocsigen_extensions.get_init_exn_handler () exn),
887
(("Fatal - Uncaught exception: "^string_of_exn exn),
893
(* reloading the cmo *)
896
(* That function cannot be interrupted??? *)
897
Ocsigen_messages.warning "Reloading config file" ;
900
match parse_config () with
904
Ocsigen_extensions.start_initialisation ();
908
Ocsigen_extensions.end_initialisation ();
912
Ocsigen_extensions.end_initialisation ();
913
errlog (fst (errmsg e)));
915
Ocsigen_messages.warning "Config file reloaded"
927
let number_of_servers = List.length config_servers in
929
if number_of_servers > 1
930
then ignore (Ocsigen_messages.warning "Multiple servers not supported anymore");
932
let ask_for_passwd sslports _ =
933
print_string "Please enter the password for the HTTPS server listening \
935
print_string (String.concat ", " (List.map (fun (_,p) -> string_of_int p) sslports));
937
let old_term= Unix.tcgetattr Unix.stdin in
938
let old_echo = old_term.Unix.c_echo in
939
old_term.Unix.c_echo <- false;
940
Unix.tcsetattr Unix.stdin Unix.TCSAFLUSH old_term;
942
let r = read_line () in
944
old_term.Unix.c_echo <- old_echo;
945
Unix.tcsetattr Unix.stdin Unix.TCSAFLUSH old_term;
948
old_term.Unix.c_echo <- old_echo;
949
Unix.tcsetattr Unix.stdin Unix.TCSAFLUSH old_term;
953
let run (user, group) (_, ports, sslports) (minthreads, maxthreads) s =
955
Ocsigen_messages.open_files ();
958
(let wait_end_init = wait () in
959
(* Listening on all ports: *)
962
ignore (listen false i wait_end_init)) ports;
965
ignore (listen true i wait_end_init)) sslports;
967
let gid = match group with
968
| None -> Unix.getgid ()
970
(Unix.getgrnam group).Unix.gr_gid
971
with e -> errlog ("Error: Wrong group"); raise e)
974
let uid = match user with
975
| None -> Unix.getuid ()
977
(Unix.getpwnam user).Unix.pw_uid
978
with e -> (errlog ("Error: Wrong user"); raise e))
981
(* A pipe to communicate with the server *)
982
let commandpipe = get_command_pipe () in
984
ignore (Unix.stat commandpipe);
985
with Unix.Unix_error _ ->
987
let umask = Unix.umask 0 in
988
Unix.mkfifo commandpipe 0o660;
989
Unix.chown commandpipe uid gid;
990
ignore (Unix.umask umask);
992
Ocsigen_messages.errlog
993
("Cannot create the command pipe: "^(string_of_exn e))));
995
(* I change the user for the process *)
1000
Ocsigen_messages.errlog ("Error: Wrong user or group"); raise e);
1002
Ocsigen_config.set_user user;
1003
Ocsigen_config.set_group group;
1008
Lwt_unix.yield () >>= f
1011
if maxthreads < minthreads
1014
(Config_file_error "maxthreads should be greater than minthreads");
1016
ignore (Lwt_preemptive.init minthreads maxthreads Ocsigen_messages.errlog);
1018
(* Now I can load the modules *)
1020
Dynlink.allow_unsafe_modules true;
1022
Ocsigen_extensions.start_initialisation ();
1024
parse_server false s;
1026
Dynlink.prohibit ["Ocsigen_extensions.R"];
1027
(* As libraries are reloaded each time the config file is read,
1028
we do not allow to register extensions in libraries *)
1029
(* seems it does not work :-( *)
1032
(* Closing stderr, stdout stdin if silent *)
1033
if (Ocsigen_config.get_silent ())
1035
(* redirect stdout and stderr to /dev/null *)
1036
let devnull = Unix.openfile "/dev/null" [Unix.O_WRONLY] 0 in
1037
Unix.dup2 devnull Unix.stdout;
1038
Unix.dup2 devnull Unix.stderr;
1040
Unix.close Unix.stdin;
1043
(* detach from the terminal *)
1044
if (Ocsigen_config.get_daemon ())
1045
then ignore (Unix.setsid ());
1047
Ocsigen_extensions.end_initialisation ();
1049
(* Communication with the server through the pipe *)
1051
ignore (Unix.stat commandpipe)
1052
with Unix.Unix_error _ ->
1053
let umask = Unix.umask 0 in
1054
Unix.mkfifo commandpipe 0o660;
1055
ignore (Unix.umask umask);
1056
ignore (Ocsigen_messages.warning "Command pipe created"));
1058
let pipe = Lwt_chan.in_channel_of_descr
1059
(Lwt_unix.of_unix_file_descr
1060
(Unix.openfile commandpipe
1061
[Unix.O_RDWR; Unix.O_NONBLOCK; Unix.O_APPEND] 0o660)) in
1064
Lwt_chan.input_line pipe >>=
1068
Ocsigen_messages.open_files ();
1069
Ocsigen_messages.warning "Log files reopened"
1070
| "reload" -> reload ()
1073
Ocsigen_messages.warning "Heap compaction requested by user"
1074
| _ -> Ocsigen_messages.warning ("Unknown command: " ^ s)
1078
wakeup wait_end_init ();
1080
warning "Ocsigen has been launched (initialisations ok)";
1086
let set_passwd_if_needed (ssl, ports, sslports) =
1091
| Some (None, None) -> ()
1092
| Some (None, _) -> raise (Ocsigen_config.Config_file_error
1093
"SSL certificate is missing")
1094
| Some (_, None) -> raise (Ocsigen_config.Config_file_error
1095
"SSL key is missing")
1096
| Some ((Some c), (Some k)) ->
1097
Ssl.set_password_callback !sslctx (ask_for_passwd sslports);
1098
Ssl.use_certificate !sslctx c k
1102
match Ocsigen_config.get_pidfile () with
1105
let spid = (string_of_int pid)^"\n" in
1106
let len = String.length spid in
1110
[Unix.O_WRONLY; Unix.O_CREAT; Unix.O_APPEND] 0o640 in
1111
ignore (Unix.write f spid 0 len);
1115
let rec launch = function
1118
let user_info, sslinfo, threadinfo = extract_info h in
1119
set_passwd_if_needed sslinfo;
1120
let pid = Unix.fork () in
1122
then run user_info sslinfo threadinfo h
1125
(Ocsigen_messages.console
1126
(fun () -> "Process "^(string_of_int pid)^" detached"));
1129
| _ -> () (* Multiple servers not supported any more *)
1133
if (not (get_daemon ())) &&
1134
number_of_servers = 1
1136
let cf = List.hd config_servers in
1138
((ssl, ports, sslports) as sslinfo),
1142
(set_passwd_if_needed sslinfo;
1143
write_pid (Unix.getpid ());
1144
Ocsigen_config.set_ports ports;
1145
Ocsigen_config.set_sslports sslports;
1146
run user_info sslinfo threadinfo cf)
1147
else launch config_servers
1150
let msg, errno = errmsg e in