Webio programmers manual

Last edit: July 14th, 2008 by John Bartas

Contents

Introduction

Internal structure

Source files
Threads entry point
Main objects
Heap memory

The demo application

Building on Linux with GCC
Building on Windows via Visual Studio

Merging Webio with your target or application

Initialization
Runtime

fsbuilder

Command line Options
Static files
Dynamic content
SSI
C-code expressions
CGI
Redirecting to another file

Compile-time options

Authentication

Portability

Header files
Portbility routines

Debugging aids

dtrap
heap checking

The case for HTML GUIs

Buster

Introduction

The Webio server is a small portable web server designed as a library for inclusion in embedded systems or as a Browser-based GUI in applications. It compiles and runs on both Windows and Linux.

Webio includes:


Internal structure

Webio is written entirely in C language, and has been tested on Microsoft and GCC compilers. Most source files are compiled on all target systems with no "ifdefs or modifications - we've tried to limit those to a few selected include files.

Some source "*.c" files may contain the HTML content, which will change from one application to the next. These files are usually generated by the fsbuilder utility, described here.

The porting engineer will also usually want to provide additional file to implement dynamic content via Webio's SSI and CGI interfaces.

Source files

The following source and header files should not be modified in the course of ordinary ports or applications:
webio.c Core thread and socket processing
webclib.c C functions for aiding SSI and CGI routines
webfs.c Support for internal (embedded) and external (disk) file systems
webobjs.c Implements web session and file objects
webutils.c Useful C utility functions
webio.h Primary Webio object definitions

These source and header files may need to be modified or replaced when porting Webio./span>
websys.c Windows vs. Linux "ifdef" code
websys.h System definitions, may include windowsdefs.h or linuxdefs.h
windowsdefs.h Windows port definitions
linuxdefs.h Linux port definitions

The next list of source and header files are produced by the fsbuilder in the demo program. The may be present as named in the your final application, or you may choose to rename them. In either case, hand editing the,m is not recommended, since they will be re-written each time you run the fsbuilder tool.
imgdata.c C char[] arrays with embedded image data
wsfcode.c C function stubs for embedded form and CGI routines
wsfdata.c File structure list and arrays with embedded HTML pages
wsfdata.h Declarations and prototypes for all the above

The following file is provided for building a simple test application with Webio, to verify basic compiler and system functionality before you merge Webio with your target application of system
webtest.c main() for Windows & Linux test app

threads entry point "wi_thread();"

Since Webio is designed to be used as a library, it uses the simplest workable model - it repeatedly polls wi_poll() the main entry point for the server. wi_poll() first does a non-blocking select() call to see the any of the web servers sockets requires action. It then loops through the list of open web server sessions, checking to see if any of those require action. If performs whatever actions it can with each object (socket or session), stopping when it can go no further without blocking. The object is then left in a state where execution can be resumed in a later instance of wi_poll() .

wi_poll() it designed to be driven by a single thread. The routine the routine wi_thread(); , is provided to implement this. easily. The demo program demonstrates how it to this. It's sole thread, the main() routine sets up the network (ifdeffed for Linux or windows) and then blocks in a call to wi_thread(); . When this call returns, the application has terminated.

All the objects keep stateful information, so if the execution is required to block, the code can simply return to the main thread, or whatever is calling wi_poll()

main objects

wi_sess
One of these objects is created for every connection which is opened to the web server. Is contains the open socket, buffers for reading and writing, and pointers to any related file objects.

The main poll activity of the program works largely by traversing a linked list of wi_sess; objects and checking each for any pending work which can now be done because of changes since the last poll.

wi_file
This is a wrapper for a lower layer FILE descriptor. One of these is maintained by the server for each open file. The lower layer descriptor may be a conventional disk file, one of the embedded files produced by fsbuilder, or other types of files defined by the programmer.

wi_filesys
One of these objects is created for each type of file supported in the build. Typically there will be one foe disk files and one for fsbuilder embeddded files.

