(* $Id: uq_multiplex.mli 1941 2013-12-30 23:12:26Z gerd $ *)
(** {1 Multiplex Controllers} *)
(** A [multiplex_controller] is a quite low-level device to abstract
* bidirectional socket connections. It is independent of any real
* device.
*
* There can be a reader, a writer (or both), or alternatively,
* the shutdown process may be in progress. One cannot have more than
* one reader and more than more writer.
*)
class type multiplex_controller =
object
method alive : bool
(** If the controller is alive, the socket is not yet completely down. *)
method mem_supported : bool
(** Whether [start_mem_reading] and [start_mem_writing] are possible *)
method event_system : Unixqueue.event_system
(** Returns the event system *)
method tls_session_props : Nettls_support.tls_session_props option
(** If TLS is enabled, this returns the session properties. These
are first available after the TLS handshake.
*)
method tls_session : (string * string) option
(** If TLS is enabled, this returns (session_id, session_data). This
is first available after the TLS handshake.
*)
method tls_stashed_endpoint : unit -> exn
(** Returns the TLS endpoint in stashed form. Note that the
multiplex controller becomes immediately unusable.
*)
method reading : bool
(** True iff there is a reader *)
method start_reading :
?peek:(unit -> unit) ->
when_done:(exn option -> int -> unit) -> string -> int -> int -> unit
(** Start reading from the connection. When data is available, the
* [when_done] callback is invoked. The int is the number of read
* bytes. It is 0 if an error occurred which is indicated by the
* exception. The exception [End_of_file] is used when the end of the
* data stream is reached. The exception [Cancelled] indicates that
* reading has been cancelled in the meantime.
*
* This starts one-time read job only, i.e. it is not restarted
* after [when_done] has been invoked.
*
* It is an error to start reading several times.
*
* The function [peek] is called immediately before data is read in
* from the underlying communication channel.
*
* For getting an engine-based version of [start_reading], use
* a [signal_engine]:
* {[
* let (e, signal) = signal_engine esys in
* mplex # start_reading ~when_done:(fun xo n -> signal (xo,n)) ...
* ]}
* Now [e] will transition to [`Done(x0,n)] when the read is done.
*)
method start_mem_reading :
?peek:(unit -> unit) ->
when_done:(exn option -> int -> unit) -> Netsys_mem.memory -> int -> int ->
unit
(** Same as [start_reading], but puts the data into a [memory] buffer.
There is an optimization for the case that the descriptor is a
connected socket, or supports [Unix.read]. If this is not possible
the method raises [Mem_not_supported].
*)
method cancel_reading : unit -> unit
(** Cancels the read job. The [when_done] callback is invoked with the
* number of bytes read so far (which may be 0) and the exception
* [Cancelled].
*
* It is no error if there is no reader.
*)
method writing : bool
(** True iff there is a writer *)
method start_writing :
when_done:(exn option -> int -> unit) -> string -> int -> int -> unit
(** Start writing to the connection. When data is written, the
* [when_done] callback is invoked. The int is the number of written
* bytes. It is 0 if an error occurred which is indicated by the
* exception. The exception [Cancelled] indicates that
* writing has been cancelled in the meantime.
*
* This starts one-time write job only, i.e. it is not restarted
* after [when_done] has been invoked.
*
* It is an error to start writing several times.
*
* See the comment for [start_reading] for how to get an engine-based
* version of this method.
*)
method start_mem_writing :
when_done:(exn option -> int -> unit) -> Netsys_mem.memory -> int -> int ->
unit
(** Same as [start_writing], but takes the data from a [memory] buffer.
There is an optimization for the case that the descriptor is a
connected socket, or supports [Unix.write]. If this is not possible
the method raises [Mem_not_supported].
*)
method supports_half_open_connection : bool
(** Whether the underlying transport mechanism can close the write side
* of the connection only (half-open connection).
*)
method start_writing_eof :
when_done:(exn option -> unit) -> unit -> unit
(** Start writing the EOF marker to the connection. When it is written,
* the [when_done] callback is invoked. The exception [Cancelled] indicates
* that writing has been cancelled in the meantime.
*
* This starts one-time write job only, i.e. it is not restarted
* after [when_done] has been invoked.
*
* It is an error to start writing several times. It is an error to
* write EOF when the socket does not support half-open connections.
*
* See the comment for [start_reading] for how to get an engine-based
* version of this method.
*)
method cancel_writing : unit -> unit
(** Cancels the write job. The [when_done] callback is invoked with the
* number of bytes read so far (which may be 0) and the exception
* [Canelled].
*
* It is no error if there is no writer.
*)
method read_eof : bool
(** Whether the EOF marker has been read *)
method wrote_eof : bool
(** Whether the EOF marker has been written *)
method shutting_down : bool
(** True iff the shutdown is in progress *)
method start_shutting_down :
?linger : float ->
when_done:(exn option -> unit) -> unit -> unit
(** Start shutting down the connection. After going through the shutdown
* procedure, the [when_done] callback is invoked. The exception
* indicates whether an error happened. [Cancelled] means that the
* shutdown operation has been cancelled in the meantime.
*
* The underlying file descriptor (if any) is not closed. A shutdown
* is only a protocol handshake. After a shutdown, both [read_eof]
* and [wrote_eof] are true. Call [inactivate] to close the descriptor.
*
* Optionally, one can [linger] for a certain period of time.
* It is only lingered when the EOF was written before the EOF
* is seen on input.
* Defaults to [linger 60.0]. Set to 0 to turn off.
*
* See the comment for [start_reading] for how to get an engine-based
* version of this method.
*)
method cancel_shutting_down : unit -> unit
(** Cancels the shutdown procedure. After that, the state of the
* connection is undefined. The [when_done] callback is invoked with
* the exception [Cancelled].
*
* It is no error if no shutdown is in progress.
*)
method inactivate : unit -> unit
(** Inactivates the connection immediately, and releases any resources
* the controller is responsible for (e.g. closes file descriptors).
* Note that this is more than
* cancelling all pending operations and shutting the connection down.
* However, the details of this method are implementation-defined.
* Callbacks are not invoked.
*)
end
(** Additional methods for unconnected datagram handling *)
class type datagram_multiplex_controller =
object
inherit multiplex_controller
method received_from : Unix.sockaddr
(** Returns the socket address of the last received datagram. This
* value is updated just before the [when_done] callback of the
* reader is invoked.
*)
method send_to : Unix.sockaddr -> unit
(** Sets the socket address of the next datagram to send. *)
end
exception Mem_not_supported
(** May be raised by multiplex controller methods [start_mem_reading] and
[start_mem_writing] if these methods are not supported for the kind
of file descriptor
*)
val create_multiplex_controller_for_connected_socket :
?close_inactive_descr:bool ->
?preclose:(unit -> unit) ->
?supports_half_open_connection:bool ->
?timeout:(float * exn) ->
Unix.file_descr -> Unixqueue.unix_event_system -> multiplex_controller
(** Creates a multiplex controller for a bidirectional socket (e.g.
* a TCP socket). It is essential that the socket is in connected state.
* This function also supports Win32 named pipes.
*
* Note that the file descriptor is not closed when the attached engines
* are terminated. One can call [inactivate] manually to do that.
*
* [close_inactive_descr]: Whether [inactivate] closes the descriptor.
* True by default.
*
* [preclose]: This function is called just before the descriptor is
* closed.
*
* [supports_half_open_connection]: This implementation does not know
* how to find out whether the socket supports half-open connections.
* You can simply set this boolean because of this. Defaults to [false].
* You can set it to [true] for TCP connections and for Unix-domain
* connections with stream semantics.
*
* [timeout]: If set to [(t, x)], a general timeout of [t] is set.
* When an operation has been started, and there is no I/O activity within
* [t] seconds, neither by the started operation nor by another operation,
* the connection times out. In this case, the operation returns the
* exception [x].
*)
val create_multiplex_controller_for_datagram_socket :
?close_inactive_descr:bool ->
?preclose:(unit -> unit) ->
?timeout:(float * exn) ->
Unix.file_descr -> Unixqueue.unix_event_system ->
datagram_multiplex_controller
(** Creates a multiplex controller for datagram sockets (e.g. UDP socket).
*
* Note that the file descriptor is not closed when the attached engines
* are terminated. One can call [inactivate] manually to do that.
*
* [close_inactive_descr]: Whether [inactivate] closes the descriptor.
* True by default.
*
* [preclose]: This function is called just before the descriptor is
* closed.
*
* [timeout]: If set to [(t, x)], a general timeout of [t] is set.
* When an operation has been started, and there is no I/O activity within
* [t] seconds, neither by the started operation nor by another operation,
* the connection times out. In this case, the operation returns the
* exception [x].
*)
val tls_multiplex_controller :
?resume:string ->
?on_handshake:(multiplex_controller -> unit) ->
role:[ `Server | `Client ] ->
peer_name:string option ->
(module Netsys_crypto_types.TLS_CONFIG) ->
multiplex_controller ->
multiplex_controller
(** Creates a new multiplex controller on top of an existing controller,
and configures the new controller for running the TLS protocol.
[resume]: The endpoint resumes an old session whose data are passed here.
This is only possible for client endpoints.
[on_handshake]: called back when the handshake is done
*)
val restore_tls_multiplex_controller :
?on_handshake:(multiplex_controller -> unit) ->
exn ->
(module Netsys_crypto_types.TLS_CONFIG) ->
multiplex_controller ->
multiplex_controller
(** Like [tls_multiplex_controller], but this function does not create a new
TLS endpoint. Instead the exn value is assumed to be a stashed old
endpoint.
*)
(*
val dtls_multiplex_controller :
?resume:string ->
?on_handshake:(multiplex_controller -> unit) ->
role:[ `Server | `Client ] ->
peer_name:string option ->
(module Netsys_crypto_types.TLS_CONFIG) ->
datagram_multiplex_controller ->
datagram_multiplex_controller
-- not yet, see comments in uq_multiplex.ml
*)