NAME

Feersum - A PSGI engine for Perl based on EV/libev

SYNOPSIS

use Feersum;
my $ngn = Feersum->endjinn; # singleton
$ngn->use_socket($io_socket);

# register a PSGI handler
$ngn->psgi_request_handler(sub {
    my $env = shift;
    return [200,
        ['Content-Type'=>'text/plain'],
        ["You win one cryptosphere!\n"]];
});

# register a Feersum handler:
$ngn->request_handler(sub {
    my $req = shift;
    my $t; $t = EV::timer 2, 0, sub {
        $req->send_response(
            200,
            ['Content-Type' => 'text/plain'],
            \"You win one cryptosphere!\n"
        );
        undef $t;
    };
});

DESCRIPTION

Feersum is an HTTP server built on EV. It fully supports the PSGI 1.1 spec including the psgi.streaming interface and is compatible with Plack. It also has its own "native" interface which is similar in a lot of ways to PSGI, but is not compatible with PSGI or PSGI middleware.

Feersum uses a single-threaded, event-based programming architecture to scale and can handle many concurrent connections efficiently in both CPU and RAM. It skips doing a lot of sanity checking with the assumption that a "front-end" HTTP/HTTPS server is placed between it and the Internet.

How It Works

All of the request-parsing and I/O marshalling is done using C or XS code. HTTP parsing is done by picohttpparser, which is the core of HTTP::Parser::XS. The network I/O is done via the libev library. This is made possible by EV::MakeMaker, which allows extension writers to link against the same libev that EV is using. This means that one can write an evented app using EV or AnyEvent from Perl that completely co-operates with the server's event loop.

Since the Perl "app" (handler) is executed in the same thread as the event loop, one need to be careful to not block this thread. Standard techniques include using AnyEvent or EV idle and timer watchers, using Coro to multitask, and using sub-processes to do heavy lifting (e.g. AnyEvent::Worker and AnyEvent::DBI).

Feersum also attempts to do as little copying of data as possible. Feersum uses the low-level writev system call to avoid having to copy data into a buffer. For response data, references to scalars are kept in order to avoid copying the string values (once the data is written to the socket, the reference is dropped and the data is garbage collected).

For even faster results, Feersum can support very simple pre-forking (See feersum, Feersum::Runner or Plack::Handler::Feersum for details).

INTERFACE

There are two handler interfaces for Feersum: The PSGI handler interface and the "Feersum-native" handler interface. The PSGI handler interface is fully PSGI 1.1 compatible, supporting psgi.streaming, psgix.input.buffered, and psgix.io. The Feersum-native handler interface is "inspired by" PSGI, but does some things differently for speed.

Feersum will use "Transfer-Encoding: chunked" for HTTP/1.1 clients and "Connection: close" streaming as a fallback. Technically "Connection: close" streaming isn't part of the HTTP/1.0 or 1.1 spec, but many browsers and agents support it anyway.

POST/PUT request bodies (including chunked transfer-encoding) are fully buffered before the request callback fires, so read() on psgi.input will never block. (The psgix.input.buffered env var is set to reflect this).

PSGI interface

Feersum fully supports the PSGI 1.1 spec including psgi.streaming.

See also Plack::Handler::Feersum, which provides a way to use Feersum with plackup and Plack::Runner.

Call psgi_request_handler($app) to register $app as a PSGI handler.

my $app = do $filename;
Feersum->endjinn->psgi_request_handler($app);

The env hash passed in will always have the following keys in addition to dynamic ones:

psgi.version      => [1,1],
psgi.nonblocking  => 1,        # PL_sv_yes
psgi.multithread  => !1,       # PL_sv_no (false)
psgi.multiprocess => !1,       # PL_sv_no (note: stays false even under pre_fork)
psgi.run_once     => !1,       # PL_sv_no (false)
psgi.streaming    => 1,
psgi.errors       => \*STDERR,
SCRIPT_NAME       => "",

Feersum adds these extensions (see below for info)

