2 OCaml HTTP - do it yourself (fully OCaml) HTTP daemon
4 Copyright (C) <2002-2007> Stefano Zacchiroli <zack@cs.unibo.it>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation, version 2.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 (** Type definitions *)
23 (** HTTP version, actually only 1.0 and 1.1 are supported. Note that
24 'supported' here means only 'accepted inside a HTTP request line', no
25 different behaviours are actually implemented depending on HTTP version *)
31 (** HTTP method, actually only GET and POST methods are supported *)
37 (** Daemon behaviour wrt request handling. `Single mode use a single process
38 to handle all requests, no request is served until a previous one has been
39 fully served. `Fork mode fork a new process for each request, the new process
40 will execute the callback function and then exit. `Thread mode create a new
41 thread for each request, the new thread will execute the callback function and
42 then exit, threads can communicate using standard OCaml Thread library. *)
43 type daemon_mode = [ `Single | `Fork | `Thread ]
45 (** A TCP server is a function taking an address on which bind and listen for
46 connections, an optional timeout after which abort client connections and a
47 callback function which in turn takes an input and an output channel as
48 arguments. After receiving this argument a TCP server sits and waits for
49 connection, on each connection it apply the callback function to channels
50 connected to client. *)
52 sockaddr:Unix.sockaddr -> timeout:int option ->
53 (in_channel -> out_channel -> unit) ->
56 (** authentication information *)
58 [ `Basic of string * string (* username, password *)
59 (* | `Digest of ... (* TODO digest authentication *) *)
62 (** @see "RFC2616" informational HTTP status *)
63 type informational_substatus =
65 | `Switching_protocols
68 (** @see "RFC2616" success HTTP status *)
69 type success_substatus =
73 | `Non_authoritative_information
79 (** @see "RFC2616" redirection HTTP status *)
80 type redirection_substatus =
90 (** @see "RFC2616" client error HTTP status *)
91 type client_error_substatus =
99 | `Proxy_authentication_required
104 | `Precondition_failed
105 | `Request_entity_too_large
106 | `Request_URI_too_large
107 | `Unsupported_media_type
108 | `Requested_range_not_satisfiable
109 | `Expectation_failed
112 (** @see "RFC2616" server error HTTP status *)
113 type server_error_substatus =
114 [ `Internal_server_error
117 | `Service_unavailable
119 | `HTTP_version_not_supported
122 type informational_status = [ `Informational of informational_substatus ]
123 type success_status = [ `Success of success_substatus ]
124 type redirection_status = [ `Redirection of redirection_substatus ]
125 type client_error_status = [ `Client_error of client_error_substatus ]
126 type server_error_status = [ `Server_error of server_error_substatus ]
129 [ client_error_status
130 | server_error_status
135 [ informational_status
138 | client_error_status
139 | server_error_status
142 type status_code = [ `Code of int | `Status of status ]
146 | FileSrc of string (** filename *)
147 | InChanSrc of in_channel (** input channel *)
149 (** {2 Exceptions} *)
151 (** invalid header encountered *)
152 exception Invalid_header of string
154 (** invalid header name encountered *)
155 exception Invalid_header_name of string
157 (** invalid header value encountered *)
158 exception Invalid_header_value of string
160 (** unsupported or invalid HTTP version encountered *)
161 exception Invalid_HTTP_version of string
163 (** unsupported or invalid HTTP method encountered *)
164 exception Invalid_HTTP_method of string
166 (** invalid HTTP status code integer representation encountered *)
167 exception Invalid_code of int
169 (** invalid URL encountered *)
170 exception Malformed_URL of string
172 (** invalid query string encountered *)
173 exception Malformed_query of string
175 (** invalid query string part encountered, arguments are parameter name and
177 exception Malformed_query_part of string * string
179 (** invalid request URI encountered *)
180 exception Malformed_request_URI of string
182 (** malformed cookies *)
183 exception Malformed_cookies of string
185 (** malformed request received *)
186 exception Malformed_request of string
188 (** malformed response received, argument is response's first line *)
189 exception Malformed_response of string
191 (** a parameter you were looking for was not found *)
192 exception Param_not_found of string
194 (** invalid HTTP status line encountered *)
195 exception Invalid_status_line of string
197 (** an header you were looking for was not found *)
198 exception Header_not_found of string
200 (** raisable by callbacks to make main daemon quit, this is the only
201 * 'clean' way to make start functions return *)
204 (** raisable by callbacks to force a 401 (unauthorized) HTTP answer.
205 * This exception should be raised _before_ sending any data over given out
207 * @param realm authentication realm (usually needed to prompt user) *)
208 exception Unauthorized of string
210 (** {2 OO representation of HTTP messages} *)
212 (** HTTP generic messages. See {! Http_message.message} *)
213 class type message = object
215 method version: version option
216 method setVersion: version -> unit
219 method setBody: string -> unit
220 method bodyBuf: Buffer.t
221 method setBodyBuf: Buffer.t -> unit
222 method addBody: string -> unit
223 method addBodyBuf: Buffer.t -> unit
225 method addHeader: name:string -> value:string -> unit
226 method addHeaders: (string * string) list -> unit
227 method replaceHeader: name:string -> value:string -> unit
228 method replaceHeaders: (string * string) list -> unit
229 method removeHeader: name:string -> unit
230 method hasHeader: name:string -> bool
231 method header: name:string -> string
232 method headers: (string * string) list
234 method clientSockaddr: Unix.sockaddr
235 method clientAddr: string
236 method clientPort: int
238 method serverSockaddr: Unix.sockaddr
239 method serverAddr: string
240 method serverPort: int
242 method toString: string
243 method serialize: out_channel -> unit
248 class type request = object
250 (** an HTTP request is a flavour of HTTP message *)
253 (** @return request method *)
256 (** @return requested URI (including query string, fragment, ...) *)
259 (** @return requested path *)
262 (** lookup a given parameter
263 @param meth if given restrict the lookup area (e.g. if meth = POST than
264 only parameters received via POST are searched), if not given both GET
265 and POST parameter are searched in an unspecified order (actually the
266 implementation prefers POST parameters but this is not granted, you've
268 @param default if provided, this value will be returned in case no
269 parameter of that name is available instead of raising Param_not_found
270 @param name name of the parameter to lookup
271 @return value associated to parameter name
272 @raise Param_not_found if parameter name was not found *)
273 method param: ?meth:meth -> ?default:string -> string -> string
275 (** like param above but return a list of values associated to given
276 parameter (a parameter could be defined indeed more than once: passed more
277 than once in a query string or passed both insider the url (the GET way)
278 and inside message body (the POST way)) *)
279 method paramAll: ?meth:meth -> string -> string list
281 (** @return the list of all received parameters *)
282 method params: (string * string) list
284 (** @return the list of all parameters received via GET *)
285 method params_GET: (string * string) list
287 (** @return the list of all parameter received via POST *)
288 method params_POST: (string * string) list
290 method cookies: (string * string) list option
292 (** @return authorization information, if given by the client *)
293 method authorization: auth_info option
297 (** HTTP responses *)
298 class type response = object
302 (** @return response code *)
305 (** set response code *)
306 method setCode: int -> unit
308 (** @return response status *)
309 method status: status
311 (** set response status *)
312 method setStatus: status -> unit
314 (** @return reason string *)
315 method reason: string
317 (** set reason string *)
318 method setReason: string -> unit
320 (** @return status line *)
321 method statusLine: string
324 @raise Invalid_status_line if an invalid HTTP status line was passed *)
325 method setStatusLine: string -> unit
327 (** response is an informational one *)
328 method isInformational: bool
330 (** response is a success one *)
331 method isSuccess: bool
333 (** response is a redirection one *)
334 method isRedirection: bool
336 (** response is a client error one *)
337 method isClientError: bool
339 (** response is a server error one *)
340 method isServerError: bool
342 (** response is either a client error or a server error response *)
345 (** add basic headers to response, see {!Http_daemon.send_basic_headers}
347 method addBasicHeaders: unit
349 (** facilities to access some frequently used headers *)
351 (** @return Content-Type header value *)
352 method contentType: string
354 (** set Content-Type header value *)
355 method setContentType: string -> unit
357 (** @return Content-Encoding header value *)
358 method contentEncoding: string
360 (** set Content-Encoding header value *)
361 method setContentEncoding: string -> unit
363 (** @return Date header value *)
366 (** set Date header value *)
367 method setDate: string -> unit
369 (** @return Expires header value *)
370 method expires: string
372 (** set Expires header value *)
373 method setExpires: string -> unit
375 (** @return Server header value *)
376 method server: string
378 (** set Server header value *)
379 method setServer: string -> unit
383 (** {2 Daemon specification} *)
385 (** daemon specification, describe the behaviour of an HTTP daemon.
387 * The default daemon specification is {!Http_daemon.default_spec}
391 (** @param address adress on which daemon will be listening, can be both a
392 * numeric address (e.g. "127.0.0.1") and an hostname (e.g. "localhost") *)
393 auth: (string * auth_info) option;
394 (** authentication requirements (currently only basic authentication is
395 * supported). If set to None no authentication is required. If set to Some
396 * ("realm", `Basic ("foo", "bar")), only clients authenticated with baisc
397 * authentication, for realm "realm", providing username "foo" and password
398 * "bar" are accepted; others are rejected with a 401 response code *)
399 callback: request -> out_channel -> unit;
400 (** function which will be called each time a correct HTTP request will be
401 * received. 1st callback argument is an Http_types.request object
402 * corresponding to the request received; 2nd argument is an output channel
403 * corresponding to the socket connected to the client *)
405 (** requests handling mode, it can have three different values:
406 * - `Single -> all requests will be handled by the same process,
407 * - `Fork -> each request will be handled by a child process,
408 * - `Thread -> each request will be handled by a (new) thread *)
409 port: int; (** TCP port on which the daemon will be listening *)
410 root_dir: string option;
411 (** directory to which ocaml http will chdir before starting handling
412 * requests; if None, no chdir will be performed (i.e. stay in the current
413 * working directory) *)
414 exn_handler: (exn -> out_channel -> unit) option;
415 (** what to do when executing callback raises an exception. If None, the
416 * exception will be re-raised: in `Fork/`Thread mode the current
417 * process/thread will be terminated. in `Single mode the exception is
418 * ignored and the client socket closed. If Some callback, the callback will
419 * be executed before acting as per None; the callback is meant to perform
420 * some clean up actions, like releasing global mutexes in `Thread mode *)
422 (** timeout in seconds after which an incoming HTTP request will be
423 * terminated closing the corresponding TCP connection; None disable the
426 (** whether ocaml-http will automatically close the connection with the
427 * client after callback has completed its execution. If set to true, close
428 * will be attempted no matter if the callback raises an exception or not *)
431 (** {2 OO representation of other HTTP entities} *)
433 (** an HTTP connection from a client to a server *)
434 class type connection =
436 (** @return next request object, may block if client hasn't submitted any
437 request yet, may be None if client request was ill-formed *)
438 method getRequest: request option
440 (** respond to client sending it a response *)
441 method respond_with: response -> unit
443 (** close connection to client. Warning: this object can't be used any
444 longer after this method has been called *)
448 (** an HTTP daemon *)
451 (** @return a connection to a client, may block if no client has connected
453 method accept: connection
455 (** shortcut method, blocks until a client has submit a request and
456 return a pair request * connection *)
457 method getRequest: request * connection