(* $Id$ *)

(** {1 Netplex support} *)

(** The important function is [nethttpd_factory], see below. The
    other functions are only needed for special effects.

    An example is explained here: {!Netplex_intro.webserver}
 *)

type config_log_error = Nethttpd_types.request_info -> string -> unit
type config_log_access = Nethttpd_types.full_info -> unit
type config_error_response = Nethttpd_types.error_response_params -> string
  (** Three type abbreviations for logging functions *)

val std_log_error : Netplex_types.container -> config_log_error 
  (** Returns a function that logs errors using the [log_subch] method of
      the passed container
   *)

val std_log_access : ?debug:bool -> 
                     Netplex_types.container -> config_log_access
  (** Returns a function that logs accesses using the [log_subch] method of
      the passed container

      If [debug] is set, additional debug log messages are printed that
      dump the whole access (incl. header and all available information)
   *)

val std_error_response : config_error_response
  (** A sample error response function *)

val restrict_file_service_config : Netplex_types.config_file ->
                                   Netplex_types.address ->  unit
  (** Restricts the subsections and paremeters in the [service]
      configuration section of type "file" to the allowed ones.
   *)


val read_file_service_config : Netplex_types.config_file ->
                               Netplex_types.address -> 
                               string ->
                                 Nethttpd_services.file_service
  (** [read_file_service_config cfg addr uri_path]: Reads the
      [service] configuration section of type "file" from config file
      [cfg] at address [addr].  [uri_path] is the default value put
      into the [file_uri] component of the returned record if no "uri"
      configuration parameter exists. (In other words, this is the
      path of the enclosing "uri" section, or "/" if there is only
      a "host" section.) All other parameters are only
      taken from the configuration section. 

      See below at [nethttpd_factory] how a file service needs to
      be configured.
   *)

val restrict_dynamic_service_config : Netplex_types.config_file ->
                                      Netplex_types.address ->  unit
  (** Restricts the subsections and paremeters in the [service]
      configuration section of type "dynamic" to the allowed ones.
   *)

val read_dynamic_service_config : 
      (string * (Netplex_types.config_file ->
                 Netplex_types.address -> 
                 string ->
                   'a Nethttpd_services.dynamic_service
                ) ) list ->
      Netplex_types.config_file ->
      Netplex_types.address -> 
      string ->
        'a Nethttpd_services.dynamic_service
  (** [read_dynamic_service_config handlers cfg addr uri_path]:
      Reads the [service] configuration section of type "dynamic" from config 
      file [cfg] at address [addr]. The alist [handlers] defines the
      available handlers. Every handler [h] is called like
      [h cfg addr uri_path]. [uri_path] is like in [read_file_service_config],
      i.e. the path of the enclosing "uri" section, or "/" by default.

      The [h] function has to return the dynamic service to use, which
      is also returned by [read_dynamic_service_config].

      See below at [nethttpd_factory] how a dynamic service needs to
      be configured.
   *)


type encap = [ `Reactor | `Engine ]