The object is primarily a list of routines which perform common ansi-like file operations, such as fopen, fread, and fwrite. New file systems may be implemented by defining another one of these objects and provided it's member functions.

em_file
One of these is created for each embedded file - files which are contained in the program itself. These files are saved as arrays of C unsigned chars and these arrays are managed by a linked list of em_file structures. Both these structures and the character arrays they manage are generated by the
fsbuilder tool.

NOTE This structure should NOT be changed unless the fsbuilder is modified to incorporate the change.

heap memory

Normal heap management with malloc() and free() are not always portable enough for Webio, especially on real-time systems; where they often don't exist at all.

We deal with the by allocating and freeing all our memory via the macros WI_MALLOC() and WI_FREE() .

These are further wrapped insire the calls wi_malloc() and wi_free() , which perform error checking by allocating slightly longer buffers than needed and placing markers around the area returned to the caller. If these markers are corrupted when the block is freed, then the code has serious errors and an embedded breakpoint can be invoked.


The demo application

The demo application is a simple standalone web server, provided for building a test application with Webio, to verify basic compiler and system functionality before you merge Webio with your target application of system. It demonstrates simple web pages with SSIs, Authentication, images, and a form.

Building on Linux with GCC

To build the demo application on linux, follow these steps:

Building on Windows via Visual Studio

To build the demo application on Windows, follow these steps:

Merging Webio with your target or application

For most applications and targets, this is simply a matter of replacing the code in webtest.c which initializes the web server at start-up and supports it during runtime. If your system will be running the web server in a single thread (recommended), you may even be able to take the "main()" routine from the demo, change the name (most tool chains won't like another use of the name "main") and use it as a basis for your thread's entry point. The main() routine does what most threads do - initializes the resources needed for the thread, and then enters an simple loop that runs until shutdown.

Initialization

To prepare to run the web server, perform the following steps in order:

1) Start the network (not needed on Linux)

On at least some versions of windows, you will need to start the "winsock" network services by calling WSAStartup(), as in the webtest.c example. Most Unix-like systems don't require this; their networks are already running when your application loads. Other OS's may have needs like Windows - check your OS networking documents to be sure.

2) call wi_init() to Initialize the web server.

This sets up Webio internals and preforms the sockets listen() call which connects Webio to the network.

3) Install any authentication routines your file system requires.

If your web server will require authentication of users to view certain pages, you will need to set a pointer to the authentication routine(s) to be used. How these routines work is described in detail in the
authentication section. To install them you need to set pointers in the structures which manage each file system. The code to install a routine for the embedded file system is:

   /* Install our embedded file authentication routine */
   emfs.wfs_fauth = wfs_auth;
...where wfs_auth is your authentication routine. This example is straight from the webtest.c sample code.

Runtime

Aside from the standard OS services (networking, file system, heap memory), the web server requires two additional services during runtime.

1) The counting timer, cticks.

This is defined as an unsigned long which is incremented by one at regular intervals, ideally about 1/10 of a second. The number of "ticks" per second is given by the #define TPS. It is the responsibility of the porting engineer to make sure that cticks in incremented at the TPS rate. Neglecting to do this will disable the web servers ability to time out idle connection.

2) CPU cycles (i.e. a thread).

The simplest way to do this is to call wi_thread();. This will block forever (until the web server terminates), so it should only be called from a dedicated thread; and then only after the initialization in the previous section is complete.

If you don't have a multi-threaded system, you can also drive the server by polling. Just call the routine wi_poll(); as often possible - at least several times a second. The more often you call this, the better your server's response time will be. This routine returns the number of web serer seesions which required CPU cycles during the call - the higher this number, the more often you should be calling wi_poll();.


The File system builder, fsbuilder

