Guide Home
POD Home

Sloop::Client

DESCRIPTION

Sloop::Client represents a connection to a remote HTTP client; such a client may have multiple such connections simultaneously, each via a separate Sloop::Client object.

The class adds HTTP related functionality to base class Sloop::Connection. It still does not include the low level I/O routines necessary for a functioning socket, and so a Sloop::Client object by itself is not useful. Instead, Sloop::Client is a mutual superclass, together with one of the Sloop::Socket::* classes, of the Sloop::Client::Regular and ::Secure subclasses. However, all of the API methods are here or in one of its superclasses.

SYNOPSIS

Note that Sloop::Client objects should not be created by the user, they are received in a $sloop->{handler} callback. However, the defaults used may be overridden via $sloop->{client_constructor} (see Sloop::Server SYNOPSIS). Note that although some "REQUIRED" parameters must be supplied, they are supplied by the server, so there are no requirements for client_constructor as long as you pass the argument stack through.

 my $client = Sloop::Client->new (
    debug => (optional) see Sloop::Base
    fault_max => (optional) see Sloop::Connection
    fd => (optional) see Sloop::Base
    generic => (optional) hash ref conforming to $Sloop::Generic::Pages
    id => (optional) see Sloop::Base
    ip => (REQUIRED) string
    logger => (REQUIRED) see Sloop::Base
    logRequest => (optional) sub ref
    maxage => (optional) integer
    max_recv => (optional) see Sloop::Connection
    max_send => (optional) see Sloop::Connection
    timeout => (optional) see Sloop::Connection
    postRequestHandler => (optional) sub ref
 );

Parameters not documented in a base class are described below.

generic

A hash ref matching the specifications of $Sloop::Generic::Pages. If not explicit, the default will be used and Sloop::Generic included via require. This defines the html accompaniment for generic status based responses (e.g., 404 "Not Found").

logRequest

A subrountine reference called after a request header has been successfully read and parsed. This function does not have to do anything; the default $client->logReq() is documented under METHODS below. Note that if you override $client->{logger}, you may want to override this as well, since logReq() calls the logger.

ip

The IP address of the remote party.

maxage

Value in seconds to use with 'Cache-Control: max-age=' in HTTP response headers. If zero, an 'Expires' header in the past is added to deal with old non-compliant browsers. See also $client->reply() under METHODS.

postRequestHandler

If defined, this is called by the server after a request header is parsed. It receives one argument, a Sloop::Client object, to which a Sloop::Client::Request will be attached.

If the handler returns a true value, the request handling continues normally. In this case, you should NOT have sent any response or closed the connection.

If the handler returns false, the handling simply stops. In this case, you SHOULD have either replied -- via e.g., reply() or redirect() -- or else closed the connection with cancel(). If not, the connection will be left in a limbo state which does not time out or allow for further communication (i.e., essentially leaked until the other side closes).

If you wish to read POST data in the request, you can do that too -- return false and send a reply in the post handler.

You can only set one primary such handler, but you can chain them together, e.g.:

 my $existingHandler = $client->{postRequestHandler};
 $client->{postRequestHandler} = sub {
     my $client = shift;
     # Do whatever.
     if ($existingHandler) { return $existingHandler($client) }
 }

ATTRIBUTES

In addition to attributes inherited from Sloop::Base or Sloop::Connection, the following fields from the constructor are accessible with the same name and meaning:

generic
logRequest
ip
maxage
postRequestHandler

There are a few potential post construction attributes as well:

$client->{request}

If defined, a Sloop::Client::Request object. If not, there is no request pending.

$client->{afterReplyHandler}

If defined, this was set via $client->setAfterReplyHandler() (see below). This is useful if you wish to chain such handlers.

METHODS

$client->cancel()

You should call this if for whatever reason you don't want to reply to a client. Do NOT do this if you have sent a reply, at least until you are sure it has been completely transmitted (see $client->setAfterReplyHandler(), below).

This immediately closes the connection and flags the object such that all internal references to $client will be removed at the next opportunity.

$client->generic($response_code)

$response_code is an HTTP response code, eg, 500 or 404.

Error checked wrapper for $client->{generic}->{$response_code}->($client), sending an HTTP response with the corresponding response code. The default Sloop::Generic module includes a simple html page for most common simple error codes. If a generic page corresponding to $response_code does not exist, an even simpler page displaying just the number is sent.

$client->logReq()

This is the default assigned to the subroutine reference $client->{logRequest} in the constructor (see SYNOPSIS). It prints a message via $client->{logger} as a LOG_REQUEST (see Sloop::Constants) with the following format:

 [$client->{id}] path ip user-agent

Where "path" is from $client->{request}->requestPath, "ip" from $client->{ip}, and "user-agent" from $client->{request}->header('user-agent'); the last field is truncated to 32 characters. This function needn't be called manually, and is documented only because of its role as a default for $client->{logRequest}.

$client->readPost($handler, $max)

An alternative to $client->simplePost() which fires a callback for each chunk of post data read in. This is necessary when using Sloop::Multipart.

