NaviServer Programmable Web Server

ns_connchan(n)

NaviServer Built-in Commands – 5.1.0


[ Main Table Of Contents | Table Of Contents | Keyword Index ]

Name

ns_connchan - Manage connection channels.

Table Of Contents

Synopsis

Description

The command ns_connchan allows one to detach the current channel from a connection thread and manage the connection outside the initiating connection thread. It allows one to write or read to the channel, to define callbacks and to list open connections and to close the connection. The read and write operations on this channel will use directly the driver infrastructure which was in use during the detach command.

The command allows e.g. to read from and to write to all network drivers (such as plain HTTP channels and from SSL/TLS connections). It can be used to implement e.g. WebSockets or asynchronous deliveries (e.g. h264 streams) including secure connections. Therefore, this command is more general than the approaches based on ns_conn channel using plain Tcl channels.

NaviServer maintains an internal table per server to keep track of the detached connection channels and to offer introspection to the state of the detached channels.

COMMANDS

ns_connchan debug channel ?level?

Sets or queries the debugging level of the current connection. When set to "1", debug messages are written to the system log file; setting it to "2" causes a separate debug file to be created. Currently, this command primarily affects send operations, although its functionality may be expanded in future releases.

ns_connchan detach

Detaches the current connection channel from the active connection thread and stores it under a fresh handle name in a per-virtual-server private table. The command returns the created handle as result.

When this command is called from a request handler, NaviServer has already received the full client request, including the request body.

This command is primarily intended for cases where longer-running request processing should be performed in the background without blocking a connection thread. Background processing can be done, for example, via ns_job.

It can also be used for spooling data to a client, where the duration of the request depends on the client (e.g., slow readers). When the primary concern is protection against slow-read attacks, the use of writer threads is typically more appropriate.

After this command has been issued in a connection thread, direct access to the original connection socket via connection-thread commands such as ns_write will fail. Further processing of the detached connection must use the returned handle and the ns_connchan subcommands.

ns_connchan close ?-server server? channel

Closes the named connection channel. The -server can be used for cleanup of stale handles.

ns_connchan exists channel

Returns 1 if the named connection channel exists, 0 otherwise.

ns_connchan list ?-server server?

Returns a list of the currently detached connection channels for the current or named server.

Every list entry contains

  • name of the channel

  • name of the thread

  • start time of the initiating request,

  • driver,

  • the IP address of the client,

  • sent bytes,

  • received bytes,

  • the client data as provided via [ns_conn clientdata],

  • the cmd name of the callback, or "" when no callback is registered,

  • the callback condition flags, or "" when no callback is registered.

When NaviServer is running in reverse proxy mode, the client IP address is taken from the value as provided by the reverse proxy server.

ns_connchan callback ?-timeout time? ?-receivetimeout time? ?-sendtimeout time? channel command when

Registers a Tcl callback for the named connection channel. -timeout is the poll timeout (the maximum time between two events), -receivetimeout is a timeout for incoming packets, -sendtimeout is the timeout for outgoing packets. When -sendtimeout has the value of 0, a read operation might return the empty string. A value larger than 0 might block the event processing for the specified time. All timeouts values are specified in the form secs?:microseconds?, or secs.fraction or as a number with a time unit.

The argument when consist of one or more characters of r, w, e, or x, specifying, when the callback should fire.

When the callback is fired, the specified Tcl commands will be called with an additional argument, which is an indicator for the reason of the call when. The value of when will be as follows:

  • r - the socket is readable

  • w - the socket is writable

  • e - the socket has an exceptional condition

  • x - the server is shutting down (not related to peer connection closure)

  • t - timeout received

When the callback exits, its return value determines, whether the callback should be canceled or not. The return value is interpreted as follows:

  • 0 - the callback is canceled, and the channel is deleted automatically (typically, when an error occurs)

  • 1 - the callback will be used as well for further events

  • 2 - the callback will be suspended. No further events will be fired, but the channel is not deleted.

When the callback returns a value greater than or equal to 1, the channel remains open. A return value of 1 keeps the callback registered for future events, while a return value of 2 suspends the callback without deleting the channel.

When the peer closes its side of the connection, the channel typically becomes readable and ns_connchan read returns the empty string. This condition can be used to detect end-of-file (EOF) or peer connection closure.

ns_connchan connect ?-cafile value? ?-capath value? ?-cert value? ?-key value? ?-hostname value? ?-insecure? ?-timeout time? ?-tls? ?--? host port

Opens a client connection channel to the specified host and port. The command is similar to ns_connchan open, but does less: While ns_connchan open sends an HTTP request to the server denoted by a URL, ns_connchan connect just connects to the server without sending any request data.