When embedding a web server as a GUI in an application or an Embedded system, it's usually desirable to package some or all of the web server's HTML files in the code itself, rather than having them on the disk. Reasons for this include: To support file embedding, Webio is bundled with the fsbuilder tool. fsbuilder is a C++ program which will compile and run on both Windows and Linux. A makefile is included for Linux, and a Microsoft Visual C project file is included for Windows.

Once built, fsbuilder is meant to be run as a command line application on the development system. The Webio Linux demo makefiles will invoke it as needed to produce new C file whenever the content changes. fsbuilder is NOT meant to be part of the program containing Webio .

fsbuilder takes a list of files as it's only required command line argument. The "listfile" is a plain text file, with one file name per line. Generally, static content files such as text and images which are in the list should exist on the working directory (or in an indicated path). The list may also include names of SSI and CGI files, for which placeholders are created by fsbuilder.

The listfile may contain comment lines starting with "#";

Per-file Options

Each file name line in the listfile may also contain several options:
-a Requests for the file will require authentication
-c Enable cache control - Browser will not cache the file
-o send C data array output to named file
-s data comes from named function (generated)
-f file maps to C code form handler
-p file is server push, generate function
-g generate C code to handle SSI or form data
-e file maps to a "C" expression
-w supress warnings on this file

Command line Options

In addition to these per-file options, the following command line options will efect ALL files:
-a Requests for the file will require authentication
-c Enable cache control - Browser will not cache the file
-o send C data array output to named file
-h send C headers to named file

Static files

fsbuilder reads the static files (those which not created with the -s, -f, -g, or -e, options) from the disk. The bytes values in the file are output as C source code, in C char arrays. The names of the char arrays are generated from teh names of the disk files, modified as needed to server as C labels For example, dots, while common in file names, are not allowed in C language array names. Each static file also results in fsbuilder creating an entry in an array of em_file structures. These structures serve a function similar to inodes in a UNIX files system. They contain information about the size and name of the file, and have a pointer to the char array with the file's data. The C files containing the embedded file data and structures, when compiled and linked with the Webio library, will server as an embedded read-only file system

Dynamic files

Lines in the listfile with the options -s, -f, -g , and -e will create pseudo files in the em_file array. This files are not associated with any file read from the disk, rather their content is generated by software each time the file is referenced. The most efficient way to do this is with C code linked into the system with Webio, but it can also be done by a reference to an external script or program (as with typical CGI). The types of dynamic files are described in the following section.

dynamic content

This section describes how to use Webio's two mains types of dynamic content, SSI and CGI. Both are implemented as "pseudo" files - entries in the em_file list which do not have any array of static data associated with them. When the core server loop does a read on these files, rather than copying out data the embedded file system code calls a C routine, passing the wi_sess structure ( aka a session) to the C routine. The C routine fills in a per-session buffer with the HTML (or other content) which is to be returned to the browser.

The exact semantics of the C routines which service SSI and CGI functions are different, but in both cases a stub routine is generated by the fsbuilder utility and left (by default) in the file wsfdata.c. This file should not be compiled directly, rather the stubs it contains should be copied to a user maintained file, which IS compiled and linked with the webio system. This allows you to add your own code to the stubs without it being overwritten the next time you build.

The name of the C routine is set by the line in the listfile which creates the pseudo file. If you get an "unresolved external" error in your link, you have probably created a pseudo file and forgotten to copy the stub out of wsfdata.c.

SSI

SSI files are created by including a line in the listfile which follows the file name with "-s", the SSI option. If the named file exists on the disk it is ignored. As described above, an entry for the file is created in the em_file list, a C routine stub is generated, and the hooks which cause the stub to be called at runtime are added to the em_file entry.

Lets look at the details of SSI via an example, one which places the current time in an HTML page. The HTML might look like this:


<!--#include file="time_of_day.ssi" -->

The syntax for the listfile line to create the SSI file and C routine stub would be:

time_of_day.ssi -s time_of_day_routine

The C stub would look like:

/* time_of_day_routine()
 *
 * SSI routine stub
 */

int
time_of_day_routine(wi_sess * sess, EOFILE * eofile)
{
   /* Add your code here */
   return 0;
}
Note: a prototype for this routine is created in wsfdata.h (default name) which SHOULD be included in your SSI and CGI code files.

Lastly, you need to provide code which puts the text/html to be displayed in the output buffer passed in sess. The buffer has it's own data type, txbuf, which has a fairly complex semantics because of the issues with allocating buffers as we need to write to them, and freeing them as they are written to the TCP socket. Fortunately you don;t need to deal with this - webio's library includes the function wi_printf(), which outputs formatted text into the session's output buffer.

Using wi_printf(), your time of day function might look something like this:


/* time_of_day_routine()
 *
 * Sample SII routine to print TOD into an output html file. 
 *
 */

int 
time_of_day_routine(wi_sess * sess, EOFILE * eofile)
{
   char * time_String; 

   time_String = get_system_time(); /* call OS for current time */

   wi_printf(sess, "Time is now: %s <br>", time_String );

   return 0;      /* OK return code */
}

Since the original include statement was in an HTML file, HTML can be included in the text written to the output buffer. In this case, a line break was inserted at end of line. Controlling HTML generation on-the-fly with C code can be a very powerful tool. Javascript can also be created by SSI routines.

The return value from SSI routines is 0 if the routine succeeds, non-zero if something goes amiss. Currently (July 2008) the core Webio code does nothing with the returned value, but it might in the future.

C-code expressions

Sometimes it's useful to be able to include the result of a simple C code expression in an HTML file, but without the additional coding (and overhead of an additional C routine) require by the SSI "-s" option. For these situations Webio supports the "-e" option. This allows C language expressions to be specified directly from the listfile. These expressions are still reference via an SSI in the HTML code, however the C code to resolve the C expression and output the result into the HTML stream is entirely generated by fsbuilder.

To understand how this works, let examine it's use in the demo server. The demo creates an SSI file which display the number of total memory blocks allocated by the server since it's been running. The listfile contains this line:

memhits.var -e u_long  wi_totalblocks
This relies on the existence of the "unsigned long" variable named wi_totalblocks (which counts the memory allocations), and creates an embedded SSI file named memhits.var. memhits.var can the be reference in an HTML page as follows:
...and mem cexp: <!--#include file="memhits.var" -->
When the page is displayed, the include tag is replace by the number of memory allocations which have occurred up to that point.
...and mem cexp: 51243

A single step is required bny ther programmer to support all the C-code expressions in the system - you must copy the routine wi_cvariables() from the wsfcode.c file generated by fsbuilder into a C file which is linked into the system. As with other SSI and CGI code, the generated code is created in a "non live" file so that you can modify it as needed, and future runs of fsbuilder will not overwrite it.

The advantages of using the C-code expressions "-e" option over the SSI "-s" option are less work during development, and less overhead during runtime.

WARNING
When fsbuilder generates the code for wi_cvariables(), is generates a switch: statement with algorithmically generated case: labels. These labels include several digits, based on the order of the files in the listfile, to ensure uniqueness. For example in the demo example the lable might be "MEMHITS_VAR5" if the file is the fifth in the listfile.

The "gotcha" this creates is when you add more files to the listfile ahead of the "-e" file. This changes the label (in this case from "MEMHITS_VAR5" to something like "MEMHITS_VAR8") in both the ".c" and the ".h" output files. Since the the header (".h") file are included in the compiled code and the ".c" files are not, this creates a discrepancy between name of the label in the two files, and at compile time you will get an error something like:

gcc -g -DLINUX -c webtest.c -o webtest.o
webtest.c: In function "wi_cvariables":
webtest.c:157: error: "MEMHITS_VAR5" undeclared (first use in this function)
If this happens, you need to update the labels in the C file, either by copying in the new wi_cvariables() routine, or manual editing.

