David Beazley (dave@dabeaz.com)Copyright (C) 2005-2007
Sotiria Lampoudi (slampoud@cs.uchicago.edu)
Gonzalo Diethelm (gonzo@diethelm.org)
Copyright (C) 1999-2005
The University of Chicago
You can obtain the latest version of SWILL from here.
SWILL (Simple Web Interface Link Library) is a programming library that makes it relatively easy to add a web interface to programs written in C and C++. It does this by allowing an application to function as a simple web-server to which users can connect using a browser.
The primary use of SWILL is in highly specialized applications that might benefit from the presence of a web interface, but which don't fit into the standard model of a web-server or a typical internet application. For example, consider a long running scientific simulation that consumes hundreds of hours of CPU time as a non-interactive batch job. When running such a simulation, it is often useful to obtain intermediate results so that scientists can monitor its progress. Although it is possible to log into the machine to examine log files and intermediate data files, this is often inconvenient. If, on the other hand, the simulation provided a tiny web-server, a scientist could directly connect to their application to obtain intermediate results all while kicking back at home, drinking a brewski, and watching their WebTV. That would be pretty cool. Of course, this obviously wouldn't apply to all scientists.
Specialized web servers can also be useful in other settings -- especially as a diagnostics and debugging tool. For instance, a compiler might use a little server to allow browsing of internal data structure such as a parse trees (using the browser as a sort of cheap debugging interface). A web interface can also be a useful way to interact with a very complicated runtime environment. For example, in a simulated hardware environment (as might be used for teaching operating systems), a web interface could be used to enable different modes of operation, to selectively control different features of the hardware, or to browse internal data structures. Similarly, small servers can be written to provide simple network services or tools for managing collections of machines. In fact, there are countless other applications that might benefit from having a simple web interface -- most of which are legal.
Why?
In age of freely available web-servers, application servers, and distributed component frameworks, you might ask why a library like SWILL is even needed. Well, the simple answer to that question is that these other approaches are often too bloated and excessively complicated to be used in the small. If you're writing an application and you wanted to slap a web interface on it, why would you want to link it into some enormous internet application framework? SWILL is an alternative that makes a lot of sense for many types of projects.
A Brief History
Bits and pieces of SWILL have actually been in development since 1996. The origins are found in the Theoretical Physics Division at Los Alamos National Laboratory where a Python-based web browser was used to add a remote simulation monitoring capability to molecular dynamics software running on the Connection Machine 5. This proved to be rather useful to the users.
The current implementation is the result of an effort to generalize the embedded server idea into a programming library that could be used with a wide variety of different programs. The primary focus has been to make something that was small, easy to use, and could be used with minimal conceptual overhead. We really wanted to have a server that was completely self-contained and which didn't require any supporting infrastructure to make it work.
Between 2005 and 2007, SWILL was ported to compile natively under Win32 using Microsoft Visual C, and support was added for SSL (using OpenSSL) under all tested platforms.
Prerequisites
To get the most out of SWILL, you should have an understanding of basic Web concepts such as HTML, forms, and possibly CGI scripting. SWILL does not attempt to hide the details of HTML for you -- it's really only concerned with making it easy for an application to serve dynamically generated documents through a web interface. For instance, you could also make it deliver XML if you were so inclined.
What's with the name "SWILL"?
Well, a lot of people working on Internet software sure seem to take themselves far too seriously. Enough said. Besides, what else would you want to serve?
For those who don't know the meaning of this word, the following definitions were taken from The Free Dictionary:
- A mixture of liquid and solid food, such as table scraps, fed to animals, especially pigs; slop.
- Kitchen waste; garbage.
- A deep draft of liquor.
- Nonsense; rubbish.
How is this related to "the Grid"?
It's not. Now, let's continue...
At its core, SWILL is nothing more than a simple embedded web server. This server provides the following set of features:
Unlike a traditional web-server, SWILL is designed to be added to existing programs. For this reason, it is not designed to take over your whole application nor does it rely on programming techniques that would alter the execution model of your program. Instead, it is primarily designed as an add-on.
Because of the lack of concurrency (threading), SWILL is not intended to be a replacement for a high-traffic web server, nor was SWILL created for the purpose of creating new "internet" applications -- if you would want to do that, you might have better luck using Apache and PHP. Sorry.
Here is a very simple SWILL example:
#include "swill.h" void count_to_ten(FILE *f) { int i; for (i = 0; i < 10; i++) { fprintf(f,"%d\n", i); } } int main() { swill_init(8080); swill_handle("ten.txt", count_to_ten, 0); swill_file("index.html",0); while (1) { swill_serve(); } }
In this example, the function swill_init() is used to open a port for the server. The function swill_handle() registers a C function with the server under the name ten.txt. The function swill_file() registers a file with the server.
After some initial setup, the program enters an infinite loop and simply calls swill_serve() to wait for a single request. At this point, the server is active.
If a user uses a browser to connect to the server on port 8080 (using a URL such as http://www.foo.com:8080/), they will get a server response. A request for ten.txt will invoke the function count_to_ten() and the resulting output will show up as the result. Similarly, a request for index.html will result in that file being returned. All other requests simply generate an error message. In addition to this, a request for the special file info will return a list of all registered handlers and documents.
In many cases, this use of SWILL is not exactly what you want. This is because the swill_serve() function blocks until a request is actually received -- preventing the application from doing useful work in the meantime. To fix this, it is more common to use swill_poll() in some kind of processing loop like this:
#include "swill.h" void count_to_ten(FILE *f) { int i; for (i = 0; i < 10; i++) { fprintf(f,"%d\n", i); } } int main() { swill_init(8080); swill_handle("ten.txt", count_to_ten, 0); swill_file("index.html",0); for (i = 0; i < nsteps; i++) { swill_poll(); /* Check for an incoming request */ ... ... other useful work ... } }
Typically, you would insert a swill_poll() operation in places where it made sense to temporary stop execution and service an incoming web request. This would depend on the underlying application and performance issues might dictate the precise manner in which this is done. For example, you would probably not check for requests in the middle of a performance critical inner loop. On the other hand, it might make sense to check every few seconds or so.
In this example, it is important to note that SWILL is not in charge of application control. In fact, the web server is inactive except at times when the services of SWILL are explicitly requested using swill_serve() or swill_poll(). This differs from a traditional web server or application framework -- reflecting the fact that SWILL is only an extension of your original application.
Finally, it is interesting to note that SWILL does not rely upon any special I/O functions for producing web pages. For example, the function count_to_ten() simply outputs data to a standard file using fprintf(). Thus, it is easy to use such functions even when the web server is not activated. Alternatively, any function that might produce I/O in an application can be used to produce web pages.
In the previous example, a function was used to produce simple text. If you are up on your HTML, functions can also produce HTML documents and images. Here is an example that displays PNG images of the Mandelbrot set using the GD graphics library. In addition, the example uses an HTML form to allow users to change image properties.
/* SWILL Mandelbrot example */ #include "swill.h" #include "gd.h" /* Structure containing plot data */ typedef struct { double Xmin; double Xmax; double Ymin; double Ymax; int Tolerance; gdImagePtr im; } MandelData; /* Handler function that draws an image */ void mandel(FILE *f, MandelData *m) { double scalingx; double scalingy; double zr,zi,ztr,zti,cr,ci; double cscale; int i,j,n; scalingx = (m->Xmax-m->Xmin)/m->im->sx; scalingy = (m->Ymax-m->Ymin)/m->im->sy; cscale = 256.0/m->Tolerance; for (i = 0; i < m->im->sx; i++) { for (j = 0; j < m->im->sy; j++) { zr = scalingx*i + m->Xmin; zi = scalingy*j + m->Ymin; cr = zr; ci = zi; n = 0; while (n < m->Tolerance) { ztr = zr*zr-zi*zi + cr; zti = 2*zr*zi + ci; zr = ztr; zi = zti; if (ztr*ztr + zti*zti > 20) break; n = n + 1; } if (n >= m->Tolerance) gdImageSetPixel(m->im,i,j,0); else gdImageSetPixel(m->im,i,j,(int) (n*cscale)); } } gdImagePng(m->im,f); } /* Handler that produces HTML form for changing values */ void mandelpage(FILE *f, MandelData *m) { double xmin, xmax, ymin, ymax; double xshift,yshift; int tol; fprintf(f,"<HTML><BODY BGCOLOR=\"#ffffff\">\n"); if (!swill_getargs("d(xmin)d(xmax)d(ymin)d(ymax)i(tol)", &xmin,&xmax,&ymin,&ymax,&tol)) { fprintf(f,"<b>Missing form variable!</b>\n"); } else { m->Xmin = xmin; m->Xmax = xmax; m->Ymin = ymin; m->Ymax = ymax; m->Tolerance = tol; } /* Link to image picture */ fprintf(f,"<p><center><img src=\"mandel.png\"></center>\n"); xshift = (m->Xmax - m->Xmin)/4; yshift = (m->Ymax - m->Ymin)/4; /* Make links for left/right/up/down/zoom in/zoom out */ fprintf(f,"<center><p>\n"); fprintf(f,"<a href=\""); swill_printurl(f,"mandelpage.html","d(xmin)d(xmax)d(ymin)d(ymax)i(tol)", m->Xmin-xshift,m->Xmax-xshift,m->Ymin,m->Ymax,m->Tolerance); fprintf(f,"\">[ Left ]</a>"); fprintf(f,"<a href=\""); swill_printurl(f,"mandelpage.html","d(xmin)d(xmax)d(ymin)d(ymax)i(tol)", m->Xmin+xshift,m->Xmax+xshift,m->Ymin,m->Ymax,m->Tolerance); fprintf(f,"\">[ Right ]</a>"); fprintf(f,"<a href=\""); swill_printurl(f,"mandelpage.html","d(xmin)d(xmax)d(ymin)d(ymax)i(tol)", m->Xmin,m->Xmax,m->Ymin-yshift,m->Ymax-yshift,m->Tolerance); fprintf(f,"\">[ Up ]</a>"); fprintf(f,"<a href=\""); swill_printurl(f,"mandelpage.html","d(xmin)d(xmax)d(ymin)d(ymax)i(tol)", m->Xmin,m->Xmax,m->Ymin+yshift,m->Ymax+yshift,m->Tolerance); fprintf(f,"\">[ Down ]</a>"); fprintf(f,"<a href=\""); swill_printurl(f,"mandelpage.html","d(xmin)d(xmax)d(ymin)d(ymax)i(tol)", m->Xmin+xshift,m->Xmax-xshift,m->Ymin+yshift,m->Ymax-yshift,m->Tolerance); fprintf(f,"\">[ Zoom in ]</a>"); fprintf(f,"<a href=\""); swill_printurl(f,"mandelpage.html","d(xmin)d(xmax)d(ymin)d(ymax)i(tol)", m->Xmin-xshift,m->Xmax+xshift,m->Ymin-yshift,m->Ymax+yshift,m->Tolerance); fprintf(f,"\">[ Zoom out ]</a>"); fprintf(f,"</center>\n"); /* Form to change values manually */ fprintf(f,"<p><form action=\"mandelpage.html\" method=GET>\n"); fprintf(f,"Xmin : <input type=text name=xmin width=10 value=\"%g\"></input><br>\n", m->Xmin); fprintf(f,"Xmax : <input type=text name=xmax width=10 value=\"%g\"></input><br>\n", m->Xmax); fprintf(f,"Ymin : <input type=text name=ymin width=10 value=\"%g\"></input><br>\n", m->Ymin); fprintf(f,"Ymax : <input type=text name=ymax width=10 value=\"%g\"></input><br>\n", m->Ymax); fprintf(f,"Tolerance : <input type=text name=tol width=10 value=\"%d\"></input><br>\n", m->Tolerance); fprintf(f,"<input type=submit value=\"Submit\"></input>\n"); fprintf(f,"</form>\n"); fprintf(f,"</body></html>\n"); } int main(int argc, char **argv) { int i; MandelData *m; m = (MandelData *) malloc(sizeof(MandelData)); m->Xmin = -2.0; m->Xmax = 2.0; m->Ymin = -2.0; m->Ymax = 2.0; m->Tolerance = 50; m->im = gdImageCreate(400,400); /* Allocate colormap */ for (i = 0; i < 256; i++) { gdImageColorAllocate(m->im,i/2,i,i); } swill_init(8080); swill_handle("mandel.png", mandel, m); swill_handle("mandelpage.html", mandelpage,m); while (1) { swill_serve(); } }
The example consists of two separate web pages. The function mandel() produces a PNG image of the Mandelbrot set and the function mandelpage() produces an HTML page that includes an inline version of the image as well as some links and a form for changing the display. Although this is a larger example, here are the highlights:
To better understand how SWILL works and its programming API, it is useful to have a basic understanding of the HTTP/1.0 protocol. First, in order to deliver web pages, some kind of server has to be listening for incoming connections on a TCP port. Normally, web servers use port 80. However, since port 80 is priviledged and may already be in use, you will have to pick some other port number. Typically you have to pick a number greater than 1024. For instance, the previous examples used port 8080.
When a browser submits a request, it is sent as a simple text message like this:
GET /foo.html HTTP/1.0 Connection: Keep-Alive User-Agent: Mozilla/4.73 [en] (X11; U; SunOS 5.8 sun4u) Pragma: no-cache Host: schlitz:8080 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */* Accept-Encoding: gzip Accept-Language: en Accept-Charset: iso-8859-1,*,utf-8
The first line of the request indicates the name of the document. Subsequent lines are known as HTTP headers and contain various information about the browser and the remote client. The request is terminated by a blank line (two newline characters). For most practical purposes, the only critical part of the request is the document name such as /foo.html.
In addition to requesting a document, an HTTP request may also include extra parameters known as query variables. These are usually attached to the URL (document name). For example, this request
includes two query variables name and pt. More often than not, query variables show up when you start using HTML forms. However, they may be manually attached to any URL that you might include in an HTML document. Also, this string of query values (after the ?) is sometimes called a query string.GET /foo.html?name=Dave&pt=1234 HTTP/1.0 Connection: Keep-Alive User-Agent: Mozilla/4.73 [en] (X11; U; SunOS 5.8 sun4u) Pragma: no-cache ...
After a request has been received, the server sends back a response. A typical response looks like this:
The first line of the response is a response code. After the code, a series of headers appear. These give information about the server and the document itself (file type, length, etc.). The headers are terminated by a blank line. After that, you find the raw document data.HTTP/1.0 200 OK Content-type: text/html Content-length: 12734 <html> ... rest of document ...
That's about all there is to the basic HTTP protocol. As one might expect, there are more advanced aspects of the protocol related to caching, security, and so forth. However, it's really not necessary to worry about this too much.
The following functions are used to control the server.
int swill_init(int port)
Initializes the server and makes it start listening on the TCP port specified in port. If port is set to zero, the server will simply pick any available port. Returns the port number on success or zero on failure. Only one server may be active at any given time. Calling swill_init() more than once will generate an assertion error.
void swill_shutdown()
Shuts down the server created using swill_init().
char *swill_title(const char *title);
Sets the title string used on various SWILL-generated pages. This is optional, but typically you would use this function to supply the name of your application. The function always returns the current title string. If the title argument is NULL, the current title is returned.
void swill_log(FILE *log)
Supplies a file that should be used for logging web connections. log can be any file opened for writing with fopen() or a related library function.
int swill_serve()
Waits for a single incoming web request. The calling process is blocked until an incoming connection is actually received. The return code is currently undefined. This function returns after any request is received. To serve multiple requests, repeated calls must be made.
int swill_poll()
Polls for an incoming connection. If a connection is found, the resulting request is serviced. Returns 0 if no connection was found, 1 if a request was serviced.
typedef void SwillHandler(FILE *out, void *clientdata)
To register a handler with the server, the special swill_handle() function is used.
int swill_handle(const char *url, SwillHandler *handler, void *clientdata)
Registers a new page with the web server. url is the name of the document that must be supplied by the user in the browser. handler is a callback function that will be invoked when the web page is accessed. clientdata is a user-supplied pointer that is passed when calling the handler function.
When a document is registered with the server, the supplied url is used to control a few different properties of the server. First, the document suffix implicitly determines the document type. Currently SWILL recognizes the following file types:
file.txt Plaintext file (text/plain) file.htm HTML document (text/html) file.html HTML document (text/html) file.gif GIF image (image/gif) file.png PNG image (image/png) file.jpg JPEG image (image/jpg) file.jpeg JPEG image (image/jpg)
If no file suffix is supplied, the file type is set to plaintext.
If the URL supplied to swill_handle() is prefixed with the special string "stdout:", SWILL captures all output printed to stdout while executing the handler function and returns it as part of the web page. For example:
void foo() { int i; for (i = 0; i < 10; i++) { printf("%d\n", i); } } int main() { ... swill_handle("stdout:foo.txt", foo, 0); ... }
The capturing of stdout also works in applications where you might want to capture the output of a system command. For example:
void getdir() { system("ls -l"); } ... int main() { ... swill_handle("stdout:getdir", getdir, 0); ... }
Using system() you could implement your own CGI mechanism. However, be aware that executing system commands is also a significant security risk -- make sure you never pass any sort of user input to such an operation (otherwise the user might be able to execute arbitrary shell commands).
The clientdata argument of handler functions is used to pass user-defined data. This would be used if you wanted to pass an object to a handler function. For example, suppose your program had the ability to make scientific plots and you wanted to provide a variety of web-pages. To do this, you might write some code that looked like this:
void make_plot(FILE *f, Plot3D *plot) { ... make_plot(plot); write_image(plot,f); ... } int main() { ... Plot3D *kinetic = KineticEnergyPlot(); Plot3D *velocity = VelocityPlot(); ... swill_handle("ke.gif", make_plot, kinetic); swill_handle("vel.gif", make_plot, velocity); ... }
If necessary, SWILL can be programmed to return existing files. The following functions can be used:
swill_file(const char *url, const char *filename)
Adds a single file to the server. url is the name of the document in HTTP requests. filename is a pathname that is relative to the current working directory of the program. If filename is NULL, then url is used for the filename.
swill_directory(const char *dirname)
Sets the document root directory to dirname. If this function is used, it allows SWILL to serve an entire directory of files. Whenever an incoming request is received and the request doesn't match the name of an existing handler or file, this directory is searched. Only one directory can be registered with the server at any given time. Furthermore, to prevent abuse, certain pathname restrictions are enforced. Specifically, web requests involving paths with special pathname components of "." and ".." are handled as errors.
In many applications, it is useful to be able to receive variables that were supplied on HTML forms. For example, you might have a web page that asks a user to input some values such as this:
<form action="gcd.txt" method=GET> X : <input type=text name=x width=5></input><br> Y : <input type=text name=y width=5></input><br> Submit : <input type=submit></input> </form>
When the form is submitted, values for x and y will be supplied along with the request. To receive these variables in a handler function, the following functions can be used:
char *swill_getvar(const char *name)
Returns a HTTP variable name as a string. If no such variable exists, returns NULL.
int swill_getargs(const char *fmt, ...)
Parses and returns a collection of form variables. fmt is a special format string that specifies the names and types of the arguments. Returns 0 on failure, 1 on success.
The swill_getargs() function is more powerful and is used as follows:
void gcd(FILE *f) { int x, y; if (!swill_getargs("i(x)i(y)",&x,&y)) { fprintf(f,"Error. Please supply x and y.\n"); } else { /* Compute gcd */ ... } }
In the example, the format string "i(x)i(y)" specifies two integer values named x and y respectively. The following format codes are currently recognized:
fmt code C datatype ------------ --------------------------------- i(name) signed int l(name) signed long h(name) signed short b(name) signed char (as a number) I(name) unsigned int L(name) unsigned long H(name) unsigned short B(name) unsigned char (as a number) f(name) float d(name) double s(name) char * p(name) void * (from an unsigned int)
In addition, a vertical bar "|" can be used in the format string to indicate the start of optional arguments.
Here is a more complicated example:
char *name; char *email; char *address; int age; ... if (!swill_getargs("s(name)s(email)s(address)|i(age)", &name, &email, &address, &age)) { fprintf(f,"Error. Missing arguments.\n"); } else { /* Whatever */ ... }
Normally, SWILL output is produced using standard functions such as printf() or fprintf(). However, SWILL also provides its own implementation of these functions:
int swill_printf(const char *fmt, ...)
int swill_fprintf(FILE *f, const char *fmt, ...)
int swill_vprintf(const char *fmt, va_list ap)
int swill_vfprintf(const char *fmt, va_list ap)
The operation of these functions is identical to their counterparts in the C library. However, there is one minor enhancement. Specifically, the SWILL I/O functions can optionally accept an encoding parameter. This is sometimes useful when producing HTML code from a handler function. For example:
swill_fprintf(f,"foo.html?name=%(url)s&address=%(url)s", name, address);
Notice the use of the special %(url)s format specifier. When used, the resulting output is formatted to produce a proper URL encoding. For example, a string such as "Brian Ward" will be turned into "Brian+Ward" and special characters will be converted into escape codes.
Currently, two encoding schemes are available:
%(url)
Formats text so that it is safe to include in a URL as might appear as part of a hyperlink in an HTML document. Spaces are replaced by "+" and special symbols are replaced by escape codes.
%(pre)
Formats text so that it is safe to include in an HTML preformatted text section (<pre>). Special characters such as <, >, and & are replaced by HTML sequences <, >, and & respectively.
Sometimes, you might want to generate a hyperlink to a URL along with optional arguments. To do this, the following function can be used:
void swill_printurl(FILE *f, const char *url, const char *fmt, ...)
Produce a URL of the form "url?args". url is the URL to use in the link. fmt is a argument format string that is identical to that used by swill_getargs().
Here is an example of using swill_printurl():
char *name = "John Doe"; char *email = "john@doe.gov"; int age = 13; ... fprintf(f,"<a href=\""); swill_printurl(f,"profile.html","s(name)s(email)i(age)", name, email, age); fprintf(f,"\">User profile");
It might come as no great surprise that there is some relationship between swill_getargs() and swill_printurl(). One function is used to receive HTTP arguments whereas the other function is used to print URLs that contain arguments. If you're clever, you can use this to pass data between web pages or as a simple mechanism for remote procedure call.
When a log file has been supplied to SWILL, output can be generated using the following function:
int swill_logprintf(const char *fmt, ...);
Produces output in the SWILL log file (if any). If no file was first supplied using swill_log(), this function does nothing.
In certain cases, you might want to retrieve or set HTTP headers on each request. For instance, when a server receives a request, it typically looks like this:
GET /foo.html HTTP/1.0 Connection: Keep-Alive User-Agent: Mozilla/4.73 [en] (X11; U; SunOS 5.8 sun4u) Pragma: no-cache Host: schlitz:8080 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */* Accept-Encoding: gzip Accept-Language: en Accept-Charset: iso-8859-1,*,utf-8
The headers are the lines following the initial request lines. To retrieve a header, use the following function:
char *swill_getheader(const char *name)Returns HTTP header name. If the header doesn't exist, returns NULL. name is case-insensitive.
Here is an example:
char *host = swill_getheader("host"); char *useragent = swill_getheader("user-agent");
To set a header in the outgoing data, use the following:
void swill_setheader(const char *name, const char *value)
Sets an HTTP header value in the outgoing data. No interpretation of name or value is made. This simply produces an output string of the form "name: value" in the header section of the HTTP response.
Under normal use, it is probably not necessary to read or set the HTTP headers. However, this capability is provided for the sake of power users.
If you ever need to change the HTTP response code, use the following function:
void swill_setresponse(const char *resp)
Sets the HTTP response code. resp is a string such as "404 File not found" or "501 Not implemented". Under normal conditions, you probably do not need to use this function.
SWILL provides support for both HTTP basic authentication and IP filtering.
Basic authentication is set using the following function:
void swill_user(const char *username, const char *passwd)
Enables basic authorization. Once called, all subsequent accesses will require a username and password. May be called more than once to register different users. In basic authorization, the user name and password are essentially sent in clear-text in HTTP requests. Therefore, you should not use a sensitive password or your login password with this function.
IP filters can be set using the following functions:
void swill_deny(const char *ip)
Adds an IP address to the deny list. If an incoming connection is received from this address, the network connection is immediately closed.
void swill_allow(const char *ip)
Adds an IP address to the allow list. If an address is allowed, it takes precedence over any address that might be denied using swill_deny(). Also, calling swill_allow() implicitly causes swill to deny all accesses except for those supplied to this function.
The IP filters use substring matching on the dotted-numerical representation of an IP address. Here are some examples:
/* Only allow connections from 128.135.11.44 */ swill_allow("128.135.11.44"); /* Allow connections from a subnet */ swill_allow("128.135.11."); /* Deny connections from a bad machine */ swill_deny("128.135.11.13"); /* Deny connections from a subnet */ swill_deny("128.135.4."); /* Deny connections from everywhere */ swill_deny("");
Since SWILL is a single-threaded web-server, a number of mechanisms are in place to help prevent reliability problems. First, SWILL will automatically close a connection whenever requests appear to be malformed or suspicious. Second, timeouts are used to help prevent lock-ups due to unresponsive clients or clients that open connections, but which don't send any data. This timeout is set to 10 seconds by default. However, it can be changed using the following function:
swill_timeout(int seconds)
Set the timeout tolerance for data transmission. Read/write operations that take longer than this time will result in a termination of the connection.
SWILL provides support for SSL through the use of OpenSSL, under all platforms supported. Of course, you are responsible for properly installing OpenSSL in a way that your environment can find all required files (headers, libraries, etc.), both at compilation and run time, as well as creating any certificates required for proper OpenSSL operation (the explanation of which is beyond this document).
The SSL support is provided through these functions:
int swill_init_ssl(int port, int ssl, const char* tmpdir)
This is the same as the old style swill_init(int port) but with two additional parameters:The old style swill_init(port) is now a preprocessor define that calls swill_init_ssl(port, 0, 0).
- ssl: set to 1 if you want to use SSL, 0 otherwise.
- tmpdir: specify a path for temporary files to be created, or 0 to use the system default path.
int swill_ssl_set_certfile(const char* crtfile, const char* keyfile, swill_ssl_pwd_getter pcb)
This call tells SWILL where your certificate file and its corresponding key file are located, and provides a callback function that will be called to retrieve the pass phrase for the key; this function must have the prototype int swill_ssl_pwd_getter(char* buf, int maxlen), receiving a preallocated buffer buf with a maximum length of maxlen, which should place the pass phrase into buf and return its actual length. An example of such a function could be:
static int pwd_getter(char* buf, int maxlen) { strcpy(buf, "NOT-SO-VERY-SECRET"); return strlen(buf); }
Using SWILL to add a webserver to an MPI program should not be difficult. That being said, SWILL has been designed with a certain execution model in mind (SPMD style programs, to be precise), and it may take slightly more to make it work with a parallel application that is doing funny things. The most important rule of thumb is this: most swill_*() calls are made within a global context. That is to say, they should either be in a part of the code that is not rank-dependent, or they should be duplicated across rank-dependent parts of the code so that they are called by everyone.
It is probably easier to explain this using a very simple example:
#include "mpi.h" #include "swill.h" int r; void foo() { printf("foo %d\n", r); } int main(int argc, char* argv[]) { MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD,&r); swill_init(8887); swill_handle("stdout:foo.txt", foo, NULL); while(1){ sleep(5); swill_poll(); } return 0; }
To begin with, SWILL expects the host application to initialize MPI (MPI_Init()). If the user doesn't do it, SWILL will not do it for her.
Secondly, swill_init() must be called. In this case it gets called in code that is not rank-dependent -- indeed, the whole example is rank-independent. If there are other rank-dependent initializations that need to be performed, then swill_init() can be called along with them, so long as it has been called on every node before any other swill_*() call.
Thirdly, swill_handle() gets called, also in a rank-independent context, to register a function callback.
Then we enter our loop, which in this case performs no computation. Again in a rank-independent context, we call swill_poll().
What happens under the hood when swill_poll() is called is this (let's call the node with rank == 0 the "master node"):
So, then, in an MPI application, calling a swill_poll() is equivalent to performing a barrier synchronization. That is because the master node has to broadcast at least one piece of information, i.e. whether there is a pending request that requires every node's output, or not.
Notice that no formatting or designation of output by rank number or anything of that sort is done. This sort of functionality is up to the user to implement.