arsd.cgi

Provides a uniform server-side API for CGI, FastCGI, SCGI, and HTTP web applications.

import arsd.cgi;

// Instead of writing your own main(), you should write a function
// that takes a Cgi param, and use mixin GenericMain
// for maximum compatibility with different web servers.
void hello(Cgi cgi) {
	cgi.setResponseContentType("text/plain");

	if("name" in cgi.get)
		cgi.write("Hello, " ~ cgi.get["name"]);
	else
		cgi.write("Hello, world!");
}

mixin GenericMain!hello;

Test on console (works in any interface mode):

$ ./cgi_hello GET / name=whatever

If using http version (default on dub builds, or on custom builds when passing -version=embedded_httpd to dmd):

$ ./cgi_hello --port 8080
# now you can go to http://localhost:8080/?name=whatever

Please note: the default port for http is 8085 and for cgi is 4000. I recommend you set your own by the command line argument in a startup script instead of relying on any hard coded defaults. It is possible though to hard code your own with RequestServer.

Public Imports

std.string
public import std.string;
Undocumented in source.
std.stdio
public import std.stdio;
Undocumented in source.
std.conv
public import std.conv;
Undocumented in source.

Members

Classes

BasicDataServerSession
class BasicDataServerSession(Data)

An implementation of Session that works on real cgi connections utilizing the BasicDataServer.

BufferedInputRange
class BufferedInputRange

This is NOT ACTUALLY an input range! It is too different. Historical mistake kinda.

Cgi
class Cgi

The main interface with the web request

CgiTester
class CgiTester

A helper test class for request handler unittests.

CollectionOf
class CollectionOf(Obj)

Base class for REST collections.

ConnectionClosedException
class ConnectionClosedException

thrown when a connection is closed remotely while we waiting on data from it

EventSourceServerImplementation
class EventSourceServerImplementation
ListeningConnectionManager
class ListeningConnectionManager

To use this thing:

MockSession
class MockSession(Data)

A mock object that works like the real session, but doesn't actually interact with any actual database or http connection. Simply stores the data in its instance members.

ResourceNotFoundException
class ResourceNotFoundException

You can throw this from an api handler to indicate a 404 response. This is done by the presentExceptionAsHtml function in the presenter.

RestObject
class RestObject(CRTP)

The base of all REST objects, to be used with serveRestObject and serveRestCollectionOf.

WebObject
class WebObject

The base class for the dispatcher function and object support.

WebPresenter
class WebPresenter(CRTP)

A web presenter is responsible for rendering things to HTML to be usable in a web browser.

WebSocket
class WebSocket

WEBSOCKET SUPPORT:

Enums

AddTrailingSlash
enum AddTrailingSlash
RemoveTrailingSlash
enum RemoveTrailingSlash

UDA used to indicate to the dispatcher that a trailing slash should always be added to or removed from the url. It will do it as a redirect header as-needed.

Functions

cgiMainImpl
void cgiMainImpl(string[] args)

This is the function GenericMain calls. View its source for some simple boilerplate you can copy/paste and modify, or you can call it yourself from your main.

decodeVariables
string[][string] decodeVariables(string data, string separator, string[]* namesInOrder, string[]* valuesInOrder)

breaks down a url encoded string

decodeVariablesSingle
string[string] decodeVariablesSingle(string data)

breaks down a url encoded string, but only returns the last value of any array

defaultListeningHost
string defaultListeningHost()

Default host for listening. 127.0.0.1 for scgi, null (aka all interfaces) for all others. If you want the server directly accessible from other computers on the network, normally use null. If not, 127.0.0.1 is a bit better. Settable with default handlers with --listening-host command line argument.

defaultListeningPort
ushort defaultListeningPort()

Returns the default listening port for the current cgi configuration. 8085 for embedded httpd, 4000 for scgi, irrelevant for others.

dispatchTo
auto dispatchTo(string urlPrefix)

Dispatches the URL (and anything under it) to another dispatcher function. The function should look something like this:

dummyCgi
Cgi dummyCgi(Cgi.RequestMethod method, string url, ubyte[] data, void delegate(const(ubyte)[]) outputSink)

use this for testing or other isolated things when you want it to be no-ops

encodeVariables
string encodeVariables(string[string] data)

url encodes the whole string

encodeVariables
string encodeVariables(string[][string] data)

url encodes a whole string