CGI

CGI routines are also created by including a line in the listfile which does not refer to a existing disk file, but to create a CGI file the listfile line contains the option "-f". The example from the demo package listfile is:
# form handlers
testaction.cgi -f testaction_cgi
This creates an em_file structure and includes it in the list of embedded files. When the file is referenced, usually because the use clicked a form submit button, the C routine following "-f" is invoked - in this case, testaction_cgi();. As with SSI function, the fsbuilder creates a C stub for the routine in wsfcode.c and a prototype in wfsdata.h. The code stub should be moved to a "live" code file, and the prototype included as appropriate. The code stub generated for testaction_cgi();. is:
/* testaction_cgi
 * Stub routine for form processing
 * 
 * Returns NULL if OK, else short error text
 */

char *
testaction_cgi(wi_sess * sess, EOFILE * eofile)
{
   char *   your_name;

   your_name = wi_formvalue(sess, "your_name");   /* default: John */


   /* Add your code here */

   return 0;
}
As you can see, this is a bit more complex than the simple SSI stub. In the case of the CGI routine, the fsbuilder scanned the HTML code, and included code to extract the name-value pairs in the form form into local C language variables. Once you copy this stub to a working filer you are ready to jump right in manipulating form data with C code - the whole name-value extraction process is automated.

In the event that Webio can't parse the HTML (hey, it happens) it may be useful to explain how the form's name-value info is passed to the CGI routine. Whenever a HTTP GET or POST request is received, the attached name-value text is parsed into a wi_form object. The definition of the object is in the webio.h file. The session objects's (wi_sess) ws_formlist field is set to point to this form structure.

As the name implies, the forms can be a linked list, however there is usually only one. Multiple forms may be attached to a session if a page invoked with name/value pairs includes and SSI which also has name/vale pairs.

Form structures are automatically freed by the Webio internals when the page processing is complete. Webio also provides the utility function wi_formvalue() to return the text for a value in the form when the corresponding name is passed.

After the form name/value pairs have been processed, the browser expects a page to be displayed in response to the GET or POST request. Since the file name in the request was directed to the CGI routine, there is no actual file associated with the request. If would be possible for the C routine to generate the entire file (as conventional CGI systems often do), however that can mean a lot of tedious extra code. To avoid this, use the wi_redirect() function described ion the next section.

The return value of the CGI routine is NULL if the routine has successfully handler the CGI input and redirected (or otherwise handled) the resulting page to be displayed. If the CGI routine return a non-NULL value, it is assumed to be a pointer to some error text, which is displayed on an otherwise blank web page. Since these error pages are rather plain, it's recommended that the CGI routine establish it's own error handler and pages, and leave the non-NULL return scenario for very unlikely situations or minimalist web servers.

Redirecting to another file

The wi_redirect() function is provided for those times when embedded C code needs to send an entire HTML file to the browser. It's use is illustrated in the file webtest.c:
...
  wi_redirect(sess, "gotname.html");

   return( NULL );
}
In this case, the file "gotname.html" is opened and prepared for sending to the browser. The are two important points to note about this operation:

Note 1: Any file currently being sent to the browser is closed and replaced with the redirect file. In other words the redirect file is NOT inserted into the existing file, it fully replaces it. This is not an issue during CGI processing since there is no existing file, but it could be confusing at other time.

Note 2: The redirect file does not actually get sent until the C routine (usually a CGI routine) returns to the Webio core and the core gets a chance to run. The redirect file is only queued for sending by wi_redirect(), the sending does not actually start during the wi_redirect() call.


Webio Options

Compile-time options

Webio has several options which can by omitted via C #ifdef statements at compile time. The are listed and described in this excerpt from the demo file.
/*********** Optional webio features - comment out to remove ***************/

#define WI_STDFILES  1     /* Allow host system (Linux, Windows) files */
#define WI_EMBFILES  1     /* Allow embedded FS files */

