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:

$ gfortran13 -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