Guides/SCGI

From J Wiki
Jump to navigation Jump to search

J can be used for SCGI (Simple Common Gateway Interface) programming. Here are some considerations and examples.

Note: the examples assume Windows and Linux, but MacOSX would be similar - feel free to add MacOSX-specific references.


What is SCGI

The SCGI protocol is a replacement for the Common Gateway Interface (CGI) protocol. It is a standard for applications to interface with HTTP servers. Instead of creating a new process for every request, SCGI uses a single persistent process which handles many requests over its lifetime.

There are a number of ways of generating dynamic web content:

1. Classic CGI - where the web server executes an external application which generates the content. A new instance of the program is started for each request. 2. mod_language - where the web server loads a script interpreter (like PHP) into its address space and executes it there. 3. FastCGI / SCGI - where the application runs separately from the web server and the two communicate over TCP.

Visualize two process trees:

httpd (parent)
|
+-- httpd (child)
|
+-- httpd (child)

      :
communication over TCP
      :

scgi_server (parent)
|
+-- scgi_server (child)

Why SCGI?

SCGI is similar to FastCGI but simpler.

Note that SCGI is a protocol. It is not specific to Python or Ruby. You can write an SCGI server in any implementation language, and as long as you stick to the protocol, HTTP server with mod_scgi will be able to talk to it.

A decent SCGI server may implement a pre-forking SCGI server. That is, after initialization, a number of children are forked, and they process incoming requests. Just like the classic Apache worker model.

Some of the benefits of SCGI over classic CGI:

  • The J interpreter is loaded only once on start-up, not once on every request.
  • Likewise, your application is only loaded / parsed once.
  • If your application needs to initialize (load config files, pre-cache things), you can do this once, on start-up, before you call serve()
  • Virtually zero start-up time per request means a huge improvement for request latency.

References:

J Interpreter

For most purposes, the jconsole should suffice for SCGI, but the full J system can also be used if required.

SCGI Server

A SCGI server is a socket server that works togther with HTTP server. You need at least a script with definitions to read the SCGI parameters and create the required SCGI output. What else is needed depends on your application. This can include the J standard libraries.

A minimal SCGI server is fscgidemo.ijs. This is not a standalone script, and can be used for testing only.

For IDE, open and run this script. For jconsole, use dos command or shell to type

./jconsole fscgidemo.ijs

The server does not exit after processing incoming requests, the J session remains alive indefinitely. Only SCGI server running under win32 with c++ frontend can respond to user command. For all other cases, it can be stopped by requesting the url

http://localhost/scgi/bye.ijs

in the browser.

Permissions

A SCGI server runs as a separate process and it can (usually if under Linux) run under a different user id. File permissions should allow the SCGI server to run the J interpreter, and read the scripts. Scripts do not need execution permission.

HTTP Servers that supporting SCGI

Apache Configuration (Win32)

It needs to add information for module mod_scgi.so into the httpd.conf

Edit Apache's config file: conf/httpd.conf

Add this line immediately after the the LoadModule of mod_rewrite (make sure mod_rewrite is uncommented):

LoadModule scgi_module modules/mod_scgi.so

For Apache1 only, add the following immediately after the AddModule of mod_rewrite:

AddModule mod_scgi.c

Apache2 doesn’t use AddModule.

where all the rest of the LoadModules are. And then this at the end:

SCGIMount /scgi 127.0.0.1:9000

In this example, all request to directory /scgi in HTTP server's document root will be routed to SCGI server at IP address 127.0.0.1 port 9000. So that the SCGI server should listen to port 9000 of IP address 127.0.0.1.

There is a SCGIHandler option that coupled with a LocationMatch tag to enable/disable SCGI handling or for virtual hosting.

The hardest part is to get the file mod_scgi.so itself because the mod_scgi module is not come with the Apache offical release. There is pre-built binary from third parties, download it and place the mod_scgi.so in your Apache's modules dir.

References:

Apache Configuration (Linux)

This has been tested on Apache/2.0.55 (Ubuntu).

The file mod_scgi.so is not available from Linux distribution or package manager's repository, so that it has to be built from source.

  • Install development headers for Apache2 from package manager.
  • Download source code from SCGI official website
  • Next save the following (you need sudo to do this.)
LoadModule scgi_module /usr/lib/apache2/modules/mod_scgi.so
SCGIMount /scgi 127.0.0.1:9000
as
/etc/apache2/mods-enabled/scgi.conf
  • Untar SCGI source and cd to its apache2 directory,
$ cd ~/scgi-1.12/apache2
$ make
$ sudo make install
This will compile mod_scgi.so and move it to /usr/lib/apache2/modules/.
  • Finally restart the Apache server.