psgix.input.buffered   => 1,
psgix.output.buffered  => 1,
psgix.body.scalar_refs => 1,
psgix.output.guard     => 1,
psgix.io               => \$magical_io_socket,

Note that SCRIPT_NAME is always blank (but defined). PATH_INFO will contain the path part of the requested URI.

psgi.input always contains a valid handle. For requests without a body (e.g. GET), reading from it returns 0 (empty stream).

my $r = delete $env->{'psgi.input'};
$r->read($body, $env->{CONTENT_LENGTH});
# optional: choose to stop receiving further input, discard buffers:
$r->close();

The psgi.streaming interface is fully supported, including the writer-object poll_cb callback feature. Feersum calls the poll_cb callback after all data has been flushed out and the socket is write-ready. The data is buffered until the callback returns at which point it will be immediately flushed to the socket.

my $app = sub {
    my $env = shift;
    return sub {
        my $respond = shift;
        my $w = $respond->([
            200, ['Content-Type' => 'application/json']
        ]);
        my $n = 0;
        $w->poll_cb(sub {
            $_[0]->write(get_next_chunk());
            # will also unset the poll_cb:
            $_[0]->close if ($n++ >= 100);
        });
    };
};

Note that $w->close() will be called when the last reference to the writer is dropped.

PSGI extensions

psgix.body.scalar_refs

Scalar refs in the response body are supported, and is indicated as an via the psgix.body.scalar_refs env variable. Passing by reference is significantly faster than copying a value onto the return stack or into an array. It's also very useful when broadcasting a message to many connected clients. This is a Feersum-native feature exposed to PSGI apps; very few other PSGI handlers will support this.

psgix.output.buffered

Calls to $w->write() will never block. This behaviour is indicated by psgix.output.buffered in the PSGI env hash.

psgix.input.buffered

psgix.input.buffered is defined as part of PSGI 1.1. It means that calls to read on the input handle will never block because the complete input has been buffered in some way.

Feersum currently buffers the entire input in memory calling the callback.

Feersum also supports a poll_cb() method on the reader handle for incremental (streaming) input. When poll_cb is active, Feersum delivers body data to the callback as it arrives. psgix.input.buffered remains 1 because data is still buffered in memory before delivery.

psgix.output.guard

The streaming responder has a response_guard() method that can be used to attach a guard to the request. When the request completes (all data has been written to the socket and the socket has been closed) the guard will trigger. This is an alternate means to doing a "write completion" callback via poll_cb() that should be more efficient. An analogy is the "on_drain" handler in AnyEvent::Handle.

A "guard" in this context is some object that will do something interesting in its DESTROY/DEMOLISH method. For example, Guard.

psgix.io

The raw socket extension psgix.io is provided in order to support Web::Hippie and websockets. psgix.io is defined as part of PSGI 1.1. To obtain the IO::Socket corresponding to this connection, read this environment variable.

For plain (non-TLS) connections the returned IO::Socket::INET wraps the raw TCP file descriptor, which will have O_NONBLOCK, TCP_NODELAY, SO_OOBINLINE enabled and SO_LINGER disabled. For TLS and HTTP/2 connections, psgix.io returns a Unix socketpair that relays data through the TLS/H2 layer transparently.

PSGI apps MUST use a psgi.streaming response so that Feersum doesn't try to flush and close the connection. For HTTP/1 connections, the "respond" parameter to the streaming callback MUST NOT be called for the same reason. For HTTP/2 Extended CONNECT, calling the responder with a 200 response is the correct way to accept the tunnel.

my $env = shift;
return sub {
    my $fh = $env->{'psgix.io'};
    syswrite $fh, "HTTP/1.1 101 Switching Protocols\r\n"
                 . "Upgrade: myproto\r\nConnection: Upgrade\r\n\r\n";
    # ... bidirectional I/O on $fh ...
};

HTTP/2 note: For H2 Extended CONNECT tunnels, Feersum automatically sends 200 HEADERS to accept the tunnel and silently swallows the HTTP/1.1 101 response written by the app. This means the same handler code works for both H1 and H2 without branching. See "HTTP/2 support" for details.

