APIHTTP store server

HTTP store server

A simple HTTP store server.

Usage example

			my $store = CDS::FolderStore->new('/path/to/store');
			my $server = CDS::HTTPServer->new(80);
			$server->addHandler(CDS::HTTPServer::StoreHandler->new('/', $store));
			$server->addHandler(CDS::HTTPServer::IdentificationHandler->new('/'));
			$server->run;
		

Creates and runs a HTTP store server.

The server authenticates the users, enforces access rights, and makes a trusted (unauthenticated) request on the store:

Application with private key HTTP server Store trusted enforcingaccess rights Server system Gateway firewall, TLS,load-balancer, ... authenticated

The store is usually a local store (such as a folder store), or a store in the local (trusted) network.

The first parameter to the StoreHandler is the path under which the store is available. The above example yields the following URLs:

Store:     http://your-domain.tld/
Objects:   http://your-domain.tld/objects/
Accounts:  http://your-domain.tld/accounts/

The identification handler is optional, and simply outputs a message saying that this is a Condensation store.

The run call starts listening for connections and does not return.

CORS

$server->setCorsAllowEverybody(1)

Sets the CORS headers to allow everybody to access the store from within a web browser. Without these headers, all major web browsers prevent foreign websites from accessing the store.

CORS has no effects on direct TCP connections from outside web browsers.

Static content delivery

The server can be configured to deliver static content. This is particularly useful to create small self-contained web apps.

$server->addHandler(CDS::HTTPServer::StaticContentHandler->new('/path/to/content', $content, $contentType));

Upon accessing the specified path, delivers the content bytes with the given content type (e.g. image/png, or text/html; charset=utf-8) and response code 200 OK. Only GET requests are processed.

$server->addHandler(CDS::HTTPServer::StaticFilesHandler->new('/path/to/static', $rootFolder, $indexFilename));

Upon accessing anything below the specified path, delivers the corresponding file from within rootFolder. If an indexFilename (e.g. index.html) is provided, folders are handled as follows:

Content types are inferred from the file extension. Additional content types may be specified as follows:

			my $handler = CDS::HTTPServer::StaticFilesHandler->new('/path/to/static', $folder, $indexFilename);
			$handler->setContentType('mp4', 'video/mp4');
			$server->addHandler($handler);
		

If the extension is not known, the content type application/octet-stream is used.

For more advanced configuration, subclass this handler and overwrite the get method with your own rules:

			sub get {
				my $o = shift;
				my $request = shift;
				my $path = $request->pathAbove($o:root) // return;
				return $o->deliverFile($request, '/srv/common/logo.png') if $path eq '/logo.png';
				return $o->deliverFile($request, '/srv/common/notes', 'text/plain; charset=utf-8') if $path eq '/notes';
				return $request->reply404 if $path !~ /\.jpg$/;
				return $o->deliverFileForPath($request, $path);
			}
		

Two high-level methods for file delivery are available:

Message gateway

$server->addHandler(CDS::HTTPServer::MessageGatewayHandler->new('/path', $keyPair, $recipient));

Sends a Condensation message to recipient for each incoming request. The message is signed by the provided key pair, and contains a single record with:

Requests are answered with a 200 OK.

The message gateway is primarily useful to collect data from sensors which submit their data via HTTP requests.

Implementing handlers

To imlement your own handler, create a package with a process method:

			package MyHandler;

			sub new { bless {} }

			sub process {
				my $o = shift;
				my $request = shift;

				return if $request->path !~ /^\/my-handler$/;

				# Options requests
				return $request->replyOptions('HEAD', 'GET')
					if $request->method eq 'OPTIONS';

				# Get requests
				return $request->reply200('You have reached this handler.')
					if $request->method eq 'HEAD' || $request->method eq 'GET';

				# Method not allowed
				return $request->reply405;
			}
		

and add an instance to the server:

			...
			$server->addHandler(MyHandler->new);
			...
		

Handlers are called in the order they have been added to the server, until a handler returns a status code. If no handler processes a request, a fallback handler drops the request data and issues a 404 Not Found reply.

Requests

The request instance provides information about the current request, as well as a few useful functions to produce a reply:

			# Request properties
			$request->server         # returns the CDS::HTTPServer instance
			$request->method         # returns the HTTP request method as uppercase string (e.g. GET)
			$request->path           # returns the request path without the query string
			$request->queryString    # returns the query string
			$request->peerAddress    # returns the client's IP address
			$request->peerPort       # returns the client's TCP port
			$request->headers        # returns the HTTP headers of the request as hashref

			# Request data
			$request->remainingData  # returns the number of remaining bytes the client sent
			$request->readData       # reads all data sent by the client and returns it as byte string
			$request->dropData       # drops (reads but ignores) all data sent by the client

			# Logging
			$request->log($text)     # writes a short text to the log file

			# Utility
			$request->pathAbove('/myroot/')    # returns the request path above this folder, or undef
			$request->parseQueryString         # parses the query string, and returns a hashref of byte strings
			$request->checkSignature($bytes)   # checks the Condensation signature for $bytes

			# Common replies
			$request->reply200($text)          # sends a 200 OK reply with a "text/plain; charset=utf-8" content type
			$request->reply200Bytes($bytes)    # sends a 200 OK reply with a "application/octet-stream" content type
			$request->reply200HTML($html)      # sends a 200 OK reply with a "text/html; charset=utf-8" content type
			$request->reply303($location)      # sends a 303 See Other reply with the given location
			$request->reply400($text)          # sends a 400 Bad Request reply with the given text message
			$request->reply403($text)          # sends a 403 Forbidden reply with the given text message
			$request->reply404($text)          # sends a 404 Not Found reply with the given text message
			$request->reply405($text)          # sends a 405 Method Not Allowed reply with the given text message
			$request->reply500($text)          # sends a 500 Internal Server Error reply with the given text message
			$request->reply503($text)          # sends a 503 Service Not Available reply with the given text message
			$request->replyOptions(@methods)   # sends a 200 OK reply to an OPTIONS request
			$request->replyFatalError($error)  # logs the error and sends an empty 500 Internal Server Error reply

			# Generic reply
			$request->reply($responseCode, $responseLabel, $headers, $content)
		

Since the query string parser returns a hashref of byte strings, its values need to be converted appropriately:

			my $args = $request->parseQueryString;
			my $valueAsBytes = $args->{'key'};
			my $valueAsText = Encode::decode_utf8($args->{'key'});
			my $valueAsNumber = $args->{'key'} * 1;
			my $valueProvided = exists $args->{'key'};
		

The HTTP store server is only available in Perl.

See also