The option -tls will connection via TLS, otherwise the connection is via plain socket. -timeout specifies the timeout for establishing the connection (default 1 second). The timeout value time can be specified in the form secs?:microseconds?, or secs.fraction, or as a number with a time unit.

ns_connchan listen ?-driver value? ?-server server? ?-bind? ?--? address port script

Opens a listening socket. Invokes the script callback for each incoming connection. On success, this command returns a dict containing channel, port, sock and address.

When port is specified as 0, the operating system selects an ephemeral local port automatically. The selected port is returned in the result dictionary under the key port.

The callback script is invoked for each incoming connection and is passed the connection channel as argument.

The return value of the callback controls the lifetime of the connection. When the callback returns a true value, the connection remains open and can be managed via ns_connchan commands. When the callback returns a false value, the connection is closed immediately.

When the connection is kept open, the accept callback will typically register an event handler via ns_connchan callback to process incoming or outgoing data.

-bind

When specified, the listening socket is explicitly bound to the provided address and port, and remains open for accepting incoming connections.

When this option is omitted, the command performs only a bindability check on the specified address/port combination (i.e., it verifies that the address can be bound), but does not keep the bound socket. The actual listening socket is then created via the default mechanism, which may not be restricted to the exact interface specified by address.

This option is therefore required when the listening socket must be restricted to a specific local interface or IP address.

-driver value

Selects the NaviServer driver module (by module name) used as context for the accepted connection channel.

This option does not enable protocol-specific request processing. The accepted connection is exposed to the script as a raw socket-style connection channel, to be handled via commands such as ns_connchan read and ns_connchan write.

Currently, ns_connchan listen uses the built-in http driver class for connection setup, and -driver can only be used to select among loaded drivers of that class via their module name. This is mainly relevant when multiple such drivers are configured differently. It does not by itself enable TLS or HTTP request parsing.

If omitted, the first matching driver is used.

-server server

Specifies the server instance in which the listening socket is registered. The callback script will be executed in the context of this server.

If omitted, the current server (as returned by ns_server) is used.

 # Listen only on the loopback interface (localhost)
 set info [ns_connchan listen -bind 127.0.0.1 9000 {
   # handle incoming connection
 }]
 # Connections are accepted only from local clients
 # Without -bind: address is only validated, not enforced
 set info [ns_connchan listen 127.0.0.1 9000 {
   # handle incoming connection
 }]
 # The listening socket may accept connections on all interfaces
 # (depending on system defaults, e.g. 0.0.0.0)
 # Allocate an ephemeral port bound to a specific interface
 set info [ns_connchan listen -bind 192.168.1.10 0 {
   # handle incoming connection
 }]
 # The returned dict contains the kernel-assigned port
 set port [dict get $info port]
ns_connchan open ?-cafile value? ?-capath value? ?-cert value? ?-key value? ?-driver value? ?-headers setId? ?-hostname value? ?-insecure? ?-method value? ?-timeout time? ?-unix_socket value? ?-version value? ?--? url

Opens a client connection channel to the specified url. The URL can either be an HTTP or an HTTPS URL.

Note that the network driver for the used protocol specified in the url must be loaded. For details, see CONFIGURATION.

The options can be any of:

-headers setId

A ns_set of request header fields.

-hostname value

Required for connecting to a server with virtual hosting that provides multiple certificates via SNI (Server Name Indication).

-method method

Specifies the HTTP method, by default GET.

-timeout time

Specifies the timeout for establishing the connection, by default 1 second. The timeout value time is specified in the form secs?:microseconds?, or secs.fraction, or as a number with a time unit.

-unix_socket value

a unix socket to connect to.

-version value

specifies the HTTP version (default 1.0).

ns_connchan read ?-websocket? channel

Reads from the specified connection channel and returns the received data.

When the option -websocket is used, then the command expects WebSocket frames. In this mode, the result of the command is a Tcl dict containing the following elements:

fin status bit, frame state (incomplete or complete), unprocessed (received number of bytes in buffer not handled so far), fragments (number of bytes in the WebSocket fragments buffer), haveData (boolean value to express that unprocessed data might be sufficient for the next frame without an extra read operation.

In case the frame is finished (fin status bit is set), the dict contains as well the WebSocket opcode and the payload of the frame.

ns_connchan status ?-server server? channel

Returns status information from the current or named server. The command returns a dict containing the following elements:

driver (module name the driver), fragments (for WebSocket read operations: number of bytes in the WebSocket fragments buffer), framebuffer (for WebSocket read operations: number of bytes in the WebSocket frame buffer), peer (communication peer, the IP address of the other side of the channel), received (number of bytes received), sendbuffer number of bytes in the send buffer, sent (number of bytes sent), and start (time when the connection started). When a callback is defined for this channel, the dict contains as well the callback and the condition on which the callback will be fired.

ns_connchan wsencode ?-binary? ?-fin 0|1? ?-mask? ?-opcode continue|text|binary|close|ping|pong? ?--? message

Returns a binary websocket frame based on the provided information in the form of a Tcl byte array. When -mask is specified, the data will be masked with random information. For the opcode binary, the input is treated as binary as well. When -binary is used, the data will be treated as binary. This is e.g. necessary on multi-segment messages, where later segments have the opcode continue.

ns_connchan write channel message

Writes the specified message to the given connection channel. The command returns the number of bytes actually sent, which may be less than the length of the provided message.

In cases of partial write operations, any unsent data is automatically queued in the connection's send buffer. This queued data is merged with any new data during the subsequent write operations. To check the current status of the send buffer, use the ns_conn status command. If you need to drain the buffer completely, repeatedly call ns_conn write with an empty message until the buffer is empty.

COMMON OPTIONS

-cafile value

specifies for HTTPS requests a PEM file containing certificates to validate the peer server (unless the option -insecure is used). All certificates in this file are trusted. Typically, the file has the name ca-bundle.crt and contains the top-level certificates. When the specified filename is not on an absolute location, the file is assumed to be in the home directory of NaviServer. The default value is ca-bundle.crt, but can be altered in the CONFIGURATION file. For more details, see SSL_CTX_load_verify_locations from the OpenSSL documentation.

-capath value

specifies for HTTPS requests a directory containing trusted certificates to validate the peer server (unless the option -insecure is used). Each file in this directory must be in PEM format and must contain exactly one certificate. When the specified directory is not an absolute path, it is assumed to be in the home directory of NaviServer. The default value is certificates, but can be altered in the CONFIGURATION file. For more details, see SSL_CTX_load_verify_locations from the OpenSSL documentation.

-cert value

used for HTTPS connections to specify the client certificate chain. The value must be the name of a file containing the certificate chain in PEM format. The certificate chain must be ordered starting with the subject's certificate (client certificate), followed by intermediate CA certificates if applicable, and ending at the highest level (root) CA.

When no separate -key is specified, the private key is expected to be included in the -cert file (combined PEM format).

-key value

used for HTTPS connections to specify a separate private key for the client certificate. The value must be the name of a file containing a PEM-encoded private key.

When -key is specified, the private key is loaded from this file instead of the -cert file. It is an error to specify -key without -cert.

-hostname value

used for HTTPS URIs to specify the hostname for the server certificate. This option has to be used, when the host supports virtual hosting, is configured with multiple certificates and supports the SNI (Server Name Indication) extension of TLS.

-insecure

used for HTTPS URIs to specify that the server certificate should NOT be verified. By default, the identity of the peer server is checked for all HTTPS requests. If the verification process fails, the TLS/SSL handshake is terminated with an alert message containing the reason for the verification failure. The default for this parameter can be specified in the CONFIGURATION file.

EXAMPLES

The following example demonstrates how to detach an incoming HTTP request from the connection thread and process it asynchronously in the background. The request handler returns immediately after calling ns_connchan detach, while the actual work is performed later (e.g., via ns_job). This pattern avoids blocking connection threads for longer-running tasks and allows the application to send the HTTP response at a later time via the detached connection channel.

After detaching, the application is responsible for sending a complete HTTP response (including status line and headers) and for closing the connection explicitly.

 # Example: detach request and process it in the background
 
 # Log request details
 set identifier "[ns_conn request] [ns_set format [ns_conn headers]]"
 ns_log notice "REQUEST START $identifier"
 
 # Ensure a job queue exists
 if {"q1" ni [ns_job queues]} { ns_job create q1 }
 
 # Detach the connection channel from the connection thread
 set channel [ns_connchan detach]
 
 # Process the request asynchronously
 ns_job queue -detached q1 [subst {
     ns_log notice "start detached job"
 
     # Simulate a long-running task
     ns_sleep 2s
 
     # Send minimal HTTP response and close connection
     ns_connchan write $channel "HTTP/1.0 200 OK\r\n\r\n"
     ns_connchan close $channel
 
     ns_log notice "DONE detached job"
 }]
 
 ns_log notice "REQUEST DONE $identifier"

The log output below shows that the request handler completes before the background job starts and finishes, demonstrating that the connection thread is not blocked.

 REQUEST START ...
 REQUEST DONE ...
 start detached job
 ... (delay)
 DONE detached job

The next example shows a two-stage use of ns_connchan listen and ns_connchan callback. The callback provided to ns_connchan listen is invoked when a new connection is accepted and must return a true value to keep the connection open. The accepted channel is then typically passed to ns_connchan callback for event-driven processing. In such a callback, peer connection closure is commonly detected when the channel becomes readable and ns_connchan read returns the empty string.

 package require nx
 
 namespace eval connchanserver {
 
     #
     # Server class: manages the listening socket and accepts connections
     #
     nx::Class create Server {
 
         # Default settings:
         #    - bind to IPv6 loopback and ephemeral port.
         #    - The OS assigned port is in the logentry (listening on ...)
 
         :property {host ::1}
         :property {port 0}
 
         #
         # Start listening for incoming connections
         #
         :public method start-listen {} {
 
             # Create a listening socket via ns_connchan.
             # -bind ensures that the socket is bound exactly to :host/:port.
             # The callback is invoked for every new incoming connection.
             set d [ns_connchan listen -bind ${:host} ${:port} \
                        [list [self] accept-handler]]
 
             # On success, ns_connchan listen returns a dict with details
             if {[dict exists $d channel]} {
 
                 # Extract returned values (channel, port, address, sock)
                 dict with d {
                     ns_log notice "Listening on address $address port $port"
 
                     # Remember the listening channel (needed to close later)
                     set :listen $channel
                 }
             } else {
                 # Failure (e.g., port already in use or bind not possible)
                 ns_log warning "Failed to listen on host ${:host} port ${:port}"
             }
         }
 
         #
         # Callback invoked for every accepted connection
         #
         # channel: connection channel for the accepted client socket
         #
         :public method accept-handler {channel} {
 
             ns_log notice "incoming data on channel $channel"
 
             # Create a new Connection object to handle this client
             Connection new -channel $channel
 
             # Return 1 to accept the connection and keep it open
             # Returning 0 would close the connection immediately
             return 1
         }
     }
 
     #
     # Connection class: handles a single client connection
     #
     nx::Class create Connection {
 
         # Channel associated with this connection
         :property {channel}
 
         #
         # Constructor: called after object creation
         #
         :method init {} {
 
             ns_log notice "[current class] starting with channel ${:channel}"
 
             # Register a callback for this connection channel 
             ns_connchan callback ${:channel} \
                 [list [self] receive] rex
         }
 
         #
         # Callback invoked when data is available or connection state changes
         #
         # condition: one of r/e/x (readable, exceptional condition, server shutdown)
         #
         :public method receive {condition} {
 
             # Read available data from the channel
             # ns_connchan read returns raw bytes
             set bytes [encoding convertfrom utf-8 \
                            [ns_connchan read ${:channel}]]
 
             # Determine length of received data
             set rlen [string length $bytes]
 
             ns_log notice "[current class] received $rlen bytes on ${:channel} cond $condition\n$bytes"
 
             # Return value controls whether the callback remains active:
             #   0 -> close channel and stop callbacks
             #   1 -> keep channel open and continue receiving callbacks
             #   2 -> keep channel open but suspend the current callback
             #
             # Here: close when no data was received (e.g., EOF)
             if {$rlen == 0} {
                 #
                 # destroy the Connection object
                 #
                 :destroy
                 return 0
             } else {
                 return 1
             }
         }
     }
 
     #
     # Create a server instance bound to IPv4 loopback
     #
     Server create s1 -host 127.0.0.1 -port 9000
 }
 
 #
 # Register startup hook to begin listening when NaviServer starts
 #
 ns_atstartup {
 
     # Start listening on configured host/port
     connchanserver::s1 start-listen
 }

CONFIGURATION

The ns_connchan command uses the network driver configuration of NaviServer. Therefore, the used network driver for the protocol must by loaded via the NaviServer configuration file. When e.g. an outgoing HTTPS request should be made, but the site administrator does not want to listen on an HTTPS port, specify in the configuration of HTTPS the port with value 0.

The default values for the peer server validation parameters -cafile, -capath, and -insecure can be configured in the configuration file as documented in ns_http. The configuration of the client certificate management is as well shared with ns_http. For configuration details, see the section HTTP client security configuration in the manual.

See Also

ns_chan, ns_conn, ns_http, ns_sockcallback, ns_time, ns_write, nssock, nsssl

Keywords

CAfile, CApath, channels, driver, network driver, reverse proxy, server built-in, socket, websocket