CGI

Common Gateway Interface (CGI) is an API that allows programs to respond to HTTP client requests through a web server, as specified in RFC 3875. The protocol was introduced in the 1990s to connect databases and applications to the World Wide Web:

The Common Gateway Interface (CGI) is a standard for interfacing external applications with information servers, such as HTTP or Web servers. A plain HTML document that the Web daemon retrieves is static, which means it exists in a constant state: a text file that doesn’t change. A CGI program, on the other hand, is executed in real-time, so that it can output dynamic information.

– The Common Gateway Interface: Introduction

The web server forwards incoming requests to the designated CGI script or program, and returns the given response to the client. The inter-communication between web server and CGI program is based on standard input/output. Requests are passed through environment variables within the CGI application context (table 1). Consequently, CGI applications can be written in nearly all programming languages.

Variable Description
AUTH_TYPE Authentication method used to validate a visitor.
CONTENT_LENGTH Length of the query data (in bytes or the number of characters) passed to the CGI program through standard input.
CONTENT_TYPE Media type of the query data, such as text/html.
GATEWAY_INTERFACE Revision of the Common Gateway Interface that the server uses.
DOCUMENT_ROOT Root directory of the server.
HTTP_ACCEPT List of the media types that the client can accept.
HTTP_COOKIE Visitor’s cookie, if one is set.
HTTP_REFERER URL of the page that called the CGI program.
HTTP_USER_AGENT Browser type of the visitor.
PATH_INFO Extra path information passed to a CGI program.
PATH_TRANSLATED Translated version of the path given by PATH_INFO.
QUERY_STRING GET query string.
REMOTE_ADDR IP address of the visitor.
REMOTE_HOST Hostname of the visitor (if the server supports reverse name-lookups, otherwise this is the IP address again).
REMOTE_IDENT Visitor making the request.
REMOTE_USER Visitor’s user name (if authenticated).
REQUEST_METHOD Method with which the information request was issued (GET, POST, …).
REQUEST_URI Interpreted path name of the requested document or CGI (relative to the document root).
SCRIPT_NAME Interpreted path name of the current CGI program (relative to the document root).
SERVER_NAME FQDN or IP address of the server.
SERVER_PORT Port number the server is listening on.
SERVER_PROTOCOL Name and revision number of the server protocol.
SERVER_SOFTWARE Name and version of the server software.
Table 2: Common CGI environment variables

Client data is sent using the HTTP methods GET and POST:

GET
The data is given as key-value pairs in environment variable QUERY_STRING. Pairs are separated by an ampersand (&), white space characters are encoded as + or %20.
POST
Any data sent by POST can simply be read from standard input. The size is given in environment variable CONTENT_LENGTH, the MIME type in CONTENT_TYPE.

CGI has the drawback of starting a new process for each HTTP request, and, therefore, producing some overhead related to process management and memory consumption, especially, if the CGI program is written in a scripting language. In the mid-1990s, the FastCGI protocol was released to address these shortcomings by creating persistent processes that handle multiple requests.

Web Server Licence CGI FastCGI SCGI
Althttpd PD ✓ ✓
Apache HTTP Server Apache ✓ ✓
Boa GPL ✓
Cherokee GPL ✓ ✓ ✓
Hiawatha GPL ✓ ✓
lighttpd BSD ✓ ✓ ✓
NaviServer MPL ✓
nginx BSD ✓ ✓
thttpd BSD ✓
Table 2: Free and open-source web server software

A CGI-compatible web server is required to connect Fortran programs to the World Wide Web (table 2).

Web Application

The example CGI application does not depend on any third-party libraries, only the intrinsic subroutine get_environment_variable() (Fortran 2003) is called to read some CGI environment variables.

! webapp.f90
program webapp
    implicit none
    character(len=*), parameter :: CR_LF        = achar(13) // achar(10)
    character(len=*), parameter :: CONTENT_TYPE = 'text/html'
    character(len=*), parameter :: HEADER       = '<!doctype html>' // &
                                                  '<html lang="en">' // &
                                                  '<head><meta charset="utf-8">' // &
                                                  '<title>Fortran + CGI</title></head>' // &
                                                  '<body bgcolor="silver">'
    character(len=*), parameter :: FOOTER       = '</body></html>'

    character(len=72)             :: env
    character(len=:), allocatable :: post_data
    integer                       :: stat, sz

    ! Output HTTP header.
    print '(a)', 'Content-Type: ' // CONTENT_TYPE // CR_LF // CR_LF

    ! Output HTML header.
    print '(a)', HEADER
    print '(a)', '<h1>Hello, from Fortran!</h1>'

    ! Read and output CGI environment variables.
    print '(a)', '<table border="1">'
    print '(a)', '<tr><th>Key</th><th>Value</th></tr>'

    call get_environment_variable('SERVER_SOFTWARE', env)
    print '(a)', '<tr><td>SERVER_SOFTWARE</td><td>' // trim(env) // '</td></tr>'

    call get_environment_variable('GATEWAY_INTERFACE', env)
    print '(a)', '<tr><td>GATEWAY_INTERFACE</td><td>' // trim(env) // '</td></tr>'

    call get_environment_variable('QUERY_STRING', env)
    print '(a)', '<tr><td>QUERY_STRING</td><td>' // trim(env) // '</td></tr>'

    print '(a)', '</table>'

    ! Read and output POST data.
    call get_environment_variable('CONTENT_LENGTH', env)
    read (env, *, iostat=stat) sz

    ! Note: To prevent XSS attacks, all user input should be properly escaped!
    if (stat == 0 .and. sz > 0) then
        allocate (character(len=sz) :: post_data)
        read (*, '(a)', iostat=stat) post_data
        print *, '<pre><code>' // post_data // '</code></pre>'
    end if

    print '(a)', FOOTER
end program webapp

Compile the source code to executable webapp:

$ gfortran14 -o webapp webapp.f90

Then, copy the binary to the cgi-bin/ directory of the web server, in this case, /var/www/cgi-bin/, and make the file executable:

# cp webapp /var/www/cgi-bin/
# chmod u+x /var/www/cgi-bin/webapp
Fortran CGI application
Fig. 1: The output of the CGI application written in Fortran

Server Configuration

We can use any CGI-compatible web server to run the web application, such as thttpd. On FreeBSD, simply install the server as a package:

# pkg install www/thttpd

The web server is configured through file /usr/local/etc/thttpd.conf. The served directory is set to /var/www but can be anywhere on the file system. The CGI path will resolve to /var/www/cgi-bin/:

dir=/var/www
cgipat=/cgi-bin/*
logfile=/var/log/thttpd.log
pidfile=/var/run/thttpd.pid
port=80
user=www
nochroot

Start the web server as a daemon:

# service thttpd onestart
Starting thttpd.

Access http://127.0.0.1/cgi-bin/webapp in a web browser to show the output of the CGI application (fig. 1). We can run cURL to pass data via POST method:

$ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" \
  -d "param1=value" http://127.0.0.1/cgi-bin/webapp

Fortran Libraries

FastCGI Wrappers

References