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. Consequently, CGI applications can be written in nearly all programming languages.

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.

A CGI-compatible web server, such as thttpd, Hiawatha, or lighttpd, is required to connect Fortran programs to the World Wide Web. On FreeBSD, we can install thttpd simply as a package:

# pkg install www/thttpd
Fortran CGI application
Fig. 1: The output of the CGI application written in Fortran

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
    integer                       :: sz, rc

    ! 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"><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=rc) sz

    ! Note: content is not properly encoded!
    if (rc == 0 .and. sz > 0) then
        allocate (character(len=sz) :: post)
        read (*, '(a)', iostat=rc) post
        print *, '<pre><code>' // post // '</code></pre>'
    end if

    print '(a)', FOOTER
end program webapp

Compile the source code to executable webapp:

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

Server Configuration

On FreeBSD, the thttpd 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 to 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

Server Software

References