My cgi.d
file is a self-contained module to create dynamic websites that can be accessed over the classic CGI protocol, the SCGI and FastCGI protocols, or by an embedded HTTP server that can be placed behind a reverse proxy—all with the same API for your code to use. The cgi.d
module provides a Cgi
class whose API exposes basic interaction with the requesting web browser such as query parameters, file uploads, cookies, HTTP headers, and writing response data.
Download cgi.d
from https://github.com/adamdruppe/arsd and put it in your project's directory. You do not need any other files from the repository nor any other external libraries unless you want to use the FastCGI interface, which requires the C library libfcgi
to be installed on the system.
For testing, you can use the embedded HTTP server and access it directly from your browser. However, to deploy the application, you'll also want to configure your web server software to use the application. This differs with each server and each protocol.
Let's execute the following steps to create a dynamic website:
arsd.cgi
.void
and take one parameter; an instance of the Cgi
class.Cgi
such as get
, post
, cookies
, pathInfo
, and domain.Cgi
such as setCookie
, setResponseContentType
, and others.write
method of Cgi
.main
function with mixin GenericMain!your_function_here;
.dmd.d yourfile.d cgi.d –version=embedded_httpd
.http://localhost:8085/
to see your page.The code is as follows:
import arsd.cgi; import std.array, std.conv; // This function does a quick and dirty HTML entity encoding // to prevent XSS. Typically, I use htmlEntitiesEncode in dom.d string simpleHtmlEncode(string s) { return s.replace("&", "&"). replace("<", "<").replace(">", ">"); } void handler(Cgi cgi) { // this is the default, so the following line is not // necessary. It is here for demonstration purposes. cgi.setResponseContentType("text/html; charset=UTF-8"); string data = "Hello, " ~ cgi.request("name", "user").simpleHtmlEncode(); data ~= "<br /> "; data ~= "Called from: " ~ cgi.pathInfo.simpleHtmlEncode(); data ~= "<br /> "; data ~= to!string(cgi.get).simpleHtmlEncode() ~ " "; cgi.write(data, true); // all written at once } mixin GenericMain!handler;
If you go to http://localhost:8085/foo?name=your+name
in your browser, you'll see the following output:
Hello, your name
Called from: /foo
["name":"your name"]
The cgi.d
module also supports a command-line interface in all compiled versions for easier debugging. Call the function with at least two arguments: an HTTP verb and a path. Then, optionally, add other request parameters as name=value
pairs. For example, ./app HEAD /foo name=test
will print the response, including headers, to stdout
.
The cgi.d
module abstracts away the details of interacting with the web server protocols by hiding them behind the Cgi
class and GenericMain
function.
The constructors of Cgi
come in two flavours: one understands CGI-like input data with a list of environment variables and a byte stream and the other understands HTTP, with just a byte stream from which the header information must be extracted. Both of these constructors support delegation, allowing the details of each protocol implementation to exist elsewhere (passed to it from GenericMain
) while reusing the core code.
The data from HTTP headers is organized into immutable
properties of the Cgi
class for easy access while prohibiting modification. These properties are immutable
to discourage using them as global variables due to a bad experience I had with PHP code using the $_POST
array to perform an action at a distance, making functions very difficult to understand. The Cgi
properties are meant to represent the HTTP request, not to be a place to stash function arguments and other global data.
Responses are crafted through the methods of the Cgi
class. The Cgi
class offers dedicated methods to set various headers including cookies, cache, content type, compression, and others. These methods try to intelligently handle multiple calls, for example, by updating the buffer instead of writing out two contradictory headers.
Response data is written with cgi.write
. If you send all the data at once, set the second argument to true
, allowing Cgi
to optimize the response. For example, a complete response need not be double-buffered or chunked.
Implementation-wise, Cgi
is a fairly simple class, using few of D's features. The GenericMain
function is a mixin template
that simply provides a prepackaged main
function. The most interesting method, implementation-wise, is cgi.request
, which uses an implicit compile-time type argument and std.conv.to
to extract a value from the request parameters. It is a simple template that does little more than call a library function with an error handler, but it is an easy-to-use way to filter input that doesn't match a particular type.
The embedded_httpd
version of Cgi
has two network server functions. One is used on Linux by default: forks
, which creates new processes to handle concurrent requests. The other one is used on all other systems, and it uses Phobos' threads and message passing to achieve the same task. Processes are more reliable because the server can recover from segmentation faults, such as null pointer uses, but aside from on Linux, they come with a performance penalty and do not work correctly at all on Windows. Thanks to D's thread-local storage, both modes are indistinguishable to most user code. To achieve maximum compatibility across all server models, you may want to use lazy initialization of thread-local resources.
vibe.d
module (the name of the entire framework) differs significantly from cgi.d
. While cgi.d
was inspired by a desire to replace PHP and is very conservative in its approach, vibe.d
was inspired by Node.js and innovates with fibers (see Chapter 10, Multitasking) for asynchronous I/O based scalability with code written in an intuitive, serial manner.3.138.35.255