#define WI_THREAD    1     /* Drive webio with a thread rather than polling */


/*********** Webio sizes and limits ***************/

#define WI_RXBUFSIZE    1536  /* rxbuf[] total size */
#define WI_TXBUFSIZE    1400  /* txbuf[] section size */
#define WI_MAXURLSIZE   512   /* URL buffer size  */
#define WI_FSBUFSIZE    4096  /* file read buffer size */

#define WI_PERSISTTMO   300   /* persistent connection timeout */
The Webio server supports an option to make it only available to Browsers on the local system. This is provided as a security measure for those who want to use Webio as a program GUI, but don't want other machines to be able to access the GUI over the network.

Activating this option is simple - you just set the global int variable wi_localhost to non-zero. This could actually be done at compile time runtime by editing the declaration in webio.c, but the current implementation does this at runtime to allow applications to treat this as a user option.


authentication

Every file system supported by the web server (usually just the embedded file system and sometimes one disk file system) has the option of supporting file authentication. Support is indicated by setting the file system wi_filesys structure's authentication routine pointer to a routine which will handle requests for authentication. The format for such routines is:
int         file_auth_routine(void * fd, char * name, char * pw);  /* Optional, for authentication */
If this member field in the wi_filesys structure is NULL then no authentication is performed. If this is non-null, it had better point to a routine of the above format. The routine should take the user name and password passed, and check the file (passed as a descriptor to an already opened file) and determine if the credentials allow access to the file. If so the routine returns a non-zero, if not the routine returns zero.

This is most efficient when used with embedded files. Each line in the listfile which contains the option "-a" will cause fsbuilder will set a flag in the embedded file's descriptor. This flag can be used to allow universal access to non-flagged files and restrict access to flagged files. Note that the programmer must still provide a routine (and name/password database) to resolve access. An example of this is in the wfs_auth() routine in the file webtest.c

Currently only Basic authentication is supported, MD5 will be in a future release.

A note about implementation: Passing the file descriptor (FD) is very efficient for the embedded FS, but may be less than ideal for the OS disk-based FS. Finding out which file is being passed form the FD requires intimate knowledge of the file system - usually a pain to actually code.

One way around this would be to hook the wi_fopen() member of the wi_filesys structure, and keep a map of file name (and/or other useful access info) indexed by the FD. Another approach would be to modify the code in webio.c to pass more info to the auth routine.


Portability:

I once said to a UNIX bigot "Unix people think 'portable' means it runs on two flavors of Unix." Of course he replied "So what's your point?". My point is that making something run on both Windows and Linux, with 99% shared code source code and no in-line #ifdefs (like Webio does) is a lot harder. Linux and Windows had different network APIs (Winsock is NOT sockets), and many embedded systems have no file system at all. Some even lack memory management. Webio is made portable across such environments by determining which aspects of the web server will vary from system to system, writing code that only uses functionality found on all the target systems, and abstracting out functionality that has has a different syntax across systems.

For example, on Linux a socket can only be closed by passing it to the file system's close() call. On Windows, a socket is closed by calling Winsock's closesocket() - passing it to close(), at least on some versions of Windows, will result in a system crash. Webio maintains portability across both system by using the less common closesocket() call throughout it's code, and mapping this to close() on Linux ports. Since the syntax of close and closesocket() are identical, this is done with a C #define.. In fact, to support the forgetful and dyslexic, we also have a #define. for socketclose()..

The remainder of this section lists each area where there are deviations between Linux, Windows, and common Embedded targets, and describes how Webio addresses the various systems with one clean code base. The only assumptions are

Portability files and routines

Wherever possible, definitions for support across projects and operating systems are in the header file websys.h. In the demo program a single websys.h works across Linux (Fedora 7) and Windows (XP) by including one of two sub-headers either linuxdefs.h or windowsdefs.h. Porting to other operating systems should start with going through whichever of these files is most like your target system, and redefining things as described on the following sections.