val nethttpd_processor : 
  ?hooks:Netplex_types.processor_hooks ->
  ?encap:encap ->
  (Netplex_types.container -> #Nethttpd_reactor.http_reactor_config) ->
  'a Nethttpd_types.http_service ->
  Netplex_types.processor
    (** [netplex_processor mk_config http_service]: Creates a Netplex processor
    * for Nethttpd.
    *
    * [mk_config] determines the nethttpd config for a container.
    * This is especially useful for setting the logging functions.
    *
    * The resulting processor must be turned into a full Netplex service
    * by [Netplex_sockserv.create_socket_service] which can then be added
    * by calling the controller's method [add_service].
    *
    * [hooks]: One can pass a Netplex hook object to set the hooks of the
    * processor.
    *
    * [encap]: Selects the encapsulation, [`Reactor] or [`Engine]. 
    * The default is [`Reactor]. Each encapsulation has specific strengths
    * and weaknesses:
    * - [`Reactor] is simpler code. Also, the request and response bodies
    *   need not to be buffered up, and are directly connected with the 
    *   underlying socket (low memory requirement). The disadvantage is
    *   that a reactor processes TCP connections serially (important to know
    *   when there is only a single Unix process)
    * - [`Engine]: The request body needs to be completely buffered up.
    *   If pipelining is enabled, the response bodies are also buffered
    *   (FIXME).
    *   The advantage of this encapsulation is that the engine can
    *   process multiple TCP connections simultaneously, even in a 
    *   single process/thread.
     *)

type ('a,'b) service_factory =
    (string * 'a Nethttpd_services.dynamic_service) list ->
    Netplex_types.config_file ->
    Netplex_types.address -> 
    string ->
      'b Nethttpd_types.http_service
    constraint 'b = [ `Dynamic_service of 'a Nethttpd_services.dynamic_service
                    | `File_service of Nethttpd_services.file_service 
		    ]
  (** The service factory function is called when a [service] configuration
      section of a certain type needs to be read. The function has args
      [handlers], [cfg], [addr], and [uri_path]. It needs to return the
      [http_service].

      Such a function is usually [read_file_service_config], or
      [read_dynamic_service_config], or a derivative, whose return
      value is turned into a [http_service]. This can be done with
      {!Nethttpd_services.file_service} and
      {!Nethttpd_services.dynamic_service}.
   *)

val default_services : (string * ('a,'b) service_factory) list
  (** The default services *)

type httpd_factory =
    { httpd_factory :
	'a . 
	  (Netplex_types.container -> Nethttpd_reactor.http_reactor_config) ->
	    'a Nethttpd_types.http_service ->
	      Netplex_types.processor
    }
  (** The type of the [nethttpd_processor] function *)


val nethttpd_factory :
      ?name:string ->
      ?hooks:Netplex_types.processor_hooks ->
      ?encap:encap ->
      ?config_cgi:Netcgi.config -> 
      ?handlers:(string * 'a Nethttpd_services.dynamic_service) list ->
      ?services:(string * ('a,'b) service_factory) list ->
      ?log_error:(Netplex_types.container -> config_log_error) ->
      ?log_access:(?debug:bool -> Netplex_types.container -> config_log_access) ->
      ?error_response:config_error_response -> 
      ?processor_factory:httpd_factory ->
      ?tls:(module Netsys_crypto_types.TLS_PROVIDER) ->
      unit ->
        Netplex_types.processor_factory
  (** Factory for a web server component.
    *
    * {b Configuration file.} See below.
    *
    * The [services] optional argument can be used to change the service
    * types understood. If not passed, it defaults to [default_services].
    * The default includes "file" and "dynamic".
    *
    * {b Arguments.}
    *
    * - [name]: The processor name. Defaults to "nethttpd". This name can
    *   be referenced by the "type" parameters in the [processor] section
    *   of the config file.
    * - [hooks]: One can pass a Netplex hook object to set the hooks of the
    *   processor. (This argument is ignored if a [processor_factory] is
    *   passed to this function.)
    * - [encap]: See {!Nethttpd_plex.nethttpd_processor}. (This argument is
    *   ignored if a [processor_factory] is
    *   passed to this function.)
    * - [config_cgi]: The CGI configuration to use
    * - [handlers]: a list of handler function. These functions can be
    *   referenced from a [service] section in the config file where
    *   [type="dynamic"] (see example above). Defaults to the empty list.
    * - [services]: A list of service handlers that can be used 
    *   by [service] sections in the config files. Defaults to
    *   {!Nethttpd_plex.default_services} which defines "file" and "dynamic".
    * - [log_error]: The error logger. Defaults to
    *   {!Nethttpd_plex.std_log_error}.
    * - [log_access]: The access logger. Defaults to
    *   {!Nethttpd_plex.std_log_access}.
    * - [error_response]: a handler which is invoked to generate error
    *   responses. Defaults to {!Nethttpd_plex.std_error_response}.
    * - [processor_factory]: the function creating the processor.
    *   Default is [nethttpd_processor].
    * - [tls]: the TLS provider to use. By default, 
    *   {!Netsys_crypto.current_tls_opt} is used.
   *)


(**

{2 Configuration files}

The configuration file understood by [nethttpd_factory] looks like:

 {[
    processor {
      type = "nethttpd";          (* or what is passed as "name" arg *)
      timeout = 300.0;
      timeout_next_request = 15.0;
      access_log = "enabled";
      suppress_broken_pipe = true;
      host {
        pref_name = "myhost";     (* optional *)
        pref_port = 80;           (* optional *)
        names = "myhost:80 yourhost:81";  (* use *:0 for any name *)
        uri {
          path = "/the/path";
          method {
            allow = "GET POST";
            (* or: deny = "..." *)
            service {
              type = "...";
              ...
            }
          }
        }
        uri {
          ...
        }
      }
      host {
        ...
      }
    }
 ]}

 The [access_log] parameter can be set to [off], [enabled], or [debug].
 The default is [off]. Access messages go to the "access" subchannel
 of the component logger. If [enabled], one line is printed with the
 most important data. If [debug] is set, all access data are printed.

 If [suppress_broken_pipe] the error "Broken pipe" is not logged
 in the error log. This error occurs frequently, and may be regarded
 as a normal condition.

 The sections [host], [uri] and [method] can be nested to any depth.
 However, on every nesting level only one of these section types must be
 used. For example, if a [host] section already contains [uri]
 subsections, it is not allowed to add [method] subsections.
 Furthermore, the outermost section must be [host].

 The [service] section may be one of (at least if the [services]
 parameter is not overridden):

 {[
    service {
      type = "file";
      docroot = "/a/path/in/the/filesystem";
      uri = "/the/uri/prefix/corresponding/to/docroot";
      media_types_file = "/etc/mime.types";
      media_type {
        type = "application/foo";
        suffix = "foo"
      }
      default_media_type = "text/plain";
      enable_gzip = true;   (* see doc in nethttpd_services.mli *)
      index_files = "index.html";
      enable_listings = true;
      hide_from_listings = "README";   (* list of PCRE regexps *)
    }
 ]}

 Note that [uri] is taken from the surrounding [uri] section (or
 assumed to be "/" if there is none) if omitted.

 {[
    service {
      type = "dynamic";
      handler = "name_of_handler";
    }
 ]}

 Binds the passed handler here.

 Any of [host], [uri], and [method] sections may contain one or several
 [access] sections (which are AND-connected):

 {[
    access {
      type = "host";
      allow = "host1 host2 ...";
      (* or deny = "host1 host2 ..."; *)
    }
 ]}

 Other access control methods are not yet available.
 *)


(**

{2:tls Configuring TLS}

First of all, there needs to be a TLS provider. See {!Tls} for more information
how to get one.

If the TLS provider is initialized, you can have a [tls] subsection inside
[processor], like:

{[
processor {
  ...
  tls {
    x509 {
      trust {
        crt_file = "ca.pem";
      }
      key {
        crt_file = "server.crt";
        key_file = "server.key";
      }
    }
  }
}
]}

All of the files, [ca.pem], [server.crt] and [server.key] need to be 
PEM-encoded. You can have several [key] sub-sections when you want to do
name-based virtual hosting. Note, however, that this relies on the SNI
extension of the TLS protocol, and not all clients support this extension.

Further parameters inside [tls]:

 - [algorithms]: This is a string describing the cipher suites and protocol
   options that are in effect. This string is provider-specific. (GnuTLS
   calls this string the "priority string".) Example:
   {[
tls {
   ...
   algorithms = "SECURE-128 !ARCFOUR-128";
}
   ]}
 - [dh_params]: This sub-section may set DH parameters. Example:
   {[
tls {
  ...
  dh_params {
    pkcs3_file = "/path/to/pkcs3_file";
  }
}
   ]}
 - [peer_auth]: This string parameter may enable client certificates.
   Set it to "required" to enforce such a certificate.
   {[
tls {
  ...
  peer_auth = "required";
}
   ]}



 *)
