# Module Netchannels

    module Netchannels: sig .. end

Object-oriented I/O: Basic types and classes

Contents

- Types
- Input channels
- Output channels
- Delegation classes
- Lifting channels
- Channels over descriptors
- Transactional channels
- Pipes and Filters
  - Notes, Examples

The tutorial has been moved to `Netchannels_tut`.

## Types

There are three levels of class types for channels:

- `rec_in_channel` and `rec_out_channel`: Primitive, but standardized level
- `raw_in_channel` and `raw_out_channel`: Unix level
- `in_obj_channel` and `out_obj_channel`: Application level

The "rec" level has been recently introduced to improve interoperability with other libraries (e.g. camomile). The idea is to standardize the real core methods of I/O, so they have the same meaning in all libraries. Read "Basic I/O class types" for more.

The "raw" level represents the level of Unix file descriptors.

The application level is what should be used in programs. In addition to the "raw" level one can find a number of convenience methods, e.g. `input_line` to read a line from the channel. The downside is that these methods usually work only for blocking I/O.

One can lower the level by coercion, e.g. to turn an `in_obj_channel` into a `rec_in_channel`, apply the function

`(fun ch -> (ch : in_obj_channel :> rec_in_channel))`

To higher the level, apply `lift_in` or `lift_out`, defined below.

Interface changes: Since ocamlnet-0.98, the semantics of the methods `input` and `output` has slightly changed. When the end of the channel is reached, `input` raises now `End_of_file`. In previous releases of ocamlnet, the value 0 was returned. When the channel cannot process data, but is in non-blocking mode, both methods now return the value 0. In previous releases of ocamlnet, the behaviour was not defined.

Ocamlnet-3.0 changed the behavior of `close_out`. Errors are no longer reported - instead, the exception is logged to `Netlog`. For a stricter error handling, it is suggested to call `flush` first. Also, `close_in` and `close_out` no longer raise `Closed_channel` when the channel is already closed. Read more about this in the section How to close channels in case of errors.

    exception Closed_channel

Raised when channel operations are called when the channel is closed

    exception Buffer_underrun

Raised by input methods if the internal buffer of the channel is too empty to read even one byte of data. This exception is only used by certain implementations of channel classes.

    exception Command_failure of Unix.process_status

Raised by `close_in` or `close_out` if the channel is connected with another process, and the execution of that process fails.

    class type rec_in_channel = object .. end

Recommended input class type for library interoperability.

    class type raw_in_channel = object .. end

Basic Unix-level class type for input channels as used by ocamlnet.

    class type rec_out_channel = object .. end

Recommended output class type for library interoperability.

    class type raw_out_channel = object .. end

Basic Unix-level class type for output channels as used by ocamlnet.

    class type raw_io_channel = object .. end

A channel supporting both input and output.

    class type compl_in_channel = object .. end

Further methods usually supported by ocamlnet channel implementations.

    class type in_obj_channel = object .. end

The application-level input channel supports raw and complemented methods

    class type compl_out_channel = object .. end

Further methods usually supported by ocamlnet channel implementations.

    class type out_obj_channel = object .. end

The application-level output channel supports raw and complemented methods

    class type io_obj_channel = object .. end

A channel supporting both input and output.

    class type trans_out_obj_channel = object .. end

A transactional output channel has a buffer for uncommitted data.

## Input channels

    class input_channel : ?onclose:unit -> unit -> Stdlib.in_channel -> in_obj_channel

Creates an input channel from an `in_channel`, which must be open.

    val input_channel : ?onclose:(unit -> unit) -> Stdlib.in_channel -> in_obj_channel

Same as function

    class input_command : string -> in_obj_channel

Runs the command with `/bin/sh`, and reads the data the command prints to stdout.

    val input_command : string -> in_obj_channel

Same as function

    class input_string : ?pos:int -> ?len:int -> string -> in_obj_channel

Creates an input channel from a (constant) string.

    val input_string : ?pos:int -> ?len:int -> string -> in_obj_channel

Same as function

    class input_bytes : ?pos:int -> ?len:int -> Stdlib.Bytes.t -> in_obj_channel

