WIP!
Most of this site is incomplete, and the current state is available as an open draft. Most of the text here is likely incomplete, misinformed, or just plain wrong. I'm looking for feedback on my website, so that I can:
- Fill in what I'm missing
- Take out what's unnecessary
- Figure out my target audience
- Find the right way to structure the site
- Filter out any errors
To anyone who wants to send me feedback, thank you, and shoot me an email!
Interactive web applications need to be more dynamic than a static website. CGI is an interface for spawning a process for each request that a web server receives. FastCGI is an efficiency improvement on CGI, which tells a web server to communicate with a persistent process to remove the overhead of building up and tearing down a new process for each request.
OpenBSD provides support for both types.
Comparison
There a few differences between the two specifications:
Classical CGI
| FastCGI
|
Strictly speaking, Httpd provides only FastCGI support. To run classical CGI applications, OpenBSD ships with slowcgi to translate one to the other.
Create a Web App with classical CGI
CGI programs can be written in any programming language. Since slowcgi is chrooted into /var/www by default, it can’t access files anywhere else – so let’s make the first application in C for now. In your home directory, write down in a file named hello.c:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
// Return a pointer to the first occurrence of any character in needles.
// If there are no occurrences, return NULL.
static const char *
strchrs(const char haystack[], const char needles[])
{
while (haystack[0] != '\0') {
if (strchr(needles, haystack[0]))
return haystack;
haystack++;
}
return NULL;
}
// Naive HTML sanitizer.
// Write unsanitized input as escaped HTML output to stdout.
static void
write_escaped(const char string[])
{
fflush(stdout);
const char *special;
while (special = strchrs(string, "<>&\""), special != NULL) {
// Write every character up to the next special character as-is.
write(STDOUT_FILENO, string, special - string);
// Write the special character as a Character Entity.
switch (*special) {
case '<':
write(STDOUT_FILENO, "<", 4);
break;
case '>':
write(STDOUT_FILENO, ">", 4);
break;
case '&':
write(STDOUT_FILENO, "&", 5);
break;
case '"':
write(STDOUT_FILENO, """, 6);
break;
}
string = special + 1;
}
puts(string);
}
int
main(int argc, char *argv[], char *envp[])
{
// An HTTP response begins with a status, and a list of headers. Our hello world
// will be an HTML document, so inform the client of that content type.
printf("Status: 200 OK\r\n");
printf("Content-Type: text/html\r\n");
printf("\r\n");
// Now, the response body -- the HTML document.
// Let's list every environment variable that Httpd gives us:
puts("<h1>Hello, world!</h1>");
puts("<ul>");
for (int i = 0; envp[i] != NULL; i++) {
puts("<li><code>");
write_escaped(envp[i]);
puts("</code></li>");
}
puts("</ul>");
return 0;
}
Then we can compile the program. Usually, C programs are dynamically linked,
pointing to shared libraries located in /usr/lib.
slowcgi isn’t able to access that by default, so we need to put in -static
to specify we want everything bundled in one
executable:
# The directory /var/www/cgi-bin is a conventional place to store CGI scripts.
# The BSD application bgplg already lives here, though its file permissions
# have effectively disabled it by default.
$ cc hello.c -static -o /var/www/cgi-bin/hello.cgi
Configure Httpd for classical CGI
Within a web server, add a directive to active Fast CGI for the URI path /cgi-bin/*
:
server "www.example.org" {
…
location "/cgi-bin/*" {
# By default, Httpd looks for the socket that slowcgi will open up.
fastcgi
# Httpd will send the path "/var/www/cgi-bin/*" to whatever FastCGI
# application is listening.
root "/"
}
}
Reload Httpd rules:
$ doas rcctl reload httpd
Enable the application
Finally, enable slowcgi to start the application:
$ doas rcctl enable slowcgi
$ doas rcctl start slowcgi
And that’s it!
Going to http://www.example.org/cgi-bin/hello.cgi should look something like this:

There’s a few queries you can experiment with to see how the variables change:
- See how /cgi-bin/hello.cgi?search=foo changes the QUERY_STRING variable
- See how /cgi-bin/hello.cgi/path/to/foo changes to PATH_INFO and DOCUMENT_URI variables
Create a Web App with FastCGI
OpenBSD’s slowcgi is only intended for applications that don’t have FastCGI capability. Let’s build a web app that is capable of this.
Install and Use Go
Since the classical CGI example used C, let’s switch gears and use Go instead. Install Go if you haven’t already:
$ doas pkg_add go
Here’s a quick dependency-free Go program named headers-app.go, that lists all the request headers. It runs persistently and listens for a FastCGI connection on port 9000:
package main
import (
"html"
"io"
"log"
"net"
"net/http"
"net/http/fcgi"
)
type HeadersApp struct{}
// ServeHTTP holds the responsibility of handling an individual request.
func (app HeadersApp) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "text/html")
io.WriteString(w, "<h1>Hello, world!</h1>")
// Go translates all FastCGI parameters into a Request structure.
io.WriteString(w, "<h2>Headers:</h2>")
io.WriteString(w, "<ul>")
for header,values := range r.Header {
for i := range values {
io.WriteString(w, "<li><code>"+header+"="+
html.EscapeString(values[i])+"</code></li>")
}
}
io.WriteString(w, "</ul>")
}
func main() {
var l net.Listener
var err error
// Create a socket on localhost at port 9000
l, err = net.Listen("tcp", "localhost:9000")
if err != nil {
log.Fatal(err)
}
// Accept incoming connections to handle requests.
log.Print("Serving FastCGI")
err = fcgi.Serve(l, HeadersApp{})
if err != nil {
log.Fatal(err)
}
}
Compile the Go program:
$ go build headers-app.go
Configure Httpd for FastCGI
Instead of defaulting to the slowcgi Unix socket, let’s listen at the root directory and to connect to our Go program:
server "www.example.org" {
listen on egress port http
fastcgi socket tcp localhost 9000
}
Restart Httpd and run the Go program:
$ doas rcctl restart httpd
$ ./headers-app
And you should start seeing a webpage at https://www.example.org/.
External Links
- slowcgi(8)
- kcgi - a C library for CGI and FastCGI applications