handleWith
auto handleWith(string urlPrefix)

Dispatches the URL to a specific function.

makeDataUrl
string makeDataUrl(string mimeType, void[] data)

Makes a data:// uri that can be used as links in most newer browsers (IE8+).

rawurlencode
string rawurlencode(char[] data)

Encodes all but the explicitly unreserved characters per rfc 3986 Alphanumeric and -_.~ are the only ones left unencoded name is borrowed from php

serveApi
auto serveApi(string urlPrefix)

Serves a class' methods, as a kind of low-state RPC over the web. To be used with dispatcher.

serveRedirect
auto serveRedirect(string urlPrefix, string redirectTo, int code)

Redirects one url to another

serveRestObject
auto serveRestObject(string urlPrefix)

Serves a REST object, similar to a Ruby on Rails resource.

serveStaticData
auto serveStaticData(string urlPrefix, immutable(void)[] data, string contentType)

Serves static data. To be used with dispatcher.

serveStaticFile
auto serveStaticFile(string urlPrefix, string filename, string contentType)

Serves a static file. To be used with dispatcher.

serveStaticFileDirectory
auto serveStaticFileDirectory(string urlPrefix, string directory)

This serves a directory full of static files, figuring out the content-types from file extensions. It does not let you to descend into subdirectories (or ascend out of it, of course)

trySimulatedRequest
bool trySimulatedRequest(string[] args)

Tries to simulate a request from the command line. Returns true if it does, false if it didn't find the args.

Interfaces

BasicDataServer
interface BasicDataServer

Direct interface to the basic data add-on server. You can typically use Cgi.getSessionObject as a more convenient interface.

EventIoServer
interface EventIoServer

You can customize your server by subclassing the appropriate server. Then, register your subclass at compile time with the registerEventIoServer template, or implement your own main function and call it yourself.

EventSourceServer
interface EventSourceServer
ScheduledJobServer
interface ScheduledJobServer
Session
interface Session(Data)

A convenience object for talking to the BasicDataServer from a higher level. See: Cgi.getSessionObject.

SessionObject
interface SessionObject

Base for storing sessions in an array. Exists primarily for internal purposes and you should generally not use this.

Mixin templates

CustomCgiMain
mixintemplate CustomCgiMain(CustomCgi, alias fun, long maxContentLength = defaultMaxContentLength)

If you want to use a subclass of Cgi with generic main, use this mixin.

DispatcherMain
mixintemplate DispatcherMain(Presenter, DispatcherArgs...)

Boilerplate mixin for a main function that uses the dispatcher function.

ForwardCgiConstructors
mixintemplate ForwardCgiConstructors()

If you are doing a custom cgi class, mixing this in can take care of the required constructors for you

GenericMain
mixintemplate GenericMain(alias fun, long maxContentLength = defaultMaxContentLength)

Use this instead of writing your own main

Static variables

fiberStackSize
size_t fiberStackSize;

The stack size when a fiber is created. You can set this from your main or from a shared static constructor to optimize your memory use if you know you don't need this much space. Be careful though, some functions use more stack space than you realize and a recursive function (including ones like in dom.d) can easily grow fast!

Structs

CreatedResource
struct CreatedResource(T)

This is meant to be returned by a function that takes a form POST submission. You want to set the url of the new resource it created, which is set as the http Location header for a "201 Created" result, and you can also set a separate destination for browser users, which it sets via a "Refresh" header.

DefaultFormat
struct DefaultFormat

UDA: default format to respond for this method

DispatcherData
struct DispatcherData(Presenter)

Used exclusively with dispatchTo

DisplayName
struct DisplayName

UDA: The name displayed to the user in auto-generated HTML.

MultipleResponses
struct MultipleResponses(T...)

Can return one of the given types, decided at runtime. The syntax is to declare all the possible types in the return value, then you can return typeof(return)(...value...) to construct it.

Redirection
struct Redirection

You can return this from WebObject subclasses for redirections.

RequestServer
struct RequestServer

A server control and configuration struct, as a potential alternative to calling GenericMain or cgiMainImpl. See the source of cgiMainImpl to an example of how you can use it.

ScheduledJobHelper
struct ScheduledJobHelper

See schedule to make one of these. You then call one of the methods here to set it up:

Uri
struct Uri

Represents a url that can be broken down or built up through properties

UrlName
struct UrlName

UDA: The name used in the URL or web parameter.