psgix.h2.trailers

An array-ref of [name, value] pairs containing HTTP/2 trailer headers received with the request. Only present for HTTP/2 requests that included trailers. Absent for HTTP/1.x requests and H2 requests without trailers.

psgix.h2.extended_connect

Set to 1 on HTTP/2 Extended CONNECT streams (RFC 8441). Absent for all other request types including plain HTTP/2 requests.

psgix.h2.protocol

Present on HTTP/2 Extended CONNECT streams. Contains the value of the H2 :protocol pseudo-header (e.g. "websocket").

psgix.proxy_tlvs

Present when the connection arrived via PROXY protocol v2 with TLV extensions. A hash ref mapping TLV type numbers to their raw values. See "proxy_tlvs" in Feersum::Connection for details.

The Feersum-native interface

The Feersum-native interface is inspired by PSGI, but is inherently incompatible with it. Apps written against this API will not work as a PSGI app.

This interface may have removals and is not stable until Feersum reaches version 1.0, at which point the interface API will become stable and will only change for bug fixes or new additions. The "stable" and will retain backwards compatibility until at least the next major release.

The main entry point is a sub-ref passed to request_handler. This sub is passed a reference to an object that represents an HTTP connection. Currently the request_handler is called during the "check" and "idle" phases of the EV event loop. The handler is always called after request headers have been read. Currently, the handler will only be called after a full request entity has been received for POST/PUT/etc.

The simplest way to send a response is to use send_response:

my $req = shift;
$req->send_response(200, \@headers, ["body ", \"parts"]);

Or, if the app has everything packed into a single scalar already, just pass it in by reference.

my $req = shift;
$req->send_response(200, \@headers, \"whole body");

Both of the above will generate Content-Length header (replacing any that were pre-defined in @headers).

An environment hash is easy to obtain, but is a method call instead of a parameter to the callback. (In PSGI, there is no $req object; the env hash is the first parameter to the callback). The hash contains the same items as it would for a PSGI handler (see above for those).

my $req = shift;
my $env = $req->env();

To read input from a POST/PUT, use the psgi.input item of the env hash.

if ($env->{REQUEST_METHOD} eq 'POST') {
    my $body = '';
    my $r = delete $env->{'psgi.input'};
    $r->read($body, $env->{CONTENT_LENGTH});
    # optional:
    $r->close();
}

Starting a response in stream mode enables the write() method (which really acts more like a buffered 'print'). Calls to write() will never block.

my $req = shift;
my $w = $req->start_streaming(200, \@headers);
$w->write(\"this is a reference to some shared chunk\n");
$w->write("regular scalars are OK too\n");
$w->close(); # close off the stream

The writer object supports poll_cb as specified in PSGI. Feersum will call the callback only when all data has been flushed out at the socket level. Use close() or unset the handler ($w->poll_cb(undef)) to stop the callback from getting called.

my $req = shift;
my $w = $req->start_streaming(
    "200 OK", ['Content-Type' => 'application/json']);
my $n = 0;
$w->poll_cb(sub {
    # $_[0] is a copy of $w so a closure doesn't need to be made
    $_[0]->write(get_next_chunk());
    $_[0]->close if ($n++ >= 100);
});

Note that $w->close() will be called when the last reference to the writer is dropped.

METHODS

These are methods on the Feersum server object.

new()
endjinn()

Returns the Feersum singleton. Takes no parameters.

new_instance()

Creates a new independent Feersum server instance. Unlike new(), each call returns a separate server object with its own listeners, configuration, and request handler. Use this when you need multiple independent servers in the same process.

my $http  = Feersum->new_instance();
my $https = Feersum->new_instance();
use_socket($sock)

Use the file-descriptor attached to a listen-socket to accept connections.

Note: Pre-encrypted sockets (e.g. IO::Socket::SSL) are not supported. Feersum operates on the raw file descriptor and will ignore any userspace encryption layer. To enable TLS, use set_tls() after adding the socket; Feersum handles encryption internally via picotls.

