(* $Id: nethttpd_engine.mli 1063 2006-12-17 20:54:34Z 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
*)
(** {1 The event-based encapsulation of the HTTP daemon}
*
* This is a user-friendlier encapsulation of the HTTP daemon. It uses
* the engine module defined in [Uq_engines].
*)
(* Integration into event-based server design *)
open Nethttpd_types
type engine_req_state =
[ `Received_header
| `Receiving_body
| `Received_request
| `Finishing
]
class type http_engine_config =
object
inherit Nethttpd_reactor.http_processor_config
method config_input_flow_control : bool
(** If [true], the engine stops reading input tokens from the HTTP kernel when
* there is data in the input channel of the engine not yet read. If [false],
* all available input tokens are fetched from the kernel and buffered up
* in the input channel.
*
* In general, this {b should} be set to [true]. However, this is only possible
* when the user of the engine is prepared for flow control. In particular,
* all data contained in the input channel must be immediately read, or else
* the engine blocks. By calling [input_ch_async # request_notification], the
* user can be notified when there is data to read.
*
* When set to [false], the engine never blocks, but the price is that the
* input channel may become as large as needed to store the whole request.
*
* The option [config_limit_pipeline_size] does not have any effect for engines.
*)
method config_output_flow_control : bool
(** If [true], the engine signals the user when there is already enough data
* to output, and no more output should be generated. The user can query
* this state by calling [output_ch_async # can_output], and react
* accordingly. The user can also ignore this signal, and the output channel
* buffers all data.
*
* If [false], the mentioned method [can_output] returns always [true]. This
* turns off flow control in the case it is implemented by the user of the
* engine, but actually not wanted.
*
* The internal processing of data is not affected by this configuration option.
* In doubt, set it to [true].
*)
end
class type extended_async_environment =
object
inherit extended_environment
(** Environment also providing asynchronous views to I/O *)
method input_ch_async : Uq_engines.async_in_channel
(** This is the [input_ch] channel taken as asynchonous channel. This type of
* channel indicates when data is available to read, and also sends notifications.
* Note that this is only an alternate interface of the [input_ch] object.
*
* The method [can_input] returns true when there is at least one byte of
* the body to read, or the EOF has been seen. The channel buffers any arriving
* data (which can be limited in amount by [config_pipeline_size]).
*
* The behaviour of this channel is influenced by the configuration option
* [config_input_flow_control].
*)
method output_ch_async : Uq_engines.async_out_channel
(** This is the [output_ch] channel taken as asynchronous channel. This type of
* channel indicates when space is available for further output, and also sends
* notifications.
* Note that this is only an alternate interface of the [output_ch] object.
*
* The method [can_output] returns [true] only when the internal buffer is empty,
* i.e. all data have been transmitted to the client. Independent of this, the
* channel buffers all data written to it.
*
* The behaviour of this channel is influenced by the configuration option
* [config_output_flow_control].
*)
end
class type http_request_header_notification =
object
(** Notification that a new request header has arrived
*
* This object notifies the user that a new request header has arrived.
* The header is accessible by the [environment] object. The channels
* also contained in this object are locked at this moment. The user must
* now either call [schedule_accept_body] or [schedule_reject_body]. The
* user will get a second notification (a [http_request_notification], below)
* when the request body has completely arrived (in case of acceptance), or
* immediately (in case of rejection). One can also call [schedule_finish]
* at any time to drop the current request.
*)
method req_state : engine_req_state
(** Returns the request processing state which is [`Received_header] at the
* moment when this notification is delivered.
*)
method environment : extended_async_environment
(** The request environment. Depending on the request processing state, parts
* of the environment are already set up or still unaccessible ("locked").
* In the state [`Received_header] only the request header and the
* derived fields are accessible, and the input and output channels are
* locked. In the state [`Receiving_body] the input channel is unlocked,
* but it is not yet filled (reading from it may cause the exception
* [Buffer_underrun]). The output channel remains locked.
* In the state [`Received_request], the input channel is unlocked and filled
* with data, and the output channel is unlocked, too.
*
* This environment is not fully CGI-compatible. In particular, the following
* differences exist:
* - There is no [cgi_path_info] and no [cgi_path_translated].
* - The user is always unauthenticated.
* - The [Status] response header works as in CGI. The [Location] header, however,
* must be a full URL when set (only browser redirects)
* - When the request body is transmitted by chunked encoding, the header
* [Content-Length] is not set. In CGI this is interpreted as missing body.
* It is unlikely that clients send requests with chunked encoding, as this
* may cause interoperability problems anyway.
*
*)
method schedule_accept_body : on_request:(http_request_notification -> unit) ->
?on_error:(unit -> unit) ->
unit -> unit
(** Schedules that the request body is accepted. In terms of HTTP, this sends the
* "100 Continue" response when necessary. One can reply with a positive or
* negative message.
*
* This method returns immediately, and sets callbacks for certain events.
* When the body has completely arrived (or is empty), the function
* [on_request] is called back. The argument is the full request notification
* (see below).
*
* When the request is dropped for some reason, [on_error] is called back instead.
* This can be used to free resources, for example.
*
* Neither of the callbacks must raise exceptions.
*)
method schedule_reject_body : on_request:(http_request_notification -> unit) ->
?on_error:(unit -> unit) ->
unit -> unit
(** Schedules that the request body is rejected. In terms of HTTP, this prevents
* sending the "100 Continue" response. Any arriving request body is silently
* discarded. One should immediately reply with an error mesage.
* negative message.
*
* This method returns immediately, and sets callbacks for certain events.
* When the body has completely arrived (or is empty), the function
* [on_request] is called back. The argument is the full request notification
* (see below).
*
* When the request is dropped for some reason, [on_error] is called back instead.
* This can be used to free resources, for example.
*
* Neither of the callbacks must raise exceptions.
*)
method schedule_finish : unit -> unit
(** Schedules that the request is finished. This method should be called after
* the regular processing of the request to ensure that the HTTP protocol
* is fulfilled. If the request body has not been
* fully read, this is now done, and its data are dropped. If the response
* is incomplete, it is completed. If the error is not recoverable, a "Server
* Error" is generated.
*)
end
and http_request_notification =
object
(** Notification that the whole request has arrived
*
* This object notifies the user that the request has fully arrived (including
* the body if accepted), and can now be responded. The [environment] is the
* same as in the request header notification, but the channels are now
* unlocked.
*)
method req_state : engine_req_state
(** Returns the request processing state which is [`Received_request] at the
* moment when this notification is delivered.
*)
method environment : extended_async_environment
(** The request environment. See above. *)
method schedule_finish : unit -> unit
(** Schedules that the request is finished. See above. *)
end
class http_engine : on_request_header:(http_request_header_notification -> unit) ->
unit ->
#http_engine_config -> Unix.file_descr ->
Unixqueue.unix_event_system ->
[unit] Uq_engines.engine
(** This engine processes the requests arriving on the file descriptor using
* the Unix event system. Whenever a new request header arrives, the function
* [on_request_header] is called back, and must handle the request.
*
* Unless aborted using the [