nginx

Nginx is an open-soure web server, reverse proxy, load balancer, and web application platform that can be extended by 3rd party modules. OpenResty is web application platform based on nginx and LuaJIT that includes several modules by default.

Installation

On FreeBSD, we can simply build nginx from ports. Select the build option LINK to enable the nginx-link-function module:

# cd /usr/local/ports/www/nginx/
# make config
# make
# make install

Or, if you prefer packages over ports and the chosen package repository provides an nginx package that has been built with LINK, run instead:

# pkg install www/nginx
# pkg info www/nginx | grep LINK

On all other platforms, we just compile nginx from source and include the nginx-link-function module at compile time. At first, we have to download and unpack the source code of the module:

# cd /tmp/
# fetch "https://github.com/Taymindis/nginx-link-function/archive/master.zip"
# unzip master.zip

After the source code of the latest version of nginx (> v1.19.0) has been downloaded, add the nginx-link-function module as an argument to the configure script, and then compile and install nginx. For example:

# fetch "https://nginx.org/download/nginx-VERSION.tar.gz"
# tar xzvf nginx-VERSION.tar.gz
# cd nginx-VERSION/
# ./configure --add-module=/tmp/nginx-link-function-master/
# make -j 2
# make install
# install -m 644 /tmp/nginx-link-function-master/src/ngx_link_func_module.h /usr/local/include/

In the install step, change the path /usr/local/include/ to your system’s directory, such as /usr/include/ (Linux). On FreeBSD, the nginx daemon is started with:

# service nginx start

fortran-nginx

In order to interact with the nginx-link-function module from Fortran, we need to link the fortran-nginx library that provides the requires ISO C binding interfaces. Clone the repository and execute the Makefile:

$ git clone https://github.com/interkosmos/fortran-nginx
$ cd fortran-nginx/
$ make

The Makefile will create a static library libfortran-nginx.a which the web application will be linked against later.

Example

Fortran web application
Fig. 1: The output of the Fortran web application

The example web application library just returns static HTML content. The implementation of the routines ngx_link_func_init_cycle() and ngx_link_func_exit_cycle() is mandatory. All routines callable from outside must have the bind(c) attribute.

! webapp.f90
module webapp
    use, intrinsic :: iso_c_binding
    use :: ngx_link_func
    implicit none
    public :: ngx_hello
    public :: ngx_link_func_exit_cycle
    public :: ngx_link_func_init_cycle

    logical, save :: is_service_on = .false.
contains
    subroutine ngx_hello(ctx) bind(c)
        !! This routine fill be called by nginx, passing in the current request
        !! context.
        character(len=*), parameter :: content = &
            '<!DOCTYPE html>&
            &<html lang="en">&
            &<head>&
            &<meta charset="utf-8">&
            &<title>Fortran Web App</title>&
            &</head>&
            &<body bgcolor="#f5f5dc">&
            &<h1>Hello, from Fortran!</h1></body>&
            &</html>'
        type(ngx_link_func_ctx_t), intent(in) :: ctx

        call ngx_link_func_log_info(ctx, 'Sending response ...' // c_null_char)
        call ngx_link_func_write_resp(ctx, &
                                      int(200, kind=8), &
                                      '200 OK' // c_null_char, &
                                      NGX_LINK_FUNC_CONTENT_TYPE_HTML, &
                                      content // c_null_char, &
                                      int(len(content), kind=8))
    end subroutine ngx_hello

    ! void ngx_link_func_init_cycle(ngx_link_func_cycle_t* cyc)
    subroutine ngx_link_func_init_cycle(cyc) bind(c)
        type(ngx_link_func_cycle_t), intent(in) :: cyc

        call ngx_link_func_cyc_log_info(cyc, 'Starting the web app ...' // c_null_char)
        is_service_on = .true.
    end subroutine ngx_link_func_init_cycle

    ! void ngx_link_func_exit_cycle(ngx_link_func_cycle_t* cyc)
    subroutine ngx_link_func_exit_cycle(cyc) bind(c)
        type(ngx_link_func_cycle_t), intent(in) :: cyc

        call ngx_link_func_cyc_log_info(cyc, 'Shutting down the web app ...' // c_null_char)
        is_service_on = .false.
    end subroutine ngx_link_func_exit_cycle
end module webapp

Compile the shared library webapp.so with:

$ gfortran10 -shared -fPIC -o webapp.so webapp.f90 libfortran-nginx.a

The library now has to be loaded by nginx.

Server Configuration

In the nginx configuration file /usr/local/etc/nginx/nginx.conf (FreeBSD), we simply set ngx_link_func_lib to the path of our web application, and add a route that invokes ngx_link_func_call to call the routine ngx_hello inside the shared library webapp.so:

# Load shared library if module is not linked statically:
load_module "/usr/local/libexec/nginx/ngx_http_link_func_module.so";

http {
    server {
        listen            80;
        server_name       localhost;
        ngx_link_func_lib "/usr/local/etc/nginx/webapp.so";

        location = / {
            ngx_link_func_call "ngx_hello";
        }
    }
}

We are now ready to start the nginx daemon. On FreeBSD, run:

# service nginx restart

Open the URL http://127.0.0.1/ inside your web browser to access the web application (fig. 1).

References