FastCGI
The FastCGI protocol allows the implementation of persistent server-side web applications. In contrast to the older Common Gateway Interface standard, FastCGI provides improved scalability, as processes handle multiple requests over their lifetime. Most modern web servers feature FastCGI passthrough, such as nginx, lighttpd, or Apache HTTP Server.
Requests are passed to the FastCGI application context using environment variables, but interface bindings are required to handle them in Fortran and to return any response. In order to run FastCGI applications written in Fortran, we will need:
- FastCGI-compatible web server,
- FastCGI spawner for program start-up and protocol handling,
- FastCGI Developer’s Kit, and
- Fortran ISO C binding interfaces to FastCGI.
On FreeBSD, install the packages nginx, FastCGI, and spawn-fcgi by running:
# pkg install www/nginx www/fcgi www/spawn-fcgi
The packages should be available on most Linux distributions as well. The
nginx web server is configured through file
/usr/local/etc/nginx/nginx.conf
(FreeBSD) or
/etc/nginx/nginx.conf
(Linux). The FastCGI spawner
spawn-fcgi just uses an rc.d(8) command script for
set-up.
FastCGI and Fortran
In Fortran, interface bindings to a few functions of the FastCGI API in
shared library libfcgi.so
are required to be able to interact with
the web server:
result = fcgi_accept()
- Blocks until the next HTTP request, then creates a new execution environment. The request is passed through environment variables. Returns < 0 on error.
result = fcgi_getchar()
- Returns a single character from content sent by POST method.
result = fcgi_puts(str)
- Appends the given character string to the HTTP response. The character
argument
str
must be null-terminated.
The FLIBS library includes
Fortran 2003 interfaces to the FastCGI functions above, along additional
interfaces, utility routines, and data structures for simplified request
handling. For the following example, we just declare iso_c_binding
interfaces to the FastCGI functions in a single Fortran module
fcgi
:
! fcgi.f90
module fcgi
use, intrinsic :: iso_c_binding, only: c_char, c_int
implicit none
private
public :: fcgi_accept
public :: fcgi_getchar
public :: fcgi_puts
interface
! int FCGI_Accept(void)
function fcgi_accept() bind(c, name='FCGI_Accept')
import :: c_int
integer(kind=c_int) :: fcgi_accept
end function fcgi_accept
! int FCGI_getchar(void)
function fcgi_getchar() bind(c, name='FCGI_getchar')
import :: c_char
character(kind=c_char) :: fcgi_getchar
end function fcgi_getchar
! int FCGI_puts(const char *str)
function fcgi_puts(str) bind(c, name='FCGI_puts')
import :: c_char, c_int
character(kind=c_char), intent(in) :: str(*)
integer(kind=c_int) :: fcgi_puts
end function fcgi_puts
end interface
end module fcgi
Compile and archive the Fortran module to static library
libfortran-fcgi.a
:
$ gfortran12 -c src/fcgi.f90
$ ar crs libfortran-fcgi.a fcgi.o
Any FastCGI application written in Fortran then has to be linked against
libfortran-fcgi.a -lfcgi
to access this subset of the FastCGI API.
However, it is not really necessary to pack the Fortran module fcgi
into a static library. We can instead link our web application just with object
file fcgi.o
.
Web Application
The following FastCGI application in Fortran returns an HTML document that
contains the values of the CGI environment variables REQUEST_URI
,
SERVER_SOFTWARE
, and GATEWAY_INTERFACE
(passed by the
web server) as an HTTP response. The HTML5 template is hard-coded into the
executable, but could possibly be loaded from file instead.
- Fig. 1: The output of the FastCGI application
Once the web application is initialised (not necessary in this example), we
call the blocking FastCGI function fcgi_accept()
to signal that
we are ready to accept a new request from the HTTP server and to create a
CGI-compatible execution environment for the request. If the function returns
−1, the web applications will terminate, which causes the FastCGI spawner
to shutdown the associated process. In this case, the web server returns HTTP
status code 503 (Service Unavailable). The nginx log file
/var/log/nginx/error.log
may give further details.
The
FastCGI parameters
of the request passed by the web server are accessed through the Fortran 2003
routine environment_variable()
, to retrieve parameters such as the
request URI, the IP address of the client, or the root path.
Each HTTP response starts with the specific type of the served content,
followed by two carriage return/line-feed characters (CR_LF
), to
mark the beginning of the payload. Instead of a HyperText document
(text/html
), we could return any other MIME type, like
text/plain
for plain text, image/gif
for GIF, or
application/octet-stream
for a byte stream download. Based on the
MIME type of the response, the client is able to decide how to handle the
returned data stream.
All character arguments given to the C function fcgi_puts()
must be properly null-terminated, using c_null_char
from module
iso_c_binding
, or simply char(0)
.
! webapp.f90
program webapp
use, intrinsic :: iso_c_binding, only: c_null_char
use :: fcgi
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 + FastCGI</title></head>' // &
'<body>'
character(len=*), parameter :: FOOTER = '</body></html>'
character(len=72) :: env
do while (fcgi_accept() >= 0)
! HTTP header.
call response('Content-Type: ' // CONTENT_TYPE // CR_LF // CR_LF)
! HTML payload.
call response(HEADER)
call response('<h1>Hello, from Fortran!</h1>' // &
'<table border="1">' // &
'<tr><th>Key</th><th>Value</th></tr>')
call get_environment_variable('REQUEST_URI', env)
call response('<tr><td>REQUEST_URI</td><td>' // trim(env) // '</td></tr>')
call get_environment_variable('SERVER_SOFTWARE', env)
call response('<tr><td>SERVER_SOFTWARE</td><td>' // trim(env) // '</td></tr>')
call get_environment_variable('GATEWAY_INTERFACE', env)
call response('<tr><td>GATEWAY_INTERFACE</td><td>' // trim(env) // '</td></tr>')
call response('</table>')
call response(FOOTER)
end do
contains
subroutine response(str)
character(len=*), intent(in) :: str
integer :: rc
rc = fcgi_puts(str // c_null_char)
end subroutine response
end program webapp
The web application has to be statically linked against our Fortran interface
bindings in libfortran-fcgi.a
, and dynamically against the FastCGI
SDK with -lfcgi
:
$ gfortran12 -I/usr/local/include/ -L/usr/local/lib/ -o webapp webapp.f90 libfortran-fcgi.a -lfcgi
Copy the FastCGI program webapp
to, for example,
/var/www/cgi-bin/
, and configure the FastCGI spawner accordingly.
Server Configuration
In the next step, we have to configure the nginx web server to redirect all
incoming requests to the FastCGI process. In the nginx configuration file, we
add a FastCGI passthrough to 127.0.0.1
on port 9000 for an
arbitrary location, is this particular case, the root path /
:
user www;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
server {
listen 80;
server_name localhost;
location / {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.html;
include fastcgi_params;
}
location /cgi-bin {
deny all;
}
}
}
By default, the FastCGI spawner spawn-fcgi binds to port 9000. We
have to add web server and spawner to /etc/rc.conf
to be loaded as
system services on FreeBSD:
nginx_enable="YES"
spawn_fcgi_enable="YES"
spawn_fcgi_app="/var/www/cgi-bin/webapp"
The path to the binary webapp
is set in variable
spawn_fcgi_app
. You may want to change the value to the actual
location. On FreeBSD, start both services with:
# service spawn-fcgi start
# service nginx start
Open a web browser and access http://127.0.0.1/
to see the
response of the FastCGI application (fig. 1). The binary
/var/www/cgi-bin/webapp
will be locked by spawn-fcgi.
Therefore, we first have to stop the service to replace the web
application:
# service spawn-fcgi stop
# cp webapp /var/www/cgi-bin/
# service spawn-fcgi start
Fortran Libraries
- FLIBS: A collection of Fortran modules, includes FastCGI bindings
- fortran-machine: FastCGI-based web framework written in modern Fortran
References
- FastCGI Archives: Backup of the official FastCGI website
< CGI | [Index] | nginx > |