ifCalledFromWeb
struct ifCalledFromWeb(alias func)

This can be attached to any constructor or function called from the cgi system.

Templates

AutomaticForm
template AutomaticForm(alias customizer)

Implies POST path for the thing itself, then GET will get the automatic form.

dispatcher
template dispatcher(definitions...)

A URL dispatcher.

schedule
template schedule(alias fn, T...)

First step to schedule a job on the scheduled job server.

Detailed Description

Compile versions

If you are using dub, use:

subConfiguration "arsd-official:cgi" "VALUE_HERE"

or to dub.json:

"subConfigurations": {"arsd-official:cgi": "VALUE_HERE"}

to change versions. The possible options for VALUE_HERE are:

  • embedded_httpd for the embedded httpd version (built-in web server). This is the default.
  • cgi for traditional cgi binaries.
  • fastcgi for FastCGI builds.
  • scgi for SCGI builds.
  • stdio_http for speaking raw http over stdin and stdout. See RequestServer.serveSingleHttpConnectionOnStdio for more information.

With dmd, use:

Interfaces(mutually exclusive)
-version=plain_cgiThe default building the module alone without dub - a traditional, plain CGI executable will be generated.
-version=embedded_httpdA HTTP server will be embedded in the generated executable. This is default when building with dub.
-version=fastcgiA FastCGI executable will be generated.
-version=scgiA SCGI (SimpleCGI) executable will be generated.
-version=embedded_httpd_threadsThe embedded HTTP server will use a single process with a thread pool. (use instead of plain embedded_httpd if you want this specific implementation)
-version=embedded_httpd_processesThe embedded HTTP server will use a prefork style process pool. (use instead of plain embedded_httpd if you want this specific implementation)
-version=embedded_httpd_processes_accept_after_forkIt will call accept() in each child process, after forking. This is currently the only option, though I am experimenting with other ideas. You probably should NOT specify this right now.
-version=stdio_httpThe embedded HTTP server will be spoken over stdin and stdout.
Tweaks(can be used together with others)
-version=cgi_with_websocketThe CGI class has websocket server support.
-version=with_opensslnot currently used
-version=cgi_embedded_sessionsThe session server will be embedded in the cgi.d server process
-version=cgi_session_server_processThe session will be provided in a separate process, provided by cgi.d.

Compile and run

For CGI, dmd yourfile.d cgi.d then put the executable in your cgi-bin directory.

For FastCGI: dmd yourfile.d cgi.d -version=fastcgi and run it. spawn-fcgi helps on nginx. You can put the file in the directory for Apache. On IIS, run it with a port on the command line (this causes it to call FCGX_OpenSocket, which can work on nginx too).

For SCGI: dmd yourfile.d cgi.d -version=scgi and run the executable, providing a port number on the command line.

For an embedded HTTP server, run dmd yourfile.d cgi.d -version=embedded_httpd and run the generated program. It listens on port 8085 by default. You can change this on the command line with the --port option when running your program.

You can also simulate a request by passing parameters on the command line, like:

./yourprogram GET / name=adr

And it will print the result to stdout.

CGI Setup tips

On Apache, you may do SetHandler cgi-script in your .htaccess file.

Integration tips

cgi.d works well with dom.d for generating html. You may also use web.d for other utilities and automatic api wrapping.

dom.d usage:

import arsd.cgi;
import arsd.dom;