$ sudo make restart

Lighttpd Configuration

Since SCGI is built-in, you’ll only need to tell Lighttpd how to connect to your SCGI server. All you need to do is point lighttpd at one port.

Linux : edit the file lighttpd.conf (actually I use gedit because I don't know how to use vi)

$ sudo vi /etc/lighttpd/lighttpd.conf

Win32 : use notepad or other text editor to open the file

c:\lighttpd\etc\lighttpd.conf

Make sure you have mod_scgi mentioned in the modules:

## modules to load
# at least mod_access and mod_accesslog should be loaded
# all other module should only be loaded if really neccesary
# - saves some time
# - saves memory
server.modules              = (
#                               "mod_rewrite",
#                               "mod_redirect",
#                               "mod_alias",
                                "mod_access",
#                               "mod_cml",
#                               "mod_trigger_b4_dl",
#                               "mod_auth",
#                               "mod_status",
#                               "mod_setenv",
#                               "mod_fastcgi",
#                               "mod_proxy",
#                               "mod_simple_vhost",
#                               "mod_evhost",
#                               "mod_userdir",
#                               "mod_cgi",
                                "mod_scgi",
#                               "mod_compress",
#                               "mod_ssi",
#                               "mod_usertrack",
#                               "mod_expire",
#                               "mod_secdownload",
#                               "mod_rrdtool",
                                "mod_accesslog" )

Next you have to tell Lighttpd to route requests to SCGI server and not to check for local. Add the following to the config file.

#### scgi module
## read scgi.txt for more info
    scgi.server = ( "/scgi/" =>
      (( "host" => "127.0.0.1",
	 "port" => 9000,
         "check-local" => "disable",
         "docroot" => "/" # remote server may use
	                  # it's own docroot
      ))
    )

Save the configuration and close all the files. Restart the lighttpd:

$ /etc/init.d/lighttpd restart

In this example, all request to directory /scgi in HTTP server's document root will be routed to SCGI server at IP address 127.0.0.1 port 9000. So that the SCGI server should listen to port 9000 of IP address 127.0.0.1.

Lighttpd will not check whether requested files actually exist or not because SCGI may have a different document root or even resides in another computer.

Lighttpd also allow multiple SCGI servers on the same computer or other computers to do load balancing.

Cherokee Configuration

Again SCGI is built-in, it is very simple to connect to a SCGI server.

Linux : edit the file cherokee.conf

$ sudo vi /etc/cherokee/cherokee.conf

Win32 : use notepad or other text editor to open the file

C:\Program Files\Cherokee\cherokee.conf

Add this section at the end:

Directory /scgi {
   Handler scgi {
      Server 127.0.0.1:9000
   }
}

References:

SCGI script

This is an ordinary J script, not a hash bang script since J interpreter is already running. Execution permission is not needed. It can assume neccesary libraries has been loaded by SCGI server.

Simple Example

Save the following as test1.ijs

NB. =========================================================
NB. cgitest v. defines html with a timestamp and cgi parameters
cgitest=: 3 : 0
require 'dates'
stdout 'Status: 200 OK', LF
stdout 'Content-type: text/html',LF,LF,'<html><body><pre>',LF
stdout LF,~'external'
stdout LF,~":6!:0''
stdout ,LF,.~":cgiparms''
stdout ,LF,.~":3 4$calendar {. 6!:0 ''
stdout '

</body></html>'

)

cgitest

to your cgi-bin directory eg.

/home/bill/cgi-bin/test1.ijs

ensure that the SCGI server has read access.

<!> SCGI server (fscgidemo) assume HOMEDIR=: '/home/bill/cgi-bin' your need to change this line and make sure SKPORT=: 9000 before running the demo.

In Linux, the root directory holding HTTP web page files usually be

/var/www

while in Win32, it usually be the sub-directory htdocs under web server's program root.

Next, save the following as file scgitest1.html in your web directory:

<html><body>
<form action="/scgi/test1.ijs" method="get">
<input type=text value="get me" name=tget><br>
<input type="submit" value="Run">
</form></body></html>

also, save the following as file scgitest2.html in your web directory:

<html><body>
<form action="/scgi/test2.ijs" method="post">
<input type=text value="get me" name=tget><br>
<input type="submit" value="Run">
</form></body></html>

Note that the use of POST instead of GET, and there is no test2.ijs, it will be handled internally by SCGI server as a demonstration.

Open these files in your browser, and press the Run button. The resulting display should show the timestamp, and the edit box name and contents:

external
2007 1 5 22 43 12.969
+----+------+
|tget|get me|
+----+------+

If this does not work, you should check that the SCGI server is running, also check the firewall setting and server log.

References


Contributed by Bill Lam