1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
|
(*
OCaml HTTP - do it yourself (fully OCaml) HTTP daemon
Copyright (C) <2002-2005> Stefano Zacchiroli <zack@cs.unibo.it>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as
published by the Free Software Foundation, version 2.
This program 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 Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
*)
(** Type definitions *)
(** HTTP version, actually only 1.0 and 1.1 are supported. Note that
'supported' here means only 'accepted inside a HTTP request line', no
different behaviours are actually implemented depending on HTTP version *)
type version =
[ `HTTP_1_0
| `HTTP_1_1
]
(** HTTP method, actually only GET and POST methods are supported *)
type meth =
[ `GET
| `POST
]
(** Daemon behaviour wrt request handling. `Single mode use a single process
to handle all requests, no request is served until a previous one has been
fully served. `Fork mode fork a new process for each request, the new process
will execute the callback function and then exit. `Thread mode create a new
thread for each request, the new thread will execute the callback function and
then exit, threads can communicate using standard OCaml Thread library. *)
type daemon_mode = [ `Single | `Fork | `Thread ]
(** A TCP server is a function taking an address on which bind and listen for
connections, an optional timeout after which abort client connections and a
callback function which in turn takes an input and an output channel as
arguments. After receiving this argument a TCP server sits and waits for
connection, on each connection it apply the callback function to channels
connected to client. *)
type tcp_server =
sockaddr:Unix.sockaddr -> timeout:int option ->
(in_channel -> out_channel -> unit) ->
unit
(** authentication information *)
type auth_info =
[ `Basic of string * string (* username, password *)
(* | `Digest of ... (* TODO digest authentication *) *)
]
(** @see "RFC2616" informational HTTP status *)
type informational_substatus =
[ `Continue
| `Switching_protocols
]
(** @see "RFC2616" success HTTP status *)
type success_substatus =
[ `OK
| `Created
| `Accepted
| `Non_authoritative_information
| `No_content
| `Reset_content
| `Partial_content
]
(** @see "RFC2616" redirection HTTP status *)
type redirection_substatus =
[ `Multiple_choices
| `Moved_permanently
| `Found
| `See_other
| `Not_modified
| `Use_proxy
| `Temporary_redirect
]
(** @see "RFC2616" client error HTTP status *)
type client_error_substatus =
[ `Bad_request
| `Unauthorized
| `Payment_required
| `Forbidden
| `Not_found
| `Method_not_allowed
| `Not_acceptable
| `Proxy_authentication_required
| `Request_time_out
| `Conflict
| `Gone
| `Length_required
| `Precondition_failed
| `Request_entity_too_large
| `Request_URI_too_large
| `Unsupported_media_type
| `Requested_range_not_satisfiable
| `Expectation_failed
]
(** @see "RFC2616" server error HTTP status *)
type server_error_substatus =
[ `Internal_server_error
| `Not_implemented
| `Bad_gateway
| `Service_unavailable
| `Gateway_time_out
| `HTTP_version_not_supported
]
type informational_status = [ `Informational of informational_substatus ]
type success_status = [ `Success of success_substatus ]
type redirection_status = [ `Redirection of redirection_substatus ]
type client_error_status = [ `Client_error of client_error_substatus ]
type server_error_status = [ `Server_error of server_error_substatus ]
type error_status =
[ client_error_status
| server_error_status
]
(** HTTP status *)
type status =
[ informational_status
| success_status
| redirection_status
| client_error_status
| server_error_status
]
type status_code = [ `Code of int | `Status of status ]
(** File sources *)
type file_source =
| FileSrc of string (** filename *)
| InChanSrc of in_channel (** input channel *)
(** {2 Exceptions} *)
(** invalid header encountered *)
exception Invalid_header of string
(** invalid header name encountered *)
exception Invalid_header_name of string
(** invalid header value encountered *)
exception Invalid_header_value of string
(** unsupported or invalid HTTP version encountered *)
exception Invalid_HTTP_version of string
(** unsupported or invalid HTTP method encountered *)
exception Invalid_HTTP_method of string
(** invalid HTTP status code integer representation encountered *)
exception Invalid_code of int
(** invalid URL encountered *)
exception Malformed_URL of string
(** invalid query string encountered *)
exception Malformed_query of string
(** invalid query string part encountered, arguments are parameter name and
parameter value *)
exception Malformed_query_part of string * string
(** invalid request URI encountered *)
exception Malformed_request_URI of string
(** malformed request received *)
exception Malformed_request of string
(** malformed response received, argument is response's first line *)
exception Malformed_response of string
(** a parameter you were looking for was not found *)
exception Param_not_found of string
(** invalid HTTP status line encountered *)
exception Invalid_status_line of string
(** an header you were looking for was not found *)
exception Header_not_found of string
(** raisable by callbacks to make main daemon quit, this is the only
* 'clean' way to make start functions return *)
exception Quit
(** raisable by callbacks to force a 401 (unauthorized) HTTP answer.
* This exception should be raised _before_ sending any data over given out
* channel.
* @param realm authentication realm (usually needed to prompt user) *)
exception Unauthorized of string
(** {2 OO representation of HTTP messages} *)
(** HTTP generic messages. See {! Http_message.message} *)
class type message = object
method version: version option
method setVersion: version -> unit
method body: string
method setBody: string -> unit
method bodyBuf: Buffer.t
method setBodyBuf: Buffer.t -> unit
method addBody: string -> unit
method addBodyBuf: Buffer.t -> unit
method addHeader: name:string -> value:string -> unit
method addHeaders: (string * string) list -> unit
method replaceHeader: name:string -> value:string -> unit
method replaceHeaders: (string * string) list -> unit
method removeHeader: name:string -> unit
method hasHeader: name:string -> bool
method header: name:string -> string
method headers: (string * string) list
method clientSockaddr: Unix.sockaddr
method clientAddr: string
method clientPort: int
method serverSockaddr: Unix.sockaddr
method serverAddr: string
method serverPort: int
method toString: string
method serialize: out_channel -> unit
end
(** HTTP requests *)
class type request = object
(** an HTTP request is a flavour of HTTP message *)
inherit message
(** @return request method *)
method meth: meth
(** @return requested URI (including query string, fragment, ...) *)
method uri: string
(** @return requested path *)
method path: string
(** lookup a given parameter
@param meth if given restrict the lookup area (e.g. if meth = POST than
only parameters received via POST are searched), if not given both GET and
POST parameter are searched in an unspecified order (actually the
implementation prefers POST parameters but this is not granted, you've
been warned)
@param name name of the parameter to lookup
@return value associated to parameter name
@raise Param_not_found if parameter name was not found *)
method param: ?meth:meth -> string -> string
(** like param above but return a list of values associated to given
parameter (a parameter could be defined indeed more than once: passed more
than once in a query string or passed both insider the url (the GET way)
and inside message body (the POST way)) *)
method paramAll: ?meth:meth -> string -> string list
(** @return the list of all received parameters *)
method params: (string * string) list
(** @return the list of all parameters received via GET *)
method params_GET: (string * string) list
(** @return the list of all parameter received via POST *)
method params_POST: (string * string) list
(** @return authorization information, if given by the client *)
method authorization: auth_info option
end
(** HTTP responses *)
class type response = object
inherit message
(** @return response code *)
method code: int
(** set response code *)
method setCode: int -> unit
(** @return response status *)
method status: status
(** set response status *)
method setStatus: status -> unit
(** @return reason string *)
method reason: string
(** set reason string *)
method setReason: string -> unit
(** @return status line *)
method statusLine: string
(** set status line
@raise Invalid_status_line if an invalid HTTP status line was passed *)
method setStatusLine: string -> unit
(** response is an informational one *)
method isInformational: bool
(** response is a success one *)
method isSuccess: bool
(** response is a redirection one *)
method isRedirection: bool
(** response is a client error one *)
method isClientError: bool
(** response is a server error one *)
method isServerError: bool
(** response is either a client error or a server error response *)
method isError: bool
(** add basic headers to response, see {!Http_daemon.send_basic_headers}
*)
method addBasicHeaders: unit
(** facilities to access some frequently used headers *)
(** @return Content-Type header value *)
method contentType: string
(** set Content-Type header value *)
method setContentType: string -> unit
(** @return Content-Encoding header value *)
method contentEncoding: string
(** set Content-Encoding header value *)
method setContentEncoding: string -> unit
(** @return Date header value *)
method date: string
(** set Date header value *)
method setDate: string -> unit
(** @return Expires header value *)
method expires: string
(** set Expires header value *)
method setExpires: string -> unit
(** @return Server header value *)
method server: string
(** set Server header value *)
method setServer: string -> unit
end
(** {2 Daemon specification} *)
(** daemon specification, describe the behaviour of an HTTP daemon.
*
* The default daemon specification is {!Http_daemon.default_spec}
*)
type daemon_spec = {
address: string;
(** @param address adress on which daemon will be listening, can be both a
* numeric address (e.g. "127.0.0.1") and an hostname (e.g. "localhost") *)
auth: (string * auth_info) option;
(** authentication requirements (currently only basic authentication is
* supported). If set to None no authentication is required. If set to Some
* ("realm", `Basic ("foo", "bar")), only clients authenticated with baisc
* authentication, for realm "realm", providing username "foo" and password
* "bar" are accepted; others are rejected with a 401 response code *)
callback: request -> out_channel -> unit;
(** function which will be called each time a correct HTTP request will be
* received. 1st callback argument is an Http_types.request object
* corresponding to the request received; 2nd argument is an output channel
* corresponding to the socket connected to the client *)
mode: daemon_mode;
(** requests handling mode, it can have three different values:
* - `Single -> all requests will be handled by the same process,
* - `Fork -> each request will be handled by a child process,
* - `Thread -> each request will be handled by a (new) thread *)
port: int; (** TCP port on which the daemon will be listening *)
root_dir: string option;
(** directory to which ocaml http will chdir before starting handling
* requests; if None, no chdir will be performed (i.e. stay in the current
* working directory) *)
exn_handler: (exn -> out_channel -> unit) option;
(** what to do when executing callback raises an exception. If None, the
* exception will be re-raised: in `Fork/`Thread mode the current
* process/thread will be terminated. in `Single mode the exception is
* ignored and the client socket closed. If Some callback, the callback will
* be executed before acting as per None; the callback is meant to perform
* some clean up actions, like releasing global mutexes in `Thread mode *)
timeout: int option;
(** timeout in seconds after which an incoming HTTP request will be
* terminated closing the corresponding TCP connection; None disable the
* timeout *)
}
(** {2 OO representation of other HTTP entities} *)
(** an HTTP connection from a client to a server *)
class type connection =
object
(** @return next request object, may block if client hasn't submitted any
request yet, may be None if client request was ill-formed *)
method getRequest: request option
(** respond to client sending it a response *)
method respond_with: response -> unit
(** close connection to client. Warning: this object can't be used any
longer after this method has been called *)
method close: unit
end
(** an HTTP daemon *)
class type daemon =
object
(** @return a connection to a client, may block if no client has connected
yet *)
method accept: connection
(** shortcut method, blocks until a client has submit a request and
return a pair request * connection *)
method getRequest: request * connection
end
|