~ubuntu-branches/ubuntu/trusty/ocamlnet/trusty

« back to all changes in this revision

Viewing changes to src/nethttpd-for-netcgi1/nethttpd_reactor.ml

  • Committer: Bazaar Package Importer
  • Author(s): Stéphane Glondu
  • Date: 2011-09-02 14:12:33 UTC
  • mfrom: (18.2.3 sid)
  • Revision ID: james.westby@ubuntu.com-20110902141233-zbj0ygxb92u6gy4z
Tags: 3.4-1
* New upstream release
  - add a new NetcgiRequire directive to ease dependency management
    (Closes: #637147)
  - remove patches that were applied upstream:
    + Added-missing-shebang-lines-in-example-shell-scripts
    + Try-also-ocamlc-for-POSIX-threads

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
(* $Id: nethttpd_reactor.ml 1101 2007-04-09 11:02:10Z gerd $
2
 
 *
3
 
 *)
4
 
 
5
 
(*
6
 
 * Copyright 2005 Baretta s.r.l. and Gerd Stolpmann
7
 
 *
8
 
 * This file is part of Nethttpd.
9
 
 *
10
 
 * Nethttpd is free software; you can redistribute it and/or modify
11
 
 * it under the terms of the GNU General Public License as published by
12
 
 * the Free Software Foundation; either version 2 of the License, or
13
 
 * (at your option) any later version.
14
 
 *
15
 
 * Nethttpd is distributed in the hope that it will be useful,
16
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 
 * GNU General Public License for more details.
19
 
 *
20
 
 * You should have received a copy of the GNU General Public License
21
 
 * along with WDialog; if not, write to the Free Software
22
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
 
 *)
24
 
 
25
 
open Nethttp
26
 
open Nethttp.Header
27
 
open Nethttpd_types
28
 
open Nethttpd_kernel
29
 
open Netchannels
30
 
 
31
 
class type http_processor_config =
32
 
object
33
 
  inherit Nethttpd_kernel.http_protocol_config
34
 
  method config_timeout_next_request : float
35
 
  method config_timeout : float
36
 
  method config_cgi : Netcgi1_compat.Netcgi_env.cgi_config
37
 
  method config_error_response : int -> string
38
 
  method config_log_error : 
39
 
    Unix.sockaddr option -> Unix.sockaddr option -> http_method option -> http_header option -> string -> unit
40
 
end
41
 
 
42
 
class type http_reactor_config =
43
 
object
44
 
  inherit http_processor_config
45
 
  method config_reactor_synch : [ `Connection | `Close | `Flush | `Write ]
46
 
end
47
 
 
48
 
 
49
 
class type internal_environment =
50
 
object
51
 
  inherit extended_environment
52
 
 
53
 
  method unlock : unit -> unit
54
 
  method req_method : http_method
55
 
  method response : http_response
56
 
end
57
 
 
58
 
 
59
 
class type http_reactive_request =
60
 
object
61
 
  method environment : extended_environment
62
 
  method accept_body : unit -> unit
63
 
  method reject_body : unit -> unit
64
 
  method finish : unit -> unit
65
 
  method finish_request : unit -> unit
66
 
end
67
 
 
68
 
 
69
 
let get_this_host addr =
70
 
  match addr with
71
 
    | Unix.ADDR_UNIX path ->
72
 
        ("", None)   (* questionable *)
73
 
    | Unix.ADDR_INET(ia,port) ->
74
 
        (Unix.string_of_inet_addr ia, Some port)
75
 
 
76
 
 
77
 
 
78
 
class http_environment (proc_config : #http_processor_config)
79
 
                       req_meth req_uri req_version req_hdr 
80
 
                       fd_addr peer_addr
81
 
                       in_ch out_ch resp close_after_send_file
82
 
                      : internal_environment =
83
 
 
84
 
  (* Decode important input header fields: *)
85
 
  let (in_host, in_port_opt) =
86
 
    (* Host and port of the [Host] header *)
87
 
    try get_host req_hdr
88
 
    with 
89
 
      | Not_found -> 
90
 
          (* For HTTP/1.1 and later this is not allowed. For earlier protocols, we
91
 
           * just fill in the IP address that accepted the request. 
92
 
           *)
93
 
          ( match req_version with
94
 
              | `Http((1,n),_) when n>= 1 ->
95
 
                  raise(Standard_response(`Bad_request, 
96
 
                                        None,
97
 
                                        Some "Nethttpd: Bad request: [Host] header is missing"))
98
 
              | _ ->
99
 
                  get_this_host fd_addr
100
 
          )