A reference to $sock is kept internally to prevent garbage collection.

accept_on_fd($fileno)

Use the specified fileno to accept connections. May be used as an alternative to use_socket.

unlisten()

Stop listening on all sockets previously added via use_socket() or accept_on_fd(). All listener file descriptors are closed.

pause_accept()

Temporarily stop accepting new connections. Existing connections continue to be processed. Returns true if paused successfully, false if already paused or during shutdown.

Useful for load shedding or controlled traffic management.

resume_accept()

Resume accepting new connections after a pause_accept() call. Returns true if resumed successfully, false if not paused or during shutdown.

accept_is_paused()

Returns true if accepting is currently paused on all listeners, false otherwise. With multiple listen sockets, all must be paused for this to return true.

request_handler(sub { my $req = shift; ... })

Sets the global request handler. Any previous handler is replaced.

The handler callback is passed a Feersum::Connection object.

Subject to change: if the request has an entity body then the handler will be called only after receiving the body in its entirety. The body may use Content-Length or chunked Transfer-Encoding. The maximum size defaults to 67108864 bytes and can be changed via max_body_len().

psgi_request_handler(sub { my $env = shift; ... })

Like request_handler, but assigns a PSGI handler instead.

read_timeout()
read_timeout($duration)

Get or set the global read timeout. Must be a positive non-zero value; passing 0 or a negative value will croak. Changes take effect for new connections only; existing connections retain the timeout they were accepted with.

Feersum will wait about this long to receive all headers of a request (within the tolerances provided by libev). If an entity body is part of the request (e.g. POST or PUT) it will wait this long between successful read() system calls. This timeout also serves as the keepalive idle timeout between requests on persistent connections; there is no separate setting for that.

header_timeout()
header_timeout($seconds)

Get or set the header completion deadline timeout (Slowloris protection). Default is 10 seconds.

When enabled, connections must complete sending all HTTP headers within this many seconds from connection acceptance or receive a 408 Request Timeout response. For TLS connections where the handshake has not yet completed, the connection is silently closed (no HTTP response can be sent before the handshake finishes). This is a hard deadline that does not reset when data arrives, unlike read_timeout which resets on each successful read.

This provides protection against Slowloris-style attacks where malicious clients send headers very slowly to exhaust server connection resources.

Recommended value for direct internet exposure: 30-60 seconds. When running behind a reverse proxy (nginx, HAProxy), this can typically be left disabled since the proxy handles slow clients.

graceful_shutdown(sub { .... })

Causes Feersum to initiate a graceful shutdown of all outstanding connections. No new connections will be accepted. All listen socket file descriptors are closed; the Perl socket objects are not freed but the underlying fds are invalid after this call.

The sub parameter is a completion callback. It will be called when all connections have been flushed and closed. This allows one to do something like this:

my $cv = AE::cv;
my $death = AE::timer 2.5, 0, sub {
    fail "SHUTDOWN TOOK TOO LONG";
    exit 1;
};
Feersum->endjinn->graceful_shutdown(sub {
    pass "all gracefully shut down, supposedly";
    undef $death;
    $cv->send;
});
$cv->recv;
DIED

Not really a method so much as a static function. Works similar to EV's/AnyEvent's error handler.

The default implementation calls Carp::confess which prints a full stack trace to STDERR. To install a custom handler:

no strict 'refs';
*{'Feersum::DIED'} = sub { warn "Error: $@" };

Will get called for any errors that happen before the request handler callback is called, when the request handler callback throws an exception and potentially for other not-in-a-request-context errors.

It will not get called for read timeouts or header deadline timeouts (Slowloris protection) that occur while waiting for a complete header, nor for timeouts while waiting for a request entity body.

Note: Any exceptions thrown by the DIED handler itself are caught and will not propagate (the handler is called with G_EVAL). The server will still respond with a 500 error to the client.

set_server_name_and_port($host,$port)

Override Feersum's notion of what SERVER_NAME and SERVER_PORT should be.

set_keepalive($bool)

