1
(* $Id: nethttpd_plex.ml 1101 2007-04-09 11:02:10Z gerd $ *)
6
type config_log_error =
7
Unix.sockaddr option -> Unix.sockaddr option -> Nethttp.http_method option -> Nethttp.http_header option -> string -> unit
9
class nethttpd_processor mk_config srv : Netplex_types.processor =
11
method process ~when_done (container : Netplex_types.container) fd proto =
12
let error_logger _ peeraddr_opt meth_opt _ msg =
14
Printf.sprintf "[%s] [%s] %s"
15
( match peeraddr_opt with
16
| Some(Unix.ADDR_INET(addr,port)) ->
17
Unix.string_of_inet_addr addr
18
| Some(Unix.ADDR_UNIX path) ->
31
container # log `Err s
33
let config = mk_config error_logger in
35
Nethttpd_reactor.process_connection config fd srv
38
container # log `Err ("Uncaught exception: " ^
39
Printexc.to_string err)
43
method receive_message _ _ _ = ()
44
method receive_admin_message _ _ _ = ()
45
method shutdown() = ()
46
method post_add_hook _ = ()
47
method post_rm_hook _ = ()
48
method pre_start_hook _ _ _ = ()
49
method post_start_hook _ = ()
50
method pre_finish_hook _ = ()
51
method post_finish_hook _ _ _ = ()
52
method supported_ptypes = [ `Multi_processing ; `Multi_threading ]
53
method global_exception_handler _ = false
56
let nethttpd_processor mk_config srv =
57
new nethttpd_processor mk_config srv
60
let is_options_request env =
61
env # cgi_request_method = "OPTIONS" && env # cgi_request_uri = "*"
63
let is_any_request env =
66
let ws_re = Pcre.regexp "[ \r\t\n]+"
69
Netstring_pcre.split ws_re s
71
let name_port_re = Pcre.regexp "^([^:]+):([0-9]+)$"
73
let split_name_port s =
74
match Netstring_pcre.string_match name_port_re s 0 with
76
let name = Netstring_pcre.matched_group m 1 s in
77
let port = Netstring_pcre.matched_group m 2 s in
78
(name, int_of_string port)
80
failwith "Bad name:port specifier"
82
let create_processor config_cgi handlers ctrl_cfg cfg addr =
84
let req_str_param addr name =
86
cfg#string_param (cfg # resolve_parameter addr name)
89
failwith ("Missing parameter: " ^ cfg#print addr ^ "." ^ name) in
91
let opt_str_param addr name =
93
Some(cfg#string_param (cfg # resolve_parameter addr name))
95
| Not_found -> None in
97
let float_param default addr name =
99
cfg#float_param (cfg # resolve_parameter addr name)
101
| Not_found -> default in
103
let bool_param addr name =
105
cfg#bool_param (cfg # resolve_parameter addr name)
107
| Not_found -> false in
109
let rec sub_service outermost_flag uri_path addr =
110
let host_sects = cfg # resolve_section addr "host" in
111
let uri_sects = cfg # resolve_section addr "uri" in
112
let method_sects = cfg # resolve_section addr "method" in
113
let service_sects = cfg # resolve_section addr "service" in
114
match (host_sects, uri_sects, method_sects, service_sects) with
116
linear_distributor [] (* Forces a 404 response *)
119
List.map (host_sub_service uri_path) host_sects in
120
host_distributor hosts
122
if outermost_flag then
123
failwith ("Outermost subsection must be 'host': " ^ cfg#print addr);
125
List.map (uri_sub_service uri_path) uri_sects in
128
if outermost_flag then
129
failwith ("Outermost subsection must be 'host': " ^ cfg#print addr);
131
List.map (method_sub_service uri_path) method_sects in
132
method_distributor methods
134
if outermost_flag then
135
failwith ("Outermost subsection must be 'host': " ^ cfg#print addr);
136
( match service_sects with
139
service uri_path service_sect
141
failwith ("Only one 'service' subsection is permitted: " ^ cfg#print addr);
144
failwith("Only one type of subsections host/uri/method/service is allowed: " ^ cfg#print addr)
146
and sub_service_ac uri_path addr =
147
(* With access control *)
148
let srv = sub_service false uri_path addr in
149
let access_sects = cfg # resolve_section addr "access" in
151
(fun srv access_sect ->
152
access_control access_sect srv)
156
and host_sub_service uri_path addr =
157
cfg # restrict_subsections addr [ "host"; "uri"; "method"; "service";
159
cfg # restrict_parameters addr [ "names"; "pref_name"; "pref_port" ];
161
let names_str = req_str_param addr "names" in
165
try split_name_port s
168
failwith (m ^ ": " ^ cfg#print addr ^ ".names")
170
(split_ws names_str) in
172
{ server_pref_name = opt_str_param addr "pref_name";
174
( try Some(cfg # int_param
175
(cfg # resolve_parameter addr "pref_port"))
176
with Not_found -> None
178
server_names = names;
181
(fun (_, port) -> (Unix.inet_addr_any, port))
183
(fun (name, _) -> name = "*")
187
let srv = sub_service_ac uri_path addr in
190
and uri_sub_service _ addr =
191
cfg # restrict_subsections addr [ "host"; "uri"; "method"; "service";
193
cfg # restrict_parameters addr [ "path" ];
194
let path = req_str_param addr "path" in
195
let srv = sub_service_ac path (* sic! *) addr in
198
and method_sub_service uri_path addr =
199
cfg # restrict_subsections addr [ "host"; "uri"; "method"; "service";
201
cfg # restrict_parameters addr [ "allow"; "deny" ];
202
let allow_opt = opt_str_param addr "allow" in
203
let deny_opt = opt_str_param addr "deny" in
205
match (allow_opt, deny_opt) with
206
| (Some host_list), None ->
207
`Limit (split_ws host_list)
208
| None, (Some host_list) ->
209
`Limit_except (split_ws host_list)
211
failwith ("Missing parameter 'allow' or 'deny': " ^ cfg#print addr)
213
failwith ("It is forbidden to specify both 'allow' and 'deny': " ^
216
let srv = sub_service_ac uri_path addr in
219
and access_control addr srv =
220
let typ = req_str_param addr "type" in
222
| "host" -> host_access_control addr srv
224
failwith ("Unknown access control type: " ^ cfg#print addr ^ ".type")
226
and host_access_control addr srv =
227
cfg # restrict_subsections addr [ ];
228
cfg # restrict_parameters addr [ "type"; "allow"; "deny" ];
229
let allow_opt = opt_str_param addr "allow" in
230
let deny_opt = opt_str_param addr "deny" in
232
match (allow_opt, deny_opt) with
233
| (Some host_list), None ->
234
`Allow (split_ws host_list)
235
| None, (Some host_list) ->
236
`Deny (split_ws host_list)
238
failwith ("Missing parameter 'allow' or 'deny': " ^ cfg#print addr)
240
failwith ("It is forbidden to specify both 'allow' and 'deny': " ^
242
ac_by_host (filter, srv)
244
and service uri_path addr =
245
let typ = req_str_param addr "type" in
247
| "file" -> file_service uri_path addr
248
| "dynamic" -> dynamic_service addr
250
failwith ("Unknown service type: " ^ cfg#print addr ^ ".type")
252
and file_service uri_path addr =
253
cfg # restrict_subsections addr [ "media_type" ];
254
cfg # restrict_parameters addr [ "type"; "media_types_file";
255
"docroot"; "default_media_type";
256
"enable_gzip"; "index_files";
258
"hide_from_listings" ];
262
cfg # restrict_subsections addr [];
263
cfg # restrict_parameters addr [ "suffix"; "type" ];
264
(req_str_param addr "suffix", req_str_param addr "type")
266
(cfg # resolve_section addr "media_type")
268
( match opt_str_param addr "media_types_file" with
271
read_media_types_file f
274
{ file_docroot = req_str_param addr "docroot";
275
file_uri = ( match opt_str_param addr "uri" with
278
file_suffix_types = suffix_types;
280
( match opt_str_param addr "default_media_type" with
281
| None -> "text/plain"
284
( if bool_param addr "enable_gzip" then
289
( match opt_str_param addr "index_files" with
291
| Some s -> [ `Enable_index_file (split_ws s) ]
293
( if bool_param addr "enable_listings" then
295
match opt_str_param addr "hide_from_listings" with
297
| Some s -> split_ws s in
298
let l = simple_listing ~hide in
299
[ `Enable_listings l ]
304
Nethttpd_services.file_service spec
306
and dynamic_service addr =
307
cfg # restrict_subsections addr [];
308
cfg # restrict_parameters addr [ "type"; "handler" ];
309
let handler_name = req_str_param addr "handler" in
312
List.assoc handler_name handlers
315
failwith ("Unknown handler `" ^ handler_name ^ "' in param " ^
316
cfg#print addr ^ ".handler") in
317
Nethttpd_services.dynamic_service srv
320
cfg # restrict_subsections addr [ "host"; "uri"; "method"; "service" ];
321
cfg # restrict_parameters addr [ "type"; "timeout"; "timeout_next_request" ];
325
[ is_options_request, options_service();
326
is_any_request, sub_service true "/" addr
329
let timeout = float_param 300.0 addr "timeout" in
330
let timeout_next_request = float_param 15.0 addr "timeout_next_request" in
334
method config_reactor_synch = `Write
335
method config_timeout_next_request = timeout_next_request
336
method config_timeout = timeout
337
method config_cgi = config_cgi
338
method config_error_response n =
340
"<html><body>Error " ^ string_of_int n ^ "</body></html>"
341
method config_log_error = cle
342
method config_max_reqline_length = 32768
343
method config_max_header_length = 65536
344
method config_max_trailer_length = 65536
345
method config_limit_pipeline_length = 1
346
method config_limit_pipeline_size = 65536
347
method config_announce_server = `Ocamlnet
357
class nethttpd_factory ?(config_cgi = Netcgi1_compat.Netcgi_env.default_config)
358
?(handlers=[]) () : processor_factory =
360
method name = "nethttpd"
361
method create_processor ctrl_cfg cfg addr =
362
create_processor config_cgi handlers ctrl_cfg cfg addr
365
let nethttpd_factory ?config_cgi ?handlers () =
366
new nethttpd_factory ?config_cgi ?handlers ()