Creating a dynamic website with cgi.d

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.

Getting ready

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.

How to do it…

Let's execute the following steps to create a dynamic website:

  1. Import arsd.cgi.
  2. Write a function that handles each request. It should return void and take one parameter; an instance of the Cgi class.
  3. Inspect request parameters with the properties of Cgi such as get, post, cookies, pathInfo, and domain.
  4. Set any cookies and headers needed for your response with the methods of Cgi such as setCookie, setResponseContentType, and others.
  5. Send a response with the write method of Cgi.
  6. Mix in the library's generic main function with mixin GenericMain!your_function_here;.
  7. Compile with dmd.d yourfile.d cgi.d –version=embedded_httpd.
  8. Run the program. It will remain open in the console, ready to serve requests.
  9. Open your web browser to http://localhost:8085/ to see your page.
  10. Terminate the server by pressing Ctrl + C in the console when you are finished.

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("<", "&lt;").replace(">", "&gt;");
}

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"]

Tip

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.

How it works…

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.

Tip

If you use cgi.request with an enum variable, it will filter input and create a whitelist. The code is as follows:

enum Option { value, option, another }
// this will always be one of the valid options,
// regardless of user input
Option option = cgi.request!Option("option");

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.

See also

  • http://vibed.org/ is another web application framework for D. The 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.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.35.255