Override Feersum's default keepalive behavior. Changes take effect for new connections only.

set_reverse_proxy($bool)

Enable or disable reverse proxy mode. Changes take effect for new connections only. When enabled, Feersum trusts X-Forwarded-For and X-Forwarded-Proto headers from upstream proxies to determine the client's real IP address and request scheme.

Security note: Feersum uses the leftmost IP from X-Forwarded-For, which assumes a single-hop reverse proxy that overwrites (not appends to) the header. If your proxy appends to an existing X-Forwarded-For, clients can spoof their IP by sending a forged header. Ensure your reverse proxy strips or replaces X-Forwarded-For rather than appending.

The Feersum::Connection methods remote_address(), remote_port(), and env() will automatically use the forwarded values when this mode is active.

get_reverse_proxy()

Returns whether reverse proxy mode is currently enabled (1 or 0).

max_connection_reqs()
max_connection_reqs($count)

Get or set the maximum number of requests allowed per keep-alive connection. Default is 0 (unlimited). When set to a positive value, the connection will be closed after serving that many requests, even if keep-alive is enabled.

This is useful for preventing any single connection from monopolizing server resources and helps with memory management by periodically recycling connections.

read_priority()
read_priority($priority)

Get or set the libev watcher priority for read I/O operations. Priority range is -2 (lowest) to +2 (highest), default is 0. Higher priority watchers are invoked before lower priority ones.

write_priority()
write_priority($priority)

Get or set the libev watcher priority for write I/O operations. Priority range is -2 (lowest) to +2 (highest), default is 0.

accept_priority()
accept_priority($priority)

Get or set the libev watcher priority for accept operations. Priority range is -2 (lowest) to +2 (highest), default is 0.

set_epoll_exclusive($bool)

Enable or disable the use of EPOLLEXCLUSIVE flag when accepting connections. This is a Linux-specific optimization that prevents the "thundering herd" problem when multiple worker processes are accepting on the same socket.

When enabled, the kernel will wake only one process when a new connection arrives, rather than waking all waiting processes.

Only effective on Linux systems; has no effect on other platforms.

get_epoll_exclusive()

Returns true if EPOLLEXCLUSIVE mode is enabled, false otherwise.

set_psgix_io($bool)

Enable or disable the psgix.io PSGI extension (default: enabled). When disabled, Feersum skips setting up psgix.io in the PSGI environment hash, avoiding the overhead of creating a raw I/O handle for each request.

Disable this if your application never uses psgix.io (WebSocket upgrades, etc.) for a small performance improvement in the PSGI path.

get_psgix_io()

Returns whether psgix.io is currently enabled (1 or 0).

set_proxy_protocol($bool)

Enable or disable PROXY protocol support. When enabled, Feersum expects all new connections to begin with a PROXY protocol header (v1 text or v2 binary format, auto-detected) before any HTTP data.

The PROXY protocol is used by load balancers like HAProxy, AWS ELB/NLB, and nginx to pass the original client IP address to backend servers. When a valid PROXY header is received, REMOTE_ADDR and REMOTE_PORT are updated to reflect the client's real address.

Special cases: - PROXY v1 UNKNOWN: Keeps original address (used for health checks) - PROXY v2 LOCAL: Keeps original address (used for health checks)

Connections without a valid PROXY header will be rejected with HTTP 400.

Only enable this when ALL connections come from a proxy that sends PROXY headers.

get_proxy_protocol()

Returns true if PROXY protocol support is enabled, false otherwise.

max_accept_per_loop()
max_accept_per_loop($count)

Get or set the maximum number of connections to accept per event loop iteration. Default is 64.

Limiting accepts per loop prevents a flood of new connections from starving existing connections of CPU time. Lower values provide more fairness between new and existing connections; higher values improve throughput under heavy connection load.

active_conns()

Returns the current count of active connection objects being handled by Feersum. For HTTP/2, each concurrent stream counts as a separate unit in addition to the underlying TCP connection, so a single H2 connection with N streams contributes N+1 to this count.