$handler is a callback that will recieve an integer arg, 1 if the post is complete, 0 if it is not, and -1 if it and $client have been cancelled due to a logged error. The accumulated post is always available in $client->{request}->{body}. You may manipulate that data at any point, as long as {body} is not left undefined (or redefined as something other than a scalar).

$max is the same as described for simplePost().

Returns -1 on error; this will be logged and $client->cancel called. Otherwise 0.

$client->redirect($url, $note)

Sends the client an HTTP 302 Redirect message. This substitutes for a $client->reply() and either one or the other (not both) should be called per request. As per the HTTP 1.1 protocol, it includes a body with a brief note for clients that do not redirect automatically. By default this is "You have been redirected, click here.", unless $note is defined, in which case that is used.

$url is an absolute URL (eg, "http://wherever") for use in the HTTP 'Location:' header and the "click here" href.

$note is the optional HTML body.

You can send a 301, 303, etc. redirect by using the appropriate %args with $client->reply(). As with all the Sloop::Client methods, this is a non-blocking call. It queues data to be sent, it does not wait for its transmission.

$client->reply($body, %args)

Sends an HTTP response to the client, including a user supplied message body. As with all the Sloop::Client methods, this is a non-blocking call. It queues data to be sent, it does not wait for its transmission.

An example:

 $client->reply (
     \$data,
     type => 'application/octet-stream',
     maxage => 3600
 );

$body is scalar reference to the response body. It may be undefined, in which case no body is sent. WARNING: If this argument is defined but not a reference, the call is FATAL. If it is not a scalar reference, the behavior is undefined. The content is copied out during the call without modification, so you can do what you like with it afterward.

%args are optional; the defaults are given below:

status => 200

An HTTP status-code; it may be any of the codes from RFC 2616 section 10. This list is also available via %Sloop::Client::HTTP_STATUS. You can add to those if you want to create a custom code, although for a general purpose web server this is a bad idea as it violates the HTTP 1.1 protocol.

maxage => $self->{maxage}

This can be used to override the value set for 'Cache-control: maxage' and 'Expires:' (see "Auto-generated Headers", below). It is in seconds and the default is 0. You can override this by defining a custom client_constructor. The reason the global default is 0 and cannot be changed more simply is that you should ideally set it in the handler reply using this parameter. If you have a collection of static pages, this is easily done using Sloop::Static::directory(); if the content is dynamic, you should probably stick with zero.

type => 'text/html; charset=UTF-8'

This is only included if $body is defined. It sets the 'Content-type' header (see "Auto-generated Headers", below).

length => bytes

This will override the 'Content-length' header (see "Auto-generated Headers", below). You should not normally use this as an appropriate value will be determined. It will only be included if $body is defined.

cookies => undef

An array ref containing strings to use with a 'Set-Cookie' header; each string gets its own header. Sloop::Lib::makeCookie() can be used to create such a string.

extra => undef

A hashref containing HTTP headers to add as key (header name) value pairs. Eg:

 extra => {
        'Accept-Language' => 'en_CA',
        'Max-Forward' => 2
 }

These will not replace any header of the same name added by sloop; their values will be amalgamated into a comma seperated list. One way to examine the header as sent is to use DBG_REPLY from Sloop::Constants.

Auto-generated Headers

Sloop adds appropriate values for the following headers to all responses:

 Cache-Control
 Server

"Cache-Control" uses a "max-age" as described in SYNOPSIS; if this is zero an "Expires" header is also used. "Server" is "Perl Sloop".

Responses with a body also have a "Content-Length", and "Content-Type" (defaults to "text/html; charset=UTF-8") header. If you want to override "Content-Type", use $args->{type}, above. Non-persistent connections (e.g., for HTTP 1.0 clients) contain a "Connection: close" header.

$client->setAfterReplyHandler($coderef)

Sets a callback that will be fired once a message sent via reply() has successfully completed transmission. For non-persistent connections, this is immediately before the connection will be closed. For persistent connections, it will be before any subsequent request is read. If sending the reply failed, the callback will not be fired and the connection will be closed.

$coderef is a subroutine reference. It receives one argument, the client object on which it was set.

If the subroutine returns 0 or undef, it will only fire once. If you wish to set a different handler, return a reference to it. If the return value is defined but not a reference, the same handler will be left attached. Do not try and call setAfterReplyHandler() itself inside the handler.

There can only be one such handler set at a time, but as described in the SYNOPSIS with regard to postRequestHandler, they may be chained together. Hence, it never hurts to check; the relevant member field is $client->{afterReplyHandler}.

$client->simplePost($handler, $max)

Wrapper for readPost() which skips reporting each chunk as it is recieved. If reading the POST fails, an error is logged but no callback is fired; $client will be left defunct. If $client->{request} is not a POST, $client is sent a generic "405 Method Not Allowed" response and simplePost returns -1.

$handler is a subroutine reference. It will fire when reading the POST data has successfully completed.

$max is the maximum value to accept in the Content-Length header; if this is exceeded, $client will be cancelled. Note that if a client sends data in excess of the Content-Length, that data will be left in the request pipeline and cause the client to be subsequently deleted. Clients which send less than the Content-Length will never have a completed post, and the data will be lost on a subsequent request in the same pipeline.

Returns The same thing as readPost().