(* $Id: nethttpd_types.mli 1410 2010-02-14 19:44:28Z gerd $ * *) (* * Copyright 2005 Baretta s.r.l. and Gerd Stolpmann * * This file is part of Nethttpd. * * Nethttpd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Nethttpd is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with WDialog; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *) (** Type definitions for the HTTP daemon * * {b Contents} * * - {!Nethttpd_types.exceptions} * - {!Nethttpd_types.environment} * - {!Nethttpd_types.service} * - {!Nethttpd_types.helpers} *) (** Many types can also be found in the [Nethttp] module (part of netstring). * Furthermore, [Netcgi_env] and [Netcgi_types] are of interest (part of cgi). *) (** {1:exceptions Exceptions} *) open Nethttp exception Standard_response of http_status * http_header option * string option (** Some HTTP containers allow you to raise this exception. The standard * response corresponding to [http_status] is sent back to the client. * If the third argument exists, an entry into the error log * is written. *) (**********************************************************************) (** {1:environment Environment} *) type output_state = [ `Start | `Sending | `End ] val string_of_output_state : output_state -> string (** Debugging *) (** An extension of [cgi_environment] for use with the daemon. The methods * retrieving the socket addresses are virtual. *) class type virtual v_extended_environment = object inherit Netcgi.cgi_environment method virtual server_socket_addr : Unix.sockaddr method virtual remote_socket_addr : Unix.sockaddr (** These are always the physical IP addresses and ports of the two endpoints * of the current connection. *) method cgi_request_uri : string (** The full request URI. Identical to the CGI property "REQUEST_URI" *) method log_props : (string * string) list -> unit (** Remember this version of [cgi_properties] as the one sent to the [config_log_access] function *) method input_channel : Netchannels.in_obj_channel (** The input channel for reading the body of the request *) method input_body_size : int64 (** so far known, or 0L *) method request_body_rejected : bool (** so far known, or false *) method send_file : Unix.file_descr -> int64 -> unit (** Sends the output header with a file as body. The file must already be open, * and positioned where the transmission begins. The number is the length * of the transmission. * * This method may return immediately when it is possible to open the file, and * to set the kernel up for file transmission. Otherwise a [Unix_error] is * raised. It is also allowed that this method blocks until the file is actually * transmitted. * * It is not allowed to print to the output channel and to call [send_file]. * Only one transmission method must be invoked. *) method virtual output_state : output_state ref (** Reflects the state of the output generation: - [`Start]: Nothing is generated yet - [`Sending]: Output is already being sent - [`End]: The response (for a single request) has been fully sent *) end (** Same as [v_extended_environment], but no virtual methods *) class type extended_environment = object inherit v_extended_environment method server_socket_addr : Unix.sockaddr method remote_socket_addr : Unix.sockaddr method output_state : output_state ref end (** {2 Construction of environments} *) class virtual empty_environment : object inherit v_extended_environment val mutable config : Netcgi.config val mutable protocol : Nethttp.protocol val mutable in_header : http_header val mutable out_header : http_header val mutable properties : (string * string) list val mutable in_channel : Netchannels.in_obj_channel val mutable out_channel : Netchannels.out_obj_channel end (** This class implements an environment with defined internal containers. * These containers are empty, but fully functional. * The following methods are empty and should be redefined: * - [send_output_header] * - [send_file] * - [log_error] * - [log_props] * * The virtual methods, of course, must be defined! *) class redirected_environment : ?in_header : http_header -> ?properties : (string * string) list -> ?in_channel : Netchannels.in_obj_channel -> extended_environment -> extended_environment (** This class overlays the input-side containers of an existing environment. * The output-side containers ([out_header], and [out_channel]) * are physically identical with the existing environment. * * If one of the argument is not passed on class instantiation, the corresponding * overlay container is initialized with the current value of the passed * environment. As exception of this rule, the input channel is initialized with * an empty input channel. *) (** {2 Auxiliary Functions for Environments} *) val output_static_response : #extended_environment -> http_status -> http_header option -> string -> unit (** Outputs the string argument as response body, together with the given status and * the header (optional). Response header fields are set as follows: * - The [Content-Length] is set to the length of the string. * - The [Content-Type] is set to "text/html" unless given by the header. * * If the header is not passed, the header of the environment is taken. If the header * argument exists, however, it overrides the header of the environment. *) val output_file_response : #extended_environment -> http_status -> http_header option -> string -> int64 -> int64 -> unit (** Outputs the contents of a file as response body, together with the given status and * the header (optional). The string is the file name. The first int64 number is * the position in the file where to start, and the second number is the length * of the body. Response header fields are set as follows: * - The [Content-Length] is set to the length of the string. * - The [Content-Type] is set to "text/html" unless given by the header. * * Note that [Content-Range] is not set automatically, even if the file is only * partially transferred. * * If the header is not passed, the header of the environment is taken. If the header * argument exists, however, it overrides the header of the environment. * * The function raises [Sys_error] when the file cannot be read. *) (** {1 Generating error responses, logging} *) class type request_info = object method server_socket_addr : Unix.sockaddr (** The socket address of this server. May raise [Not_found] if there is no such address *) method remote_socket_addr : Unix.sockaddr (** The socket address of the client. May raise [Not_found] if there is no such address *) method request_method : string (** The method like [GET]. May raise [Not_found] *) method request_uri : string (** The URI of the client request. This is often without the server designation, i.e. just [/path?query]. May raise [Not_found] *) method input_header : Nethttp.http_header (** The request header. May raise [Not_found] *) method cgi_properties : (string * string) list (** The distilled CGI properties *) method input_body_size : int64 (** The size of the input body. May raise [Not_found] *) end class type full_info = object inherit request_info method response_status_code : int (** The HTTP status code to response *) method request_body_rejected : bool (** Whether the request body was rejected *) method output_header : Nethttp.http_header (** The response header *) method output_body_size : int64 (** The size of the output body. *) end class create_full_info : response_status_code:int -> request_body_rejected:bool -> output_header:Nethttp.http_header -> output_body_size:int64 -> request_info -> full_info (** Creates a [full_info] object by adding to a [request_info] object *) class type error_response_params = object inherit request_info method response_status_code : int (** The HTTP status code to response *) method error_message : string (** The error message explaining the detail that went wrong *) end class type min_config = object method config_error_response : error_response_params -> string method config_log_error : request_info -> string -> unit end (** Minimal configuration needed for [output_std_response] *) val output_std_response : #min_config -> #extended_environment -> http_status -> http_header option -> string option -> unit (** Outputs a "standard response" for the [http_status]. The string argument * is an optional entry into the error log. * * If the header is not passed, an empty header is taken. If the header argument * exists, this header is taken. The header of the environment is never taken. *) (**********************************************************************) (** {1:service Service Providers} * * Service providers are defined using the three class types: * - [http_service]: The service provider as such. When a HTTP header has been * received, and the service provider is invoked, it returns an object * fitting to the next class type, [http_service_receiver]. This object * is tagged with [`Accept_body]; at this point there are also alternate ways * of processing, see below. * - [http_service_receiver]: The task of this object is to receive the request * body. When the body has been completely received, the object is notified, * and returns a third object of type [http_service_generator]. * - [http_service_generator]: The task of this object is to generate the * response. * * An implementor is free to define only one class that satisfies all three * class types at once. However, this is only an option. * * The three objects reflect three stages of HTTP processing. The stages have * made explicit to allow the implementor of services to intercept the points * in time when the processing of the next stage begins. Furthermore, in multi-threaded * environments it is allowed that the stages are performed in the contexts of * different threads. * * In addition to the three-stage model there also several faster paths of * processing: * - [`Reject_body] can be used to refuse the acceptance of the request body when * it is already clear that an error response is sent back. This path skips * the stage [http_service_receiver]. * - [`Static] can be used to send a constant string back (only to be used * when the string needs not to be computed) * - [`File] can be used to send the contents of a file back (only to be used * when the file already exists) *) exception Redirect_request of string * http_header (** The "early" redirect is only allowed in stage 1 of HTTP processing. * The string argument is the new URI path of the request. The header can also * be exchanged except the fields that are needed to decode the request * body. It is not possible to change the method. *) exception Redirect_response of string * http_header (** The "late" redirect is only allowed in stage 3 of HTTP processing. * The string argument is the new URI path of the request. The header can also * be exchanged except the fields that are needed to decode the request * body. {b The method is always changed to [GET].} *) class type http_service_generator = object method generate_response : extended_environment -> unit (** Third stage of HTTP processing: * This method is called when the HTTP request has been completely received, * and the response is going to be generated. This method can again be called * from a different thread than the previous stages. It is allowed to spend * any necessary time to compute the response. * * When the method returns, the request processing is finished. No more data * is allowed to be written to the output channel past this moment. * * The method may raise [Standard_response] to generate one of the * standard messages. *) end class type http_service_receiver = object method process_body : extended_environment -> http_service_generator (** Second stage of HTTP processing: * This method is called when the body is expected to be arriving. Note that * the main difference to [process_header] is that this method can be * called from a different thread. It is allowed (and expected) that this method * blocks while reading the input. Of course, the input and output * channels of the environment are unlocked, and can be used. * * When the method returns, the request processing continues with stage 3. * Any body data not read is dropped. * * It is allowed that this method generates a response (or part of it), * although this should be better done in stage 3. * * The method may raise [Standard_response] to generate one of the * standard messages. * * One way of implementing this method is to instantiate [Netcgi.std_activation]. *) end type http_service_reaction = [ `Accept_body of http_service_receiver | `Reject_body of http_service_generator | `Static of http_status * http_header option * string | `File of http_status * http_header option * string * int64 * int64 | `Std_response of http_status * http_header option * string option ] (** Indicates the immediate reaction upon an arriving HTTP header: * - [`Accept_body] is the regular way of processing requests. If necessary, * the client is told to continue sending the rest of the request. * - [`Reject_body] can be used when the body of the request is not needed, * and the response will be negative. * - [`Static] means to send the header and a constant string back as response. * The header is taken from the environment if not explicitly passed, * Note: The [Content-Length] header is automatically added. The [Content-Type] * defaults to "text/html". * - [`File] is similar to this, but the data come from a file. The file * is specified by an absolute pathname. The range of the file is given * by the start position and the length of the range. * The header is taken from the environment if not explicitly passed, * Note: The [Content-Length] header is automatically added. The [Content-Type] * defaults to "text/html". * - [`Std_response] is similar to [`Static], however the body is the standard * text for the status code. If the header is omitted, it is taken as empty. * The third argument is an optional entry into the error log. * Note: The [Content-Length] header is automatically added. The [Content-Type] * defaults to "text/html". *) class type ['a] http_service = object method name : string (** The name of the type of the service provider *) method def_term :'a (** The definition term *) method print : Format.formatter -> unit (** Outputs the definition term to a formatter *) method process_header : extended_environment -> http_service_reaction (** First stage of HTTP processing: * This method is called when the HTTP header has been received. This method * must return quickly without blocking the thread how to go on. For example, * this could look as follows: * - Check whether the client is allowed to access this resource. If this * can be done immediately, it should be done now. (If an external service * must be queried, the check must not be done now, but deferred to the * second or third stage.) If the access is denied, an error response can * be sent back using [`Static], [`File], or, if computed, using [`Reject_body]. * - Check whether the request is delegated to another service provider * (e.g. lookup by hostname, by port number, or by URI). In this case, * the result of this [process_header] call is simply the result of the * [process_header] call of the other service provider. * - If this service provider generates the contents, there are mainly two * types of reaction. If the contents are stored in a file, one can simply * return [`File]. Otherwise, return [`Accept_body] to continue with the * second stage. Note that even if no body is expected to arrive, one must * go through the second stage, and drop any unexpected body. * * The argument of this function is the Netcgi environment. The header is * complete, including the request method. One cannot access the input and * output channels at this stage of processing. *) end (**********************************************************************) (** {1:helpers Helpers} *) val update_alist : ('a * 'b) list -> ('a * 'b) list -> ('a * 'b) list (** [update_alist updl l]: Returns the alist with all elements of [updl] * and all elements of [l] that are not member of [updl]. *)