Same for constant bytes

    val input_bytes : ?pos:int -> ?len:int -> Stdlib.Bytes.t -> in_obj_channel

Same as function

    class input_memory : ?pos:int -> ?len:int -> Netsys_types.memory -> in_obj_channel

Same for constant memory

    val input_memory : ?pos:int -> ?len:int -> Netsys_types.memory -> in_obj_channel

Same as function

    val input_tstring : ?pos:int -> ?len:int -> Netsys_types.tstring -> in_obj_channel

Same for tagged strings (only as function)

    val create_input_netbuffer : ?keep_data:bool -> Netbuffer.t -> in_obj_channel * (unit -> unit)

Creates an input channel and a shutdown function for a netbuffer. This is a destructive implementation: Every time data is read, the octets are taken from the beginning of the netbuffer, and they are deleted from the netbuffer (recall that a netbuffer works like a queue of characters).

Conversely, the user of this class may add new data to the netbuffer at any time. When the shutdown function is called, the EOF condition is recorded, and no further data must be added.

If the netbuffer becomes empty, the input methods raise `Buffer_underrun` when the EOF condition has not yet been set, and they raise `End_of_file` when the EOF condition has been recorded.

`keep_data`: do not delete read data from the buffer

    val lexbuf_of_in_obj_channel : in_obj_channel -> Stdlib.Lexing.lexbuf

Creates a lexical buffer from an input channel. The input channel is not closed when the end is reached

This function does not work for non-blocking channels.

    val string_of_in_obj_channel : in_obj_channel -> string

Reads from the input channel until EOF and returns the characters as string. The input channel is not closed.

This function does not work for non-blocking channels.

    val bytes_of_in_obj_channel : in_obj_channel -> Stdlib.Bytes.t

Same for bytes

    val lines_of_in_obj_channel : in_obj_channel -> string list

Reads from the input channel until EOF and returns the lines as string list. The input channel is not closed.