total_requests()

Returns the total number of requests processed since the server started. Useful for monitoring and statistics. The counter is a native unsigned integer (64-bit on 64-bit Perl builds, 32-bit on 32-bit builds).

max_connections()
max_connections($limit)

Get or set the maximum number of concurrent connections. Default is 10000.

When the limit is reached, Feersum first tries to close the oldest idle keep-alive connection to make room. If no idle connections are available, the new connection is closed immediately after accept(). This provides protection against Slowloris-style DoS attacks that attempt to exhaust server resources by holding many connections open.

Setting this to 0 disables the limit. In production, consider also running Feersum behind a reverse proxy (nginx, HAProxy) which can provide additional connection limiting and rate limiting.

Note: When HTTP/2 is in use, each H2 stream pseudo-connection counts against this limit in addition to the TCP connection itself. See active_conns().

max_read_buf()
max_read_buf($bytes)

Get or set the maximum read buffer size per connection (default 64 MB). This limits how large the read buffer can grow during header parsing and chunked body reception. Requests that exceed the limit receive a 413 response. Set to 0 to reset to the compile-time default.

max_body_len()
max_body_len($bytes)

Get or set the maximum request body size (default 64 MB). This limits Content-Length values and cumulative chunked body sizes. Requests that exceed the limit receive a 413 response (HTTP/1.1) or RST_STREAM (HTTP/2). Set to 0 to reset to the compile-time default.

max_uri_len()
max_uri_len($bytes)

Get or set the maximum request URI length (default 8192 bytes). URIs that exceed the limit receive a 414 response. Set to 0 to reset to the compile-time default.

write_timeout()
write_timeout($seconds)

Get or set the write/response timeout. Default is 0 (disabled).

When enabled, connections that make no write progress within this many seconds are forcibly closed. The timer resets on each successful write. This protects against slow consumers that stall the server by not reading response data. For HTTP/2, the timeout operates per-stream: a stalled stream receives RST_STREAM rather than closing the entire connection. Disabled when the application takes over the socket via io() or psgix.io.

wbuf_low_water()
wbuf_low_water($bytes)

Get or set the write buffer low-water-mark. Default is 0 (callback fires only when the buffer is completely empty).

When using streaming responses with poll_cb, this setting controls when the write callback is invoked. If set to a positive value, the callback fires when the buffered data drops to or below this threshold, allowing the application to keep the write pipe full for better throughput. Works across all transports (plain, TLS, and HTTP/2).

set_tls(cert_file => $path, key_file => $path, [listener => $idx])

Enable TLS 1.3 on a listener. Requires Feersum to be compiled with TLS support (picotls submodule + OpenSSL; see Alien::OpenSSL).

The cert_file should be a PEM-encoded certificate chain, and key_file the corresponding PEM-encoded private key.

The optional listener parameter specifies which listener to configure (0-based index, in order of use_socket()/accept_on_fd() calls). Defaults to the last-added listener.

Call this after use_socket() or accept_on_fd() to apply TLS to that listener. Croaks if no listeners have been configured yet. Different listeners can have different TLS configurations, or some can be plain HTTP while others use TLS.

my $ngn = Feersum->endjinn;
$ngn->use_socket($tls_socket);
$ngn->set_tls(cert_file => 'server.crt', key_file => 'server.key');

When TLS is enabled and Alien::nghttp2 was available at build time, HTTP/2 can be enabled by passing h2 => 1. Without this flag, only http/1.1 is offered during ALPN negotiation:

$ngn->set_tls(cert_file => 'server.crt', key_file => 'server.key',
              h2 => 1);

Feersum::Runner also accepts h2 => 1 as a top-level option.

has_tls()

Returns true if Feersum was compiled with TLS support (picotls).

has_h2()

Returns true if Feersum was compiled with HTTP/2 support (nghttp2).

OBSERVABILITY

Feersum includes static USDT (Userland Statically Defined Tracing) probes for high-performance observability via DTrace or eBPF (bpftrace).

Probes provided by the feersum provider:

conn_new(fd, remote_addr, remote_port)

Fired when a new TCP connection is accepted.

conn_free(fd)

Fired when a connection is closed and its resources are freed.

req_new(fd, method, uri)

Fired when a complete set of HTTP headers has been parsed and a new request is starting.

req_body(fd, length)

Fired when a chunk of the request entity body is received.

resp_start(fd, status_code)

Fired when the response begins (headers are being sent).

GRITTY DETAILS

Compile Time Options

There are a number of constants at the top of Feersum.xs. If you change any of these, be sure to note that in any bug reports.

MAX_HEADERS

Defaults to 64. Controls how many headers can be present in an HTTP request.

If a request exceeds this limit, a 400 response is given and the app handler does not run.

MAX_HEADER_NAME_LEN

Defaults to 128. Controls how long the name of each header can be.

If a request exceeds this limit, a 431 response is given and the app handler does not run.

MAX_URI_LEN

Defaults to 8192. Controls the maximum length of the request URI (including query string).

If a request exceeds this limit, a 414 response is given and the app handler does not run.

MAX_BODY_LEN

Compile-time default for max_body_len() (64 MB). Controls how large the body of a POST/PUT/etc. can be. Use max_body_len($bytes) to override at runtime.

See also "BUGS".

READ_BUFSZ
READ_GROW_FACTOR

READ_BUFSZ defaults to 4096, READ_GROW_FACTOR 4.

Together, these tune how data is read for a request.

Read buffers start out at READ_BUFSZ bytes. If another read is needed and the buffer is under READ_BUFSZ bytes then the buffer gets an additional READ_GROW_FACTOR * READ_BUFSZ bytes. The trade-off with the grow factor is memory usage vs. system calls.

READ_TIMEOUT

Controls read timeout. Default is 5.0 sec. Also used as the keepalive idle timeout (there is no separate keepalive timeout setting).

FEERSUM_IOMATRIX_SIZE

Controls the size of the main write-buffer structure in Feersum. Making this value lower will use slightly less memory per connection at the cost of speed (and vice-versa for raising the value). The effect is most noticeable when you're app is making a lot of sparce writes. The default of 64 generally keeps usage under 4k per connection on full 64-bit platforms when you take into account the other connection and request structures.

NOTE: FEERSUM_IOMATRIX_SIZE cannot exceed your OS's defined IOV_MAX or UIO_MAXIOV constant. Solaris defines IOV_MAX to be 16, making it the default on that platform. Linux and OSX seem to set this at 1024.

FEERSUM_STEAL

For non-threaded perls >= 5.12.0, this defaults to enabled.

When enabled, Feersum will "steal" the contents of temporary lexical scalars used for response bodies. The scalars become undef as a result, but due to them being temps they likely aren't used again anyway. Stealing saves the time and memory needed to make a copy of that scalar, resulting in a mild to moderate performance boost.

This egregious hack only extends to non-magical, string, PADTMP scalars.

If it breaks for your new version of perl, please send stash a note (or a pull request!) on github.

Worth noting is that a similar zero-copy effect can be achieved by using the psgix.body.scalar_refs feature.

HTTP/2 Support