void hello_dom(Cgi cgi) {
	auto document = new Document();

	static import std.file;
	// parse the file in strict mode, requiring it to be well-formed UTF-8 XHTML
	// (You'll appreciate this if you've ever had to deal with a missing </div>
	// or something in a php or erb template before that would randomly mess up
	// the output in your browser. Just check it and throw an exception early!)
	//
	// You could also hard-code a template or load one at compile time with an
	// import expression, but you might appreciate making it a regular file
	// because that means it can be more easily edited by the frontend team and
	// they can see their changes without needing to recompile the program.
	//
	// Note on CTFE: if you do choose to load a static file at compile time,
	// you *can* parse it in CTFE using enum, which will cause it to throw at
	// compile time, which is kinda cool too. Be careful in modifying that document,
	// though, as it will be a static instance. You might want to clone on on demand,
	// or perhaps modify it lazily as you print it out. (Try element.tree, it returns
	// a range of elements which you could send through std.algorithm functions. But
	// since my selector implementation doesn't work on that level yet, you'll find that
	// harder to use. Of course, you could make a static list of matching elements and
	// then use a simple e is e2 predicate... :) )
	document.parseUtf8(std.file.read("your_template.html"), true, true);

	// fill in data using DOM functions, so placing it is in the hands of HTML
	// and it will be properly encoded as text too.
	//
	// Plain html templates can't run server side logic, but I think that's a
	// good thing - it keeps them simple. You may choose to extend the html,
	// but I think it is best to try to stick to standard elements and fill them
	// in with requested data with IDs or class names. A further benefit of
	// this is the designer can also highlight data based on sources in the CSS.
	//
	// However, all of dom.d is available, so you can format your data however
	// you like. You can do partial templates with innerHTML too, or perhaps better,
	// injecting cloned nodes from a partial document.
	//
	// There's a lot of possibilities.
	document["#name"].innerText = cgi.request("name", "default name");

	// send the document to the browser. The second argument to `cgi.write`
	// indicates that this is all the data at once, enabling a few small
	// optimizations.
	cgi.write(document.toString(), true);
}

Concepts: Input: Cgi.get, Cgi.post, Cgi.request, Cgi.files, Cgi.cookies, Cgi.pathInfo, Cgi.requestMethod, and HTTP headers (Cgi.headers, Cgi.userAgent, Cgi.referrer, Cgi.accept, Cgi.authorization, Cgi.lastEventId)

Output: Cgi.write, Cgi.header, Cgi.setResponseStatus, Cgi.setResponseContentType, Cgi.gzipResponse

Cookies: Cgi.setCookie, Cgi.clearCookie, Cgi.cookie, Cgi.cookies

Caching: Cgi.setResponseExpires, Cgi.updateResponseExpires, Cgi.setCache

Redirections: Cgi.setResponseLocation

Other Information: Cgi.remoteAddress, Cgi.https, Cgi.port, Cgi.scriptName, Cgi.requestUri, Cgi.getCurrentCompleteUri, Cgi.onRequestBodyDataReceived

Overriding behavior: Cgi.handleIncomingDataChunk, Cgi.prepareForIncomingDataChunks, Cgi.cleanUpPostDataState

Installing: Apache, IIS, CGI, FastCGI, SCGI, embedded HTTPD (not recommended for production use)

Guide for PHP users

If you are coming from PHP, here's a quick guide to help you get started:

<?php
	$foo = $_POST["foo"];
	$bar = $_GET["bar"];
	$baz = $_COOKIE["baz"];

	$user_ip = $_SERVER["REMOTE_ADDR"];
	$host = $_SERVER["HTTP_HOST"];
	$path = $_SERVER["PATH_INFO"];

	setcookie("baz", "some value");

	echo "hello!";
?>
import arsd.cgi;
void app(Cgi cgi) {
	string foo = cgi.post["foo"];
	string bar = cgi.get["bar"];
	string baz = cgi.cookies["baz"];

	string user_ip = cgi.remoteAddress;
	string host = cgi.host;
	string path = cgi.pathInfo;

	cgi.setCookie("baz", "some value");

	cgi.write("hello!");
}

mixin GenericMain!app

Array elements

In PHP, you can give a form element a name like "something[]", and then $_POST["something"] gives an array. In D, you can use whatever name you want, and access an array of values with the cgi.getArray["name"] and cgi.postArray["name"] members.

Databases

PHP has a lot of stuff in its standard library. cgi.d doesn't include most of these, but the rest of my arsd repository has much of it. For example, to access a MySQL database, download database.d and mysql.d from my github repo, and try this code (assuming, of course, your database is set up):

import arsd.cgi;
import arsd.mysql;

void app(Cgi cgi) {
	auto database = new MySql("localhost", "username", "password", "database_name");
	foreach(row; mysql.query("SELECT count(id) FROM people"))
		cgi.write(row[0] ~ " people in database");
}

mixin GenericMain!app;

Similar modules are available for PostgreSQL, Microsoft SQL Server, and SQLite databases, implementing the same basic interface.

See Also

You may also want to see arsd.dom, arsd.web, and arsd.html for more code for making web applications.

For working with json, try arsd.jsvar.

arsd.database, arsd.mysql, arsd.postgres, arsd.mssql, and arsd.sqlite can help in accessing databases.

If you are looking to access a web application via HTTP, try std.net.curl, arsd.curl, or arsd.http2.

Meta