101
 
      | Bad_header_field _ ->
102
 
          raise(Standard_response(`Bad_request,
103
 
                                   None,
104
 
                                Some "Nethttpd: Bad request: Cannot decode [Host] header")) in
105
 
 
106
 
  let (script_name, query_string) = decode_query req_uri in
107
 
 
108
 
(*
109
 
  let full_uri =
110
 
    "http://" ^ in_host ^ 
111
 
    (match in_port with Some n -> ":" ^ string_of_int n | None -> "") ^ 
112
 
    req_uri
113
 
 *)
114
 
 
115
 
object(self)
116
 
  inherit empty_environment
117
 
 
118
 
  val mutable locked = true
119
 
 
120
 
  initializer (
121
 
    config <- proc_config # config_cgi;
122
 
    in_state <- `Received_header;
123
 
    out_state <- `Start;
124
 
    in_header <- req_hdr;
125
 
    in_channel <- in_ch;
126
 
    out_channel <- out_ch;
127
 
    protocol <- req_version;
128
 
    properties <- [ "GATEWAY_INTERFACE", "Nethttpd/0.0";
129
 
                  "SERVER_SOFTWARE",   "Nethttpd/0.0";
130
 
                  "SERVER_NAME",       in_host;
131
 
                  "SERVER_PROTOCOL",   string_of_protocol req_version;
132
 
                  "REQUEST_METHOD",    req_meth;
133
 
                  "SCRIPT_NAME",       script_name;
134
 
                  (* "PATH_INFO",         ""; *)
135
 
                  (* "PATH_TRANSLATED",   ""; *)
136
 
                  "QUERY_STRING",      query_string;
137
 
                  (* "REMOTE_HOST",       ""; *)
138
 
                  "REMOTE_ADDR",       fst(get_this_host peer_addr);
139
 
                  (* "AUTH_TYPE",         ""; *)
140
 
                  (* "REMOTE_USER",       ""; *)
141
 
                  (* "REMOTE_IDENT",      ""; *)
142
 
                  "HTTPS",             "off";
143
 
                  "REQUEST_URI",       req_uri;
144
 
                  ] @
145
 
                  ( match in_port_opt with
146
 
                      | Some p -> [ "SERVER_PORT", string_of_int p ]
147
 
                      | None   -> [] )
148
 
  )
149
 
 
150
 
  method unlock() =
151
 
    locked <- false
152
 
 
153
 
  method server_socket_addr = fd_addr
154
 
  method remote_socket_addr = peer_addr
155
 
 
156
 
  method response = resp
157
 
  method req_method = (req_meth, req_uri)
158
 
 
159
 
  method send_output_header() =
160
 
    if locked then failwith "Nethttpd_reactor: channel is locked";
161
 
    if out_state <> `Start then
162
 
      failwith "send_output_header";
163
 
    (* The response status is encoded in the [Status] pseudo header *)
164
 
    let (code, phrase) = status_of_cgi_header out_header in
165
 
    resp # send (`Resp_status_line(code, phrase));
166
 
    (* Create a copy of the header without [Status]: *)
167
 
    let h = new Netmime.basic_mime_header out_header#fields in
168
 
    h # delete_field "Status";
169
 
    resp # send (`Resp_header h);
170
 
    out_state <- `Sent_header
171
 
 
172
 
  method send_file fd length =
173
 
    if locked then failwith "Nethttpd_reactor: channel is locked";
174
 
    if out_state <> `Start then
175
 
      failwith "send_file";
176
 
    (* The response status is encoded in the [Status] pseudo header *)
177
 
    let (code, phrase) = status_of_cgi_header out_header in
178
 
    let status = http_status_of_int code in
179
 
     (* Create a copy of the header without [Status]: *)
180
 
    let h = new Netmime.basic_mime_header out_header#fields in
181
 
    h # delete_field "Status";
182
 
    send_file_response resp status (Some h) fd length;
183
 
    out_state <- `Sending_body;  (* best approximation *)
184
 
    close_after_send_file()
185
 
 
186
 
  method log_error s =
187
 
    proc_config # config_log_error 
188
 
      (Some fd_addr) (Some peer_addr) (Some(req_meth,req_uri)) (Some req_hdr) s
189
 
 
190
 
end
191
 
 
192
 
 
193
 
class http_reactor_input next_token =   (* an extension of rec_in_channel *)
194
 
object(self)
195
 
  val mutable current_chunk = None
196
 
  val mutable eof = false
197
 
  val mutable closed = false
198
 
  val mutable locked = true
199
 
 
200
 
  method private refill() =
201
 
    match next_token() with
202
 
      | `Req_body(s,pos,len) ->
203
 
          assert(len > 0);
204
 
          current_chunk <- Some(s,pos,len)
205
 
      | `Req_trailer _ ->
206
 
          self # refill ()   (* ignore *)
207
 
      | `Req_end ->
208
 
          current_chunk <- None;
209
 
          eof <- true;
210
 
          raise End_of_file;
211
 
      | _ ->
212
 
          (* Something else... Handle this as `Req_end! *)
213
 
          current_chunk <- None;
214
 
          eof <- true;
215
 
          raise End_of_file;
216
 
          
217
 
 
218
 
  method input s spos slen =
219
 
    if closed then raise Closed_channel;
220
 
    if locked then failwith "Nethttpd_reactor: channel is locked";
221
 
    if eof then raise End_of_file;
222
 
    if current_chunk = None then self#refill();
223
 
    match current_chunk with
224
 
      | Some(u,upos,ulen) ->
225
 
          (* We have [ulen] data, copy that to [s] *)
226
 
          let len = min slen ulen in
227
 
          String.blit u upos s spos len;
228
 
          let ulen' = ulen - len in
229
 
          if ulen' = 0 then
230
 
            current_chunk <- None
231
 
          else
232
 
            current_chunk <- Some(u,upos+len,ulen');
233
 
          len
234
 
      | None ->
235
 
          (* After [refill] this is not possible *)
236
 
          assert false
237
 
 
238
 
  method close_in() =
239
 
    if closed then raise Closed_channel;
240
 
    if locked then failwith "Nethttpd_reactor: channel is locked";
241
 
    (* It is no problem to ignore further arriving tokens. These will be "eaten" by
242
 
     * [finish_request] later. (Of course, we could call [finish_request] here,
243
 
     * but that would probably defer the generation of responses.)
244
 
     *)
245
 
    closed <- true;
246
 
 
247
 
  method unlock() =
248
 
    locked <- false;
249
 
 
250
 
  method drop() =
251
 
    locked <- false;
252
 
    eof <- true
253
 
 
254
 
end
255
 
 
256
 
 
257
 
class http_reactor_output config resp synch =   (* an extension of rec_in_channel *)
258
 
object
259
 
  val mutable closed = false
260
 
  val mutable locked = true
261
 
 
262
 
  method output s spos slen =
263
 
    if closed then raise Closed_channel;
264
 
    if locked then failwith "Nethttpd_reactor: channel is locked";
265
 
    let u = String.sub s spos slen in
266
 
    resp # send (`Resp_body(u, 0, String.length u));
267
 
    ( match config#config_reactor_synch with
268
 
        | `Write ->
269
 
            synch()
270
 
        | _ ->
271
 
            ()
272
 
    );
273
 
    slen
274
 
 
275
 
  method flush() =
276
 
    if closed then raise Closed_channel;
277
 
    if locked then failwith "Nethttpd_reactor: channel is locked";
278
 
    match config#config_reactor_synch with
279
 
      | `Write
280
 
      | `Flush ->
281
 
          synch()
282
 
      | _ ->
283
 
          ()
284
 
 
285
 
  method close_out() =
286
 
    if closed then raise Closed_channel;
287
 
    if locked then failwith "Nethttpd_reactor: channel is locked";
288
 
    closed <- true;
289
 
    resp # send `Resp_end;
290
 
    match config#config_reactor_synch with
291
 
      | `Write
292
 
      | `Flush
293
 
      | `Close ->
294
 
          synch()
295
 
      | _ ->
296
 
          ()
297
 
 
298
 
  method close_after_send_file() =
299
 
    closed <- true;
300
 
    match config#config_reactor_synch with
301
 
      | `Write
302
 
      | `Flush
303
 
      | `Close ->
304
 
          synch()
305
 
      | _ ->
306
 
          ()
307
 
 
308
 
  method unlock() =
309
 
    locked <- false
310
 
 
311
 
end
312
 
 
313
 
 
314
 
class http_reactive_request_impl config env inch outch resp expect_100_continue
315
 
                                 finish_request
316
 
                                 : http_reactive_request =
317
 
object(self)
318
 
  method environment = 
319
 
    (env : internal_environment :> extended_environment)
320
 
 
321
 
  method accept_body() =
322
 
    if expect_100_continue then
323
 
      resp # send resp_100_continue;
324
 
    (* We need not to synch here! The attempt to read the body will synchronize
325
 
     * implicitly.
326
 
     * We should keep in mind, however, that when the existing body isn't read
327
 
     * the "100 Continue" might be transmitted very late. This is no disadvantage,
328
 
     * I think.
329
 
     *)
330
 
    inch # unlock();
331
 
    outch # unlock();
332
 
    env # unlock()
333
 
 
334
 
  method reject_body() =
335
 
    inch # drop();
336
 
    outch # unlock();
337
 
    env # unlock()
338
 
 
339
 
  val mutable fin_req = false
340
 
 
341
 
  method finish_request() =
342
 
    if not fin_req then (    (* Do this only once *)
343
 
      fin_req <- true;
344
 
      inch # drop();
345
 
      outch # unlock();
346
 
      env # unlock();
347
 
      finish_request();   (* Read the rest of the request until `Req_end *)
348
 
    )
349
 
 
350
 
  method finish() =
351
 
    self # finish_request();
352
 
    match env # output_state with
353
 
      | `Start ->
354
 
          (* The whole response is missing! Generate a "Server Error": *)
355
 
          output_std_response config env `Internal_server_error None 
356
 
            (Some "Nethttpd: Missing response, replying 'Server Error'");
357
 
          env # set_output_state `End;
358
 
      | `Sent_header
359
 
      | `Sending_body ->
360
 
          (* The response body is probably incomplete or missing. Try to close
361
 
           * the channel.
362
 
           *)
363
 
          ( try env # output_ch # close_out() with Closed_channel -> () );
364
 
          env # set_output_state `End;
365
 
      | `Sent_body 
366
 
      | `End ->
367
 
          (* Everything ok, just to be sure... *)
368
 
          ( try env # output_ch # close_out() with Closed_channel -> () );
369
 
          env # set_output_state `End;
370
 
      | _ ->
371
 
          (* These states must not happen! *)
372
 
          assert false
373
 
 
374
 
end
375
 
 
376
 
 
377
 
 
378
 
class http_reactor (config : #http_reactor_config) fd =
379
 
object(self)
380
 
  val proto = new http_protocol config fd
381
 
  val fd_addr = Unix.getsockname fd
382
 
  val peer_addr = Unix.getpeername fd
383
 
 
384
 
  method private cycle() =
385
 
    let block = 
386
 
      if proto # waiting_for_next_message then 
387
 
        config#config_timeout_next_request
388
 
      else
389
 
        config#config_timeout in
390
 
    proto # cycle ~block ();
391
 
 
392
 
  method private next_token() =
393
 
    if proto # recv_queue_len = 0 then (
394
 
      self # cycle();
395
 
      self # next_token()
396
 
    )
397
 
    else
398
 
      proto # receive() 
399
 
 
400
 
  method private peek_token() =
401
 
    if proto # recv_queue_len = 0 then (
402
 
      self # cycle();
403
 
      self # peek_token()
404
 
    )
405
 
    else
406
 
      proto # peek_recv() 
407
 
 
408
 
  method private finish_request() =
409
 
    (* Read the rest of the previous request, ignoring it *)
410
 
    match self # peek_token() with
411
 
      | `Req_header _
412
 
      | `Eof
413
 
      | `Fatal_error _
414
 
      | `Bad_request_error _
415
 
      | `Timeout ->
416
 
          (* Everything ok, do nothing *)
417
 
          ()
418
 
      | `Req_end ->
419
 
          (* Just drop this token, the next token starts the new request *)
420
 
          ignore(proto # receive ())
421
 
      | `Req_expect_100_continue
422
 
      | `Req_body _
423
 
      | `Req_trailer _ ->
424
 
          (* Continue to read this request until its end *)
425
 
          while
426
 
            match self # peek_token () with
427
 
              | `Req_header _
428
 
              | `Eof 
429
 
              | `Fatal_error _
430
 
              | `Bad_request_error _ 
431
 
              | `Timeout ->
432
 
                  false   (* Do not read further *)
433
 
              | _ ->
434
 
                  ignore(self # next_token());
435
 
                  true    (* Continue *)
436
 
          do
437
 
            ()
438
 
          done
439
 
 
440
 
 
441
 
  method private synch() =
442
 
    (* Ensure that all written data are actually transmitted: *)
443
 
    while proto # do_output do
444
 
      self # cycle();
445
 
    done;
446
 
    (* CHECK: Maybe we have to throw away the remaining tokens of the current request! *)
447
 
 
448
 
 
449
 
  method next_request () =
450
 
    let tok = self # next_token() in
451
 
    match tok with
452
 
      | `Req_header (req, req_hdr, resp) ->
453
 
          (* Ok, we have a new request. Initialize the new environment processing
454
 
           * it
455
 
           *)
456
 
          let expect_100_continue =
457
 
            try
458
 
              proto # peek_recv() = `Req_expect_100_continue
459
 
            with
460
 
                Recv_queue_empty -> false in
461
 
          if expect_100_continue then
462
 
            ignore(proto # receive());
463
 
 
464
 
          let ((req_meth, req_uri), req_version) = req in
465
 
 
466
 
          let input_ch = new http_reactor_input self#next_token in
467
 
          let output_ch = new http_reactor_output config resp self#synch in
468
 
          let lifted_input_ch = 
469
 
            lift_in ~buffered:false (`Rec (input_ch :> rec_in_channel)) in
470
 
          let lifted_output_ch = 
471
 
            lift_out (`Rec (output_ch :> rec_out_channel)) in
472
 
          (* The input channel needs no additional buffer here. The httpd kernel
473
 
           * already implements a good buffer.
474
 
           *
475
 
           * The output channel profits from a buffer. The effect is that the
476
 
           * kernel is driven with output chunks of uniform size. Furthermore,
477
 
           * `Write synchronization is only performed after every of these chunks,
478
 
           * and not after every output method invocation.
479
 
           *)
480
 
          
481
 
          ( try
482
 
              let env = new http_environment 
483
 
                          config 
484
 
                             req_meth req_uri req_version req_hdr 
485
 
                             fd_addr peer_addr
486
 
                           lifted_input_ch lifted_output_ch resp
487
 
                          output_ch#close_after_send_file
488
 
              in
489
 
              let req_obj = new http_reactive_request_impl 
490
 
                              config env input_ch output_ch resp expect_100_continue 
491
 
                              self#finish_request
492
 
              in
493
 
              Some req_obj
494
 
            with
495
 
                Standard_response(status, hdr_opt, msg_opt) ->
496
 
                  (* Probably a problem when decoding a header field! *)
497
 
                  ( match msg_opt with
498
 
                      | Some msg ->
499
 
                          config # config_log_error
500
 
                            (Some fd_addr) (Some peer_addr) (Some(req_meth,req_uri)) 
501
 
                            (Some req_hdr) msg
502
 
                      | None -> ()
503
 
                  );
504
 
                  (* CHECK: Also log to access log? *)
505
 
                  let code = int_of_http_status status in
506
 
                  let body = config # config_error_response code in
507
 
                  Nethttpd_kernel.send_static_response resp status hdr_opt body;
508
 
                  self # synch();
509
 
                  self # finish_request();
510
 
                  self # next_request()
511
 
          )
512
 
 
513
 
      | `Eof ->
514
 
          self # synch();
515
 
          None
516
 
          
517
 
      | `Fatal_error e ->
518
 
          (* The connection is already down. Just log the incident: *)
519
 
          let msg = Nethttpd_kernel.string_of_fatal_error e in
520
 
          config # config_log_error 
521
 
            (Some fd_addr) (Some peer_addr) None None msg;
522
 
          None
523
 
 
524
 
      | `Bad_request_error (e, resp) ->
525
 
          (* Log the incident, and reply with a 400 response: *)
526
 
          let msg = string_of_bad_request_error e in
527
 
          let status = status_of_bad_request_error e in
528
 
          config # config_log_error
529
 
            (Some fd_addr) (Some peer_addr) None None msg;
530
 
          let body = config # config_error_response (int_of_http_status status) in
531
 
          Nethttpd_kernel.send_static_response resp status None body;
532
 
          self # next_request()
533
 
 
534
 
      | `Timeout ->
535
 
          (* Just ignore. The next token will be `Eof *)
536
 
          self # next_request()
537
 
 
538
 
      | _ ->
539
 
          (* Everything else means that we lost synchronization, and this is a
540
 
           * fatal error!
541
 
           *)
542
 
          config # config_log_error 
543
 
            (Some fd_addr) (Some peer_addr) None None 
544
 
            "Nethttpd: Reactor out of synchronization";
545
 
          proto # abort `Server_error;
546
 
          self # next_request()
547
 
 
548
 
  method close () =
549
 
    ( try
550
 
        self # synch();
551
 
      with
552
 
        | err -> Unix.close fd; raise err
553
 
    );
554
 
    if proto # need_linger then (
555
 
      let lc = new Nethttpd_kernel.lingering_close fd in
556
 
      while lc # lingering do
557
 
        lc # cycle ~block:true ()
558
 
      done
559
 
    )
560
 
    else
561
 
      Unix.close fd
562
 
 
563
 
end
564
 
 
565
 
 
566
 
exception Redirect_response_legal of string * http_header
567
 
 
568
 
type x_reaction = 
569
 
    [ http_service_reaction
570
 
    | `Redirect_request of string * http_header
571
 
    ]
572
 
 
573
 
 
574
 
let process_connection config fd (stage1 : 'a http_service) =
575
 
 
576
 
  let _fd_addr = Unix.getsockname fd in
577
 
  let _peer_addr = Unix.getpeername fd in
578
 
 
579
 
  let protect env f arg =
580
 
    try
581
 
      f arg
582
 
    with
583
 
      | Redirect_response_legal(_,_) as e -> raise e
584
 
 
585
 
      | Standard_response(status, hdr_opt, errmsg_opt) when env#output_state = `Start ->
586
 
          output_std_response config env status hdr_opt errmsg_opt;
587
 
 
588
 
      | err when env#output_state = `Start ->
589
 
          output_std_response config env `Internal_server_error None 
590
 
            (Some("Nethttpd: Uncaught exception: " ^ Printexc.to_string err));
591
 
  in
592
 
 
593
 
  let do_stage3 env stage3 =
594
 
    try
595
 
      stage3 # generate_response env
596
 
    with
597
 
      | Redirect_request(_,_) ->
598
 
          failwith "Caught Redirect_request in stage 3, but it is only allowed in stage 1"
599
 
      | Redirect_response(uri,hdr) ->
600
 
          if env#output_state <> `Start then
601
 
            failwith "Caught Redirect_response, but it is too late for redirections";
602
 
          raise (Redirect_response_legal(uri,hdr))
603
 
  in
604
 
 
605
 
  let do_stage2 req env stage2 =
606
 
    let stage3 = 
607
 
      try
608
 
        stage2 # process_body env 
609
 
      with
610
 
        | Redirect_request(_,_) ->
611
 
            failwith "Caught Redirect_request in stage 2, but it is only allowed in stage 1"
612
 
        | Redirect_response(_,_) ->
613
 
            failwith "Caught Redirect_response in stage 2, but it is only allowed in stage 3"
614
 
    in
615
 
    req # finish_request();
616
 
    do_stage3 env stage3
617
 
  in
618
 
 
619
 
  let rec process_request req redir_env redir_count =
620
 
    (* [redir_env]: The environment of the request, possibly rewritten by redirects.
621
 
     * [redir_count]: The number of already performed redirections
622
 
     * [req]: Contains always the original environment
623
 
     *)
624
 
    if redir_count > 10 then
625
 
      failwith "Too many redirections";
626
 
    let reaction = 
627
 
      try (stage1 # process_header redir_env :> x_reaction)
628
 
      with 
629
 
        | Redirect_request(new_uri, new_hdr) ->
630
 
            `Redirect_request(new_uri, new_hdr)
631
 
        | Redirect_response(_,_) ->
632
 
            failwith "Caught Redirect_response in stage 1, but it is only allowed in stage 3"
633
 
    in
634
 
    ( try
635
 
        ( match reaction with
636
 
            | `Accept_body stage2 ->
637
 
                req # accept_body();
638
 
                protect redir_env (do_stage2 req redir_env) stage2
639
 
            | `Reject_body stage3 ->
640
 
                req # reject_body();
641
 
                protect redir_env (do_stage3 redir_env) stage3
642
 
            | `Static(status, resp_hdr_opt, resp_str) ->
643
 
                req # reject_body();
644
 
                output_static_response redir_env status resp_hdr_opt resp_str
645
 
            | `File(status, resp_hdr_opt, resp_filename, pos, length) ->
646
 
                req # accept_body();
647
 
                protect
648
 
                  redir_env 
649
 
                  (output_file_response redir_env status resp_hdr_opt resp_filename pos) 
650
 
                  length
651
 
            | `Std_response(status, resp_hdr_opt, errlog_opt) ->
652
 
                req # reject_body();
653
 
                output_std_response config redir_env status resp_hdr_opt errlog_opt
654
 
            | `Redirect_request(new_uri, new_hdr) ->
655
 
                let (new_script_name, new_query_string) = decode_query new_uri in
656
 
                new_hdr # update_multiple_field 
657
 
                  "Content-length" (redir_env # multiple_input_header_field "Content-length");
658
 
                let new_properties =
659
 
                  update_alist 
660
 
                    [ "REQUEST_URI", new_uri;
661
 
                      "SCRIPT_NAME", new_script_name;
662
 
                      "QUERY_STRING", new_query_string ] 
663
 
                    redir_env#cgi_properties in
664
 
                let new_env =
665
 
                  new redirected_environment 
666
 
                    ~properties:new_properties
667
 
                    ~in_header:new_hdr
668
 
                    ~in_channel:(redir_env # input_ch) redir_env in
669
 
                process_request req new_env (redir_count+1)
670
 
        )
671
 
      with
672
 
        | Redirect_response_legal(new_uri, new_hdr) ->
673
 
            if redir_env # output_state <> `Start then
674
 
              failwith "Redirect_response is not allowed after output has started";
675
 
            let (new_script_name, new_query_string) = decode_query new_uri in
676
 
            new_hdr # update_field "Content-length" "0";
677
 
            let new_properties =
678
 
              update_alist 
679
 
                [ "REQUEST_URI", new_uri;
680
 
                  "SCRIPT_NAME", new_script_name;
681
 
                  "QUERY_STRING", new_query_string;
682
 
                  "REQUEST_METHOD", "GET"
683
 
                ] 
684
 
                redir_env#cgi_properties in
685
 
            let new_env =
686
 
              new redirected_environment 
687
 
                ~properties:new_properties
688
 
                ~in_header:new_hdr
689
 
                redir_env in
690
 
            process_request req new_env (redir_count+1)
691
 
            
692
 
    );
693
 
    req # finish()
694
 
  in
695
 
 
696
 
  let rec fetch_requests reactor =
697
 
    match reactor # next_request() with
698
 
      | None ->
699
 
          ()
700
 
      | Some req ->
701
 
          process_request req req#environment 0;
702
 
          fetch_requests reactor
703
 
  in
704
 
  
705
 
  let reactor = 
706
 
    try
707
 
      new http_reactor config fd 
708
 
    with
709
 
        err ->
710
 
          (* An exception means here that getsockname or getpeername failed.
711
 
             We can only close the descriptor!
712
 
           *)
713
 
          Unix.close fd;
714
 
          raise err
715
 
  in
716
 
  ( try
717
 
      fetch_requests reactor
718
 
    with
719
 
        err ->
720
 
          config # config_log_error None None None None
721
 
                      ("Nethttpd: Uncaught exception: " ^ Printexc.to_string err);
722
 
  );
723
 
  ( try
724
 
      reactor # close()
725
 
    with
726
 
        err ->
727
 
          config # config_log_error None None None None
728
 
                      ("Nethttpd: Uncaught exception: " ^ Printexc.to_string err);
729
 
  )
730
 
;;