This function does not work for non-blocking channels.

    val with_in_obj_channel : (#in_obj_channel as 'a) -> ('a -> 'b) -> 'b

`with_in_obj_channel ch f`: Computes `f ch` and closes `ch`. If an exception happens, the channel is closed, too.

## Output channels

    class output_channel : ?onclose:unit -> unit -> Stdlib.out_channel -> out_obj_channel

Creates an output channel writing into an `out_channel`.

    class output_command : ?onclose:unit -> unit -> string -> out_obj_channel

Runs the command with `/bin/sh`, and data written to the channel is piped to stdin of the command.

    class output_buffer : ?onclose:unit -> unit -> Stdlib.Buffer.t -> out_obj_channel

This output channel writes the data into the passed buffer.

    class output_netbuffer : ?onclose:unit -> unit -> Netbuffer.t -> out_obj_channel

This output channel writes the data into the passed netbuffer.

    class output_null : ?onclose:unit -> unit -> unit -> out_obj_channel

This output channel discards all written data.

    val with_out_obj_channel : (#out_obj_channel as 'a) -> ('a -> 'b) -> 'b

`with_out_obj_channel ch f`: Computes `f ch` and closes `ch`. If an exception happens, the channel is closed, too.

## Delegation classes

Delegation classes just forward method calls to an parameter object, i.e. when method `m` of the delegation class is called, the definition of `m` is just to call the method with the same name `m` of the parameter object. This is very useful in order to redefine methods individually.

For example, to redefine the method `pos_in` of an `in_obj_channel`, use

     class my_channel = object(self)
       inherit in_obj_channel_delegation ...
       method pos_in = ...
     end

As a special feature, the following delegation classes can suppress the delegation of `close_in` or `close_out`, whatever applies. Just pass `close:false` to get this effect, e.g.

     class input_channel_don't_close c =
       in_obj_channel_delegation ~close:false (new input_channel c)

This class does not close `c : in_channel` when the `close_in` method is called.

    class rec_in_channel_delegation : ?close:bool -> rec_in_channel -> rec_in_channel

    class raw_in_channel_delegation : ?close:bool -> raw_in_channel -> raw_in_channel

    class in_obj_channel_delegation : ?close:bool -> in_obj_channel -> in_obj_channel

    class rec_out_channel_delegation : ?close:bool -> rec_out_channel -> rec_out_channel

    class raw_out_channel_delegation : ?close:bool -> raw_out_channel -> raw_out_channel

    class out_obj_channel_delegation : ?close:bool -> out_obj_channel -> out_obj_channel

## Lifting channels

The following classes and functions add missing methods to reach a higher level in the hierarchy of channel class types. For most uses, the `lift_in` and `lift_out` functions work best.

    val lift_in : ?eol:string list ->
           ?buffered:bool ->
           ?buffer_size:int ->
           ?pass_through:int ->
           [ `Raw of raw_in_channel | `Rec of rec_in_channel ] ->
           in_obj_channel

Turns a `rec_in_channel` or `raw_in_channel`, depending on the passed variant, into a full `in_obj_channel` object. (This is a convenience function, you can also use the classes below directly.) If you want to define a class for the lifted object, use

     class lifted_ch ... =
       in_obj_channel_delegation (lift_in ...)

`eol` : The accepted end-of-line delimiters. The method `input_line` recognizes any of the passed strings as EOL delimiters. When more than one delimiter matches, the longest is taken. Defaults to `["\n"]` . The default cannot be changed when `buffered=false` (would raise `Invalid_argument`). The delimiter strings must neither be empty, nor longer than `buffer_size`.
`buffered` : Whether a buffer is added, by default true
`buffer_size` : The size of the buffer, if any, by default 4096
`pass_through` : If the read request has at least this size, and the buffer is currently empty, the buffer will be bypassed. Defaults to `max_int`, i.e. it is off.

    val lift_out : ?buffered:bool ->
           ?buffer_size:int ->
           ?pass_through:int ->
           [ `Raw of raw_out_channel | `Rec of rec_out_channel ] ->
           out_obj_channel

Turns a `rec_out_channel` or `raw_out_channel`, depending on the passed variant, into a full `out_obj_channel` object. (This is a convenience function, you can also use the classes below directly.) If you want to define a class for the lifted object, use

     class lifted_ch ... =
       out_obj_channel_delegation (lift_out ...)

`buffered` : Whether a buffer is added, by default true
`buffer_size` : The size of the buffer, if any, by default 4096
`pass_through` : If the write request has at least this size, and the buffer is currently empty, the buffer will be bypassed. Defaults to `max_int`, i.e. it is off.

    class virtual augment_raw_in_channel : object .. end

This class implements the methods from `compl_in_channel` by calling the methods of `raw_in_channel`.

    class lift_rec_in_channel : ?start_pos_in:int -> rec_in_channel -> in_obj_channel

This class implements `pos_in` and the methods from `compl_in_channel` by calling the methods of `rec_in_channel`.

    class virtual augment_raw_out_channel : object .. end

This class implements the methods from `compl_out_channel` by calling the methods of `raw_out_channel`.

    class lift_raw_out_channel : raw_out_channel -> out_obj_channel

This class implements the methods from `compl_out_channel` by calling the methods of `raw_out_channel`.

    class lift_rec_out_channel : ?start_pos_out:int -> rec_out_channel -> out_obj_channel

This class implements `pos_out` and the methods from `compl_out_channel` by calling the methods of `rec_out_channel`.

    type input_result = [ `Data of int | `Separator of string ]

This type is for the method `enhanced_input` of `enhanced_raw_in_channel`.

- ``Data n` means that `n` bytes have been copied to the target string
- ``Separator s` means that no bytes have been copied, but that an end-of-line separator `s` has been found

    class type enhanced_raw_in_channel = object .. end

Defines private methods reading text line by line

    class buffered_raw_in_channel : ?eol:string list -> ?buffer_size:int -> ?pass_through:int -> raw_in_channel -> enhanced_raw_in_channel

This class adds a buffer to the underlying `raw_in_channel`.

    class buffered_raw_out_channel : ?buffer_size:int -> ?pass_through:int -> raw_out_channel -> raw_out_channel

This class adds a buffer to the underlying `raw_out_channel`.

## Channels over descriptors

    class input_descr : ?blocking:bool -> ?start_pos_in:int -> ?fd_style:Netsys.fd_style -> Unix.file_descr -> raw_in_channel

Creates a `raw_in_channel` for the passed file descriptor, which must be open for reading.

    class output_descr : ?blocking:bool -> ?start_pos_out:int -> ?fd_style:Netsys.fd_style -> Unix.file_descr -> raw_out_channel

Creates a `raw_out_channel` for the passed file descriptor, which must be open for writing.

    class socket_descr : ?blocking:bool -> ?start_pos_in:int -> ?start_pos_out:int -> ?fd_style:Netsys.fd_style -> Unix.file_descr -> raw_io_channel

Creates a `raw_io_channel` for the passed socket descriptor, which must be open for reading and writing, and not yet shut down in either direction.

## Transactional channels

    type close_mode = [ `Commit | `Rollback ]

Whether a `close_out` implies a commit or rollback operation

    class buffered_trans_channel : ?close_mode:close_mode -> out_obj_channel -> trans_out_obj_channel

A transactional output channel with a transaction buffer implemented in memory

    val make_temporary_file : ?mode:int ->
           ?limit:int ->
           ?tmp_directory:string ->
           ?tmp_prefix:string -> unit -> string * Stdlib.in_channel * Stdlib.out_channel

Creates a temporary file in the directory `tmp_directory` with a name prefix `tmp_prefix` and a unique suffix. The function returns the triple (name, inch, outch) containing the file `name`, the file opened as in_channel `inch` and as out_channel `outch`.

`mode` : The creation mask of the file; defaults to 0o600, i.e. the file is private for the current user
`limit` : Limits the number of trials to find the unique suffix. Defaults to 1000.
`tmp_directory` : Defaults to `Netsys_tmp.tmp_directory()`
`tmp_prefix` : By default `"netstring"`. This needs not to be unique, but just descriptive.

    class tempfile_trans_channel : ?close_mode:close_mode -> ?tmp_directory:string -> ?tmp_prefix:string -> out_obj_channel -> trans_out_obj_channel

A transactional output channel with a transaction buffer implemented as temporary file

## Pipes and Filters

Note that this has nothing to do with "pipes" on the Unix level. It is, however, the same idea: Connecting two I/O resources with an intermediate buffer.

    class pipe : ?conv:Netbuffer.t -> bool -> Netbuffer.t -> unit -> ?buffer_size:int -> unit -> io_obj_channel

A `pipe` has two internal buffers (realized by Netbuffer).

    class output_filter : io_obj_channel -> out_obj_channel -> out_obj_channel

An `output_filter` filters the data written to it through the `io_obj_channel` (usually a `pipe`), and writes the filtered data to the passed `out_obj_channel`.

    class input_filter : in_obj_channel -> io_obj_channel -> in_obj_channel

An `input_filter` filters the data read from it through the `io_obj_channel` (usually a `pipe` after the data have been retrieved from the passed `in_obj_channel`.

### Notes, Examples

If you have the choice, prefer `output_filter` over `input_filter`. The latter is slower.

The primary application of filters is to encode or decode a channel on the fly. For example, the following lines write a BASE64-encoded file:

    let ch = new output_channel (open_out "file.b64") in
     let encoder = new Netencoding.Base64.encoding_pipe ~linelength:76 () in
     let ch' = new output_filter encoder ch in
     ... (* write to ch' *)
     ch' # close_out();
     ch  # close_out();  (* you must close both channels! *)

All bytes written to `ch'` are BASE64-encoded and the encoded bytes are written to `ch`.

There are also pipes to decode BASE64, and to encode and decode the "Quoted printable" format. Encoding and decoding work even if the data is delivered in disadvantageous chunks, because the data is "re-chunked" if needed. For example, BASE64 would require that data arrive in multiples of three bytes, and to cope with that, the BASE64 pipe only processes the prefix of the input buffer that is a multiple of three, and defers the encoding of the extra bytes till the next opportunity.