Header files

The header files required for standard ANSI C library prototypes, as well as common typedefs, are not the same across all operating systems. For this reason, the webio C source files all include websys.h, and these other files (stdio.h, ctypes.h, etc) should be included within websys.h.

Portbility routines

The following routine require actions from the system, but the syntax is different between systems. Each of these performs a simple function which needs to be implemented (either by macro or as a routine) on each target system.

WI_NOBLOCKSOCK()

This primitive is passed an open socket, which should be set to non-blocking mode. Once in non-blocking mode, calls to redcv() and send() should return EWOULDBLOCK if they cannot complete immediately. The Linux example:
#define WI_NOBLOCKSOCK(socket) fcntl(socket, F_SETFL, O_NONBLOCK)

wi_getdate()

This primitive should return a static string containing the current date and time in a user-readable format. The returned value will be used in building HTTP headers, so the format must be compliant with HTTP standards. See the Windows example for a usable format string.

strnicmp()

This primitive compares two strings up to a passed length, ignoring case. It is a merging of the ANSI functions stricmp() and strncmp(). Despite it's non-standard status, many C libraries (notably Microsoft) support it anyway. Because of this, Webio ports to systems without it require that it be in the portability code.

closesocket()

This primitive should close the passed open socket. On most Unix-like systems it can be mapped directly to close().


Debugging aids

dtrap()

This is used throughout the code as a sort of embedded breakpoint. It is called whenever something suspicious is detected (say, a variable value out of the expected range). Developers with debuggers should ALWAYS place a breakpoint on dtrap so they are aware of such problems

heap checking

This is already described in the section on
Heap memory. It's worth noting here that the heap wrapper routines also keep counters of alloc and free calls, so gradual memory leaks can be detected by watching for a slow increase between these counters.

The case for HTML GUIs

This section could be called a rant, but this author has been mystified for 20 years as to why there is no industry standard for implementing GUIs across different Operating Systems. We have an ANSI standard C library, sockets (and quasi-socket Winsock) for Networking. But since the Xerox Star, through Apple's Lisa and MAC, past OS/2, and into Windows and X-Windows, no two popular systems use even similar GUIs - until the advent of HTML.

HTML with CSS and Javascript provides a serviceable GUI across every OS with a mouse and graphics screen. While the displays don't usually support HTML natively, most platforms have at least one free web browser available. Even my cell phone came with one.

With the universal GUI platform, It only makes sense for application writers to implement simple GUIs with HTML, rather than Windows, MAC or Linux GUI calls. While some graphics intensive apps (such paint programs) are still better done on a native GUI, apps such as spreadsheets, text editors, and databases are as easy, if not easier, to implement on HTML. Look at the sophisticated office suite provided by Google apps.

The HTML GUI also has the benefit of freeing the user from having the GUI on the same computer that runs the app. By opening a few ports in a home firewall, you could run all your apps on a machine at your house and use them from any browser, anywhere on the Internet. No VPN or PC-Anywhere-like add-ons required.

The one missing ingredient for most application writers to write such apps is a web server designed to be embedded right in the application as a library - like Webio.

Buster

A few years back I wanted to write a portable application, including a portable GUI. I created Webio, since none of the open source web servers out there did quite what I wanted. The result is the open source Network monitoring aplication Buster, downloadable from www.praemio.com. Buster (along with Webio) has also been modified to serve the Network Neutrality Squad's NN-Agent; available from www.nnsquad.org. In both cases, a Windows build and full source is available for download. Both these applications demonstrate a GUI rich in forms, pictures and graphics. They serve as advanced examples of what Webio can do.

I'm hoping that by releasing this into open source, with any license restrictions, It will accerate the changing over from OS-locked GUIS to the universal GUI - HTML.

If you need help or have suggestions, feel free to email me - jbartas @speakeasy.net