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. In the following example, iso_c_binding
interfaces to the
FastCGI functions are declared in a single Fortran module fcgi
,
with additional wrapper procedures for type conversion and null-termination:
! fcgi.f90
module fcgi
use, intrinsic :: iso_c_binding
implicit none
private
public :: fcgi_accept
public :: fcgi_getchar
public :: fcgi_puts
private :: fcgi_getchar_
private :: fcgi_puts_
interface
! int FCGI_Accept(void)
function fcgi_accept() bind(c, name='FCGI_Accept')
import :: c_int
implicit none
integer(kind=c_int) :: fcgi_accept
end function fcgi_accept
! int FCGI_getchar(void)
function fcgi_getchar_() bind(c, name='FCGI_getchar')
import :: c_int
implicit none
integer(kind=c_int) :: 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
implicit none
character(kind=c_char), intent(in) :: str
integer(kind=c_int) :: fcgi_puts_
end function fcgi_puts_
end interface
contains
character function fcgi_getchar() result(a)
!! Wrapper that returns the result of the interface as character.
a = achar(fcgi_getchar_())
end function fcgi_getchar
subroutine fcgi_puts(str)
!! Null-terminates argument `str` before passing it to the interface.
character(len=*), intent(in) :: str
integer :: stat
stat = fcgi_puts_(str // c_null_char)
end subroutine fcgi_puts
end module fcgi
Compile and archive the Fortran module to static library
libfortran-fcgi.a
:
$ gfortran13 -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.
! webapp.f90
program webapp
use, intrinsic :: iso_c_binding
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 fcgi_puts('Content-Type: ' // CONTENT_TYPE // CR_LF // CR_LF)
! HTML payload.
call fcgi_puts(HEADER)
call fcgi_puts('<h1>Hello, from Fortran!</h1>' // &
'<table border="1">' // &
'<tr><th>Key</th><th>Value</th></tr>')
call get_environment_variable('REQUEST_URI', env)
call fcgi_puts('<tr><td>REQUEST_URI</td><td>' // trim(env) // '</td></tr>')
call get_environment_variable('SERVER_SOFTWARE', env)
call fcgi_puts('<tr><td>SERVER_SOFTWARE</td><td>' // trim(env) // '</td></tr>')
call get_environment_variable('GATEWAY_INTERFACE', env)
call fcgi_puts('<tr><td>GATEWAY_INTERFACE</td><td>' // trim(env) // '</td></tr>')
call fcgi_puts('</table>')
call fcgi_puts(FOOTER)
end do
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
:
$ gfortran13 -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
for them 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 have to change the value to the actual
location. On FreeBSD, start both services by running:
# 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 > |