nhttp_sock (nhttp_lib v1.0.0)

View Source

Socket abstraction layer for nhttp.

Provides a unified interface for TCP and SSL sockets. All socket operations are tagged with transport type for pattern matching.

Socket Types

Sockets are represented as {tcp, gen_tcp:socket()} or {ssl, ssl:socket()}. This allows unified handling while preserving transport information.

ALPN Negotiation

For TLS connections, use negotiated_protocol/1 to determine which application protocol was negotiated (e.g., <<"h2">> or <<"http/1.1">>).

Summary

Functions

Accept a connection on a listening socket. A TCP listener returns {tcp, _} (ready for I/O); an SSL listener returns {ssl_pending, _} which requires handshake/3 before any I/O. The outer tag always reflects the actual transport state, so dispatch on it is safe everywhere.

Build SSL options for client connections.

Build SSL options from opts map. Accepts any map containing SSL-related keys.

Close a socket. Pre-handshake and listen sockets close at the TCP layer.

Connect to a remote host. Returns a connected socket or an error.

Connect to a remote host with explicit timeout.

Transfer socket ownership to another process.

Complete the TLS handshake on a freshly accepted socket. The {ssl_pending, _} tag from accept/2 is upgraded to {ssl, _} on success. Calling handshake/3 on a pure-TCP {tcp, _} or an already-upgraded {ssl, _} socket is a no-op.

Create a listening socket with the given options.

Get the negotiated ALPN protocol. Returns {error, no_alpn} for non-TLS or pre-handshake sockets.

Get the remote address and port.

Receive data from a socket. {ssl_pending, _} is accepted: pre-handshake reads operate on the underlying TCP socket. This is intended for byte-accurate pre-TLS protocols (e.g. the PROXY protocol) where reading past the prefix into the TLS ClientHello would break the handshake.

Send data on a socket. Not valid on {ssl_pending, _}. Call handshake/3 first.

Set socket options.

Get the local address and port.

Get the transport type of a socket. ssl_pending reports as ssl.

Types

connect_opts()

-type connect_opts() ::
          #{transport => transport(),
            nodelay => boolean(),
            send_timeout => timeout(),
            buffer => pos_integer(),
            certfile => file:filename(),
            keyfile => file:filename(),
            cacertfile => file:filename(),
            cacerts => [public_key:der_encoded()],
            alpn_advertised_protocols => [binary()],
            verify => verify_none | verify_peer,
            server_name_indication => inet:hostname() | disable,
            tls_versions => ['tlsv1.2' | 'tlsv1.3'],
            wildcard_hostname => boolean()}.

listen_opts()

-type listen_opts() ::
          #{port := inet:port_number(),
            transport => transport(),
            backlog => pos_integer(),
            nodelay => boolean(),
            send_timeout => timeout(),
            buffer => pos_integer(),
            certfile => file:filename(),
            keyfile => file:filename(),
            cacertfile => file:filename(),
            alpn_preferred_protocols => [binary()],
            verify => verify_none | verify_peer,
            tls_versions => ['tlsv1.2' | 'tlsv1.3']}.

socket_error()

-type socket_error() :: closed | timeout | inet:posix() | {tls_error, ssl:error_alert() | ssl:reason()}.

t()

-type t() ::
          {tcp, gen_tcp:socket()} |
          {ssl, ssl:sslsocket()} |
          {ssl_listen, gen_tcp:socket()} |
          {ssl_pending, gen_tcp:socket()}.

transport()

-type transport() :: tcp | ssl.

Functions

accept/2

-spec accept(t(), timeout()) -> {ok, t()} | {error, socket_error()}.

Accept a connection on a listening socket. A TCP listener returns {tcp, _} (ready for I/O); an SSL listener returns {ssl_pending, _} which requires handshake/3 before any I/O. The outer tag always reflects the actual transport state, so dispatch on it is safe everywhere.

build_client_ssl_opts(Opts)

-spec build_client_ssl_opts(connect_opts()) -> [ssl:tls_client_option()].

Build SSL options for client connections.

build_ssl_opts(Opts)

-spec build_ssl_opts(map()) -> [ssl:tls_server_option()].

Build SSL options from opts map. Accepts any map containing SSL-related keys.

close/1

-spec close(t()) -> ok.

Close a socket. Pre-handshake and listen sockets close at the TCP layer.

connect(Host, Port, Opts)

-spec connect(Host, inet:port_number(), connect_opts()) -> {ok, t()} | {error, socket_error()}
                 when Host :: binary() | string() | inet:ip_address().

Connect to a remote host. Returns a connected socket or an error.

connect(Host, Port, Opts, Timeout)

-spec connect(Host, inet:port_number(), connect_opts(), timeout()) ->
                 {ok, t()} | {error, socket_error()}
                 when Host :: binary() | string() | inet:ip_address().

Connect to a remote host with explicit timeout.

controlling_process/2

-spec controlling_process(t(), pid()) -> ok | {error, socket_error()}.

Transfer socket ownership to another process.

handshake/3

-spec handshake(t(), timeout(), SslOpts) -> {ok, t()} | {error, socket_error()}
                   when SslOpts :: [ssl:tls_server_option()].

Complete the TLS handshake on a freshly accepted socket. The {ssl_pending, _} tag from accept/2 is upgraded to {ssl, _} on success. Calling handshake/3 on a pure-TCP {tcp, _} or an already-upgraded {ssl, _} socket is a no-op.

listen(Opts)

-spec listen(listen_opts()) -> {ok, t()} | {error, socket_error()}.

Create a listening socket with the given options.

negotiated_protocol/1

-spec negotiated_protocol(t()) -> {ok, binary()} | {error, no_alpn}.

Get the negotiated ALPN protocol. Returns {error, no_alpn} for non-TLS or pre-handshake sockets.

peername/1

-spec peername(t()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, socket_error()}.

Get the remote address and port.

recv/3

-spec recv(t(), non_neg_integer(), timeout()) -> {ok, binary()} | {error, socket_error()}.

Receive data from a socket. {ssl_pending, _} is accepted: pre-handshake reads operate on the underlying TCP socket. This is intended for byte-accurate pre-TLS protocols (e.g. the PROXY protocol) where reading past the prefix into the TLS ClientHello would break the handshake.

send/2

-spec send(t(), iodata()) -> ok | {error, socket_error()}.

Send data on a socket. Not valid on {ssl_pending, _}. Call handshake/3 first.

setopts/2

-spec setopts(t(), [gen_tcp:option() | ssl:tls_option()]) -> ok | {error, socket_error()}.

Set socket options.

sockname/1

-spec sockname(t()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, socket_error()}.

Get the local address and port.

transport/1

-spec transport(t()) -> transport().

Get the transport type of a socket. ssl_pending reports as ssl.