When Feersum is built with TLS (picotls + Alien::OpenSSL) and HTTP/2 (Alien::nghttp2) support, HTTP/2 can be negotiated via ALPN on TLS connections. HTTP/2 is disabled by default and must be explicitly enabled by passing h2 => 1 to set_tls() or to Feersum::Runner.

  • TLS-only -- cleartext HTTP/2 (h2c) is not supported. HTTP/2 is negotiated exclusively through the h2 ALPN token during the TLS handshake.

  • Request methods -- all standard methods (GET, POST, PUT, DELETE, etc.) are supported. Request bodies are fully buffered before the handler is called, same as HTTP/1.x. Note: unlike HTTP/1.1 where Feersum rejects non-standard methods (TRACE, PROPFIND, etc.) with 405, HTTP/2 passes all methods through to the request handler.

  • Streaming responses -- the psgi.streaming / start_streaming() interface works over HTTP/2, with each write() producing DATA frames.

  • Multiple concurrent streams -- the server processes many streams in parallel on a single connection, up to FEER_H2_MAX_CONCURRENT_STREAMS (default 100).

  • Not supported -- server push, server-sent trailers, streaming (incremental) request bodies, and sendfile. For HTTP/2 responses, use write() instead of sendfile.

  • PSGI environment -- psgi.url_scheme is https for HTTP/2 streams. SERVER_PROTOCOL is HTTP/2.

  • Extended CONNECT / WebSocket tunnels (RFC 8441) -- Feersum advertises SETTINGS_ENABLE_CONNECT_PROTOCOL=1 so HTTP/2 clients can open WebSocket tunnels via Extended CONNECT. Feersum translates the H2 Extended CONNECT into H1-equivalent PSGI env variables (matching HAProxy/nghttpx behaviour), so existing PSGI WebSocket middleware works transparently:

    REQUEST_METHOD       => 'GET'             # translated from CONNECT
    HTTP_UPGRADE         => 'websocket'       # synthesised from :protocol
    HTTP_CONNECTION      => 'Upgrade'         # synthesised
    psgix.h2.protocol    => 'websocket'       # raw :protocol value
    psgix.h2.extended_connect => 1

    The native $req->method() likewise returns GET for these streams.

    The handler code is identical to HTTP/1.1 upgrades: write an HTTP/1.1 101 response line followed by Upgrade: / Connection: headers via psgix.io (or $req->io()). Under H2, Feersum automatically sends 200 HEADERS to accept the tunnel and silently swallows the HTTP/1.1 101 response written by the app, relaying only the subsequent data as H2 DATA frames. This means the same PSGI handler works for both H1 and H2 without any protocol branching.

    psgix.io (or $req->io()) returns a bidirectional handle backed by a Unix socketpair; Feersum bridges bytes between that handle and the HTTP/2 DATA frames in both directions.

BUGS

Please report bugs using http://github.com/stash/Feersum/issues/

Request bodies are capped at MAX_BODY_LEN (64 MB by default). For untrusted clients it is still recommended to run Feersum behind a reverse proxy that enforces tighter entity-size limits.

Although not explicitly a bug, the following may cause undesirable behavior. Feersum will have set SIGPIPE to be ignored by the time your handler gets called. If your handler needs to detect SIGPIPE, be sure to do a local $SIG{PIPE} = ... (perlipc) to make it active just during the necessary scope.

Feersum is not thread-safe and must not be used with Perl ithreads. It uses global/static data structures (free lists, lookup tables) that are not protected by locks. Running Feersum in a multi-threaded environment will cause race conditions and memory corruption. Use pre-fork instead of threads for parallelism.

SEE ALSO

http://en.wikipedia.org/wiki/Feersum_Endjinn

Feersum Git: http://github.com/stash/Feersum git://github.com/stash/Feersum.git

picohttpparser Git: http://github.com/kazuho/picohttpparser git://github.com/kazuho/picohttpparser.git

AUTHOR

Jeremy Stashewsky, stash@cpan.org

THANKS

Tatsuhiko Miyagawa for PSGI and Plack.

Marc Lehmann for EV and AnyEvent (not to mention JSON::XS and Coro).

Kazuho Oku for picohttpparser.

Luke Closs (lukec), Scott McWhirter (konobi), socialtexters and van.pm for initial feedback and ideas. Audrey Tang and Graham Termarsch for XS advice.

Hans Dieter Pearcey (confound) for docs and packaging guidance.

For bug reports: Chia-liang Kao (clkao), Lee Aylward (leedo)

Audrey Tang (au) for flash socket policy support.

COPYRIGHT AND LICENSE

Copyright (C) 2011 by Jeremy Stashewsky

Portions Copyright (C) 2010 Socialtext Inc.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.7 or, at your option, any later version of Perl 5 you may have available.

picohttpparser is Copyright 2009 Kazuho Oku. It is released under the same terms as Perl itself.