Click here to Skip to main content
Click here to Skip to main content

Webio - An embedded web server

By , 20 Jul 2008
 

Introduction

Webio is a small-footprint web server, designed to be embedded in an application or an embedded system. It's useful when you want to implement a complex browser based GUI (which can be accessed by everything from PCs to cell phones) in a very efficient manner. Webio compiles and runs equally well under Linux and Windows, and should be easy to port to most other platforms. It comes with a programmer's manual (progman.html) which explains how to use it and how to port it.

Background

In 1996, I wrote one of the first web servers designed for embedded devices. In those days, most embedded devices had no file systems, so I created the "HTML compiler" to embed the files into the code image. Similarly, the lack of a file system led to C-language CGI functions.

Creating basic GUIs with this system was so easy that I started using it in Windows applications in preference to the Windows GUI. Back then, before JavaScript and CSS, it was somewhat limited - for example, it wouldn't make a very good "photo shop" type program - but for basic GUIs, it was great.

As my company started using Linux and Browser-enabled hand-held devices, a really huge advantage became obvious - my new applications worked everywhere, not just on Windows. The user was not tied on one OS or one type of device. They didn't even have to be near the machine running the application.

In 2007, I needed a similar server for an open-source project. I had left the previous company, which still retained the rights to the my old server. They wouldn't open source it, and nothing suitable was available in the public domain. I decided to create a second generation version of the server and release it under the BSD license so I would never have to write it again.

The result is Webio - my second (and hopefully last) embedded web server.

Using the code

The Windows version is probably of most interest to CodeProject readers, and so the .zip file is made available here. Follow these steps:

  1. Unzip it (preserving the directory structure).
  2. Type buildfs to compile the embedded file system.
  3. Open the project file with Visual C++ 6.0 or newer, and click Build.

You should get a little application which, when run, allows your PC to act as a web server - point a browser at it. You can do this in loopback by typing "http://127.1" in your browser's location bar.

Points of interest

Webio has a few improvements over my first embedded web server:

  • The "HTML compiler" is now a full-fledged file system builder, designed from the group up to generate not only file images in your C code, but also generate code for C-language CGI.
  • The server buffers all code-generated output, allowing accurate Content-Length fields on files with variable sized SSIs.
  • A fast path for binary files improves performance.
  • Portability across Windows/Linux/Embedded systems is enhanced.

History

  • July 2008 - First public release.
  • July 27th - Updated, call this release 1.1. Changes:
    • Added command line option to set the HTTP port (default is still 80).
    • Error message is more helpful if another web server already has port 80.
    • Fixed some typos and omissions in the manual.

License

This article, along with any associated source code and files, is licensed under The BSD License

About the Author

jbartas
Chief Technology Officer praemio.com
United States United States
Member
See my bio here:
 
http://www.bartas.net/resume.htm

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionLinks are dead again...No Makefile in the current download?memberCraig L Soucie20 Nov '12 - 10:12 
Hi All,
 
I downloaded the current zip and unpacked it. Not seeing any Makefile in this archive but saw a link in another email. Tried downloading that but daddywall.com is dead (expired on 11-15-12 I think). Also looked at the code browser on Code Project and can see a Makefile in the manifest but it is not viewable or downloadable. Any chance I can get either Makefile or a zip with everything included?
 
Thanks,
CS
Questionpushmembersteve vreeland9 Apr '12 - 4:43 
does anybody have a simple push function that will do something like update a clock on screen?
I could sure use some help here. This is the only thing preventing me from using this as a real UI.
(I realize it was an exercise left for the student...but I can't seem to get there from here)
QuestionJBartas In Supermax Doing ChinupsmemberCleveland Mark Blakemore8 Mar '12 - 19:31 
The Justice Department is really cracking down on open source authors:
 
http://chicago.cbslocal.com/2011/01/14/webio-founder-gets-16-years-in-prison/[^]
 
He now has the words "Love" and "Hate" tattooed on his knuckles and has the CAPE FEAR soundtrack playing the background while he plots his revenge upon release.
AnswerRe: JBartas In Supermax Doing Chinupsmemberjbartas9 Mar '12 - 7:34 
Yikes! I was going to name my next code project "Ponzi", but I've changed my mind.
AnswerRe: JBartas In Supermax Doing Chinupsmembersteve vreeland9 Apr '12 - 4:40 
bright.
 
Chicagosportswebio.com
QuestionSo awesomememberCleveland Mark Blakemore24 Jan '12 - 20:05 
I got the latest version and have succeeded in compiling it for DOS Real Mode, DOS-32 Protected Mode, Win-32 and Linux all from targets on the Open Watcom compiler. In addition to being the greatest thing in web technology since sliced bread, it is also the easiest ANSI C portable source I have ever used. If you need a Berkeley Sockets compliant library for TCP-IP on DOS try :
 
http://www.softsystem.co.uk/products/swssock.htm[^]
 
I had very little trouble compiling and inserting the DOS wrapper into WebIO using this library. I also got SQLite to compile to 224K on the DOS platform.
 
So far my realtime embedded SCADA server is compiling to around 634K with a couple dozen embedded pages. It is distributed as a single executable, zero configuration, zero installation, zero maintenance, you run it and surf to "localhost" and can see a colossal web site.
 
WebIO is incredible. The recent fixes have improved performance noticeably on Windows NT Workstation and Windows 2000 in my testing. Now all I need to do is compile the Dillo Browser for DOS into this source and I will have an X-Windows compliant web server realtime workstation. Smile | :)
QuestionUpdate and new site for official sourcesmemberjbartas31 Dec '11 - 15:58 
I've finally gotten around to some belated fixes to the build system. Praemio.com is now dead, so the new sources are here:
 
http://www.daddywall.com/webio/webio-dec2011.zip[^]
 
The programmers manual is there too:
http://www.daddywall.com/webio/progman.html[^]
 
Here's the text of the new readme.txt file:
 

Quote:
Readme file for Webio embedded we server directory.
 
Last Edit Dec. 31st, 2011
 
Webio sources are currently available at codeproject.com, daddywall.com,
and soon sourceforge.com. Once we find someone will to donate a Subversion
server we'll start publishing specific version numbers. Until then just find
source and documentation that seems to have consistent dates across the system.
 
The latest and greatest is currently at:
 
http://www.daddywall.com/webio/webio-dec2011.zip
 
Detailed documentation is in the file progman.html, which should have been included
with sources and samples. This file fills in update info which may not make it
into the html file.
 
During 2011 the demo application was built and run on Centos Linux 5.6 and
Windows & using Microsoft Visual Studio 2010. Project files for Visual
Studio 6.0 are preserved with "-60" added to the main filename, i.e.
webio.dsp for MSVC 6.0 is named webio-60.dsp. These files have not been tested
in a few years - use at your own risk.
 
A new Linux makefile has been added so that both the demo and a library can be
built int he same directory. The demo is built be the default "makefile", the
library (at least the "daddywall" version) can be built be dwallweb.mk. Since the
Daddywall sources are not open the latter file is purely informational.
 
Known bugs & problems:
 
* The cticks counter is not maintained in the demo programs.
 
* The -c flag (for cache control) is documented but not supported.
 
* On at least some Windows 7 machines, bind fails on port 80. It seems Microsoft
used it for their proprietary "IIS". To get around this the default port for
windows builds is changed to 88, thus your browser can access your web
server's pages at http://127.0.0.1:80 More proof that if Bill Gates
pair a nickel to every programmer who had to work around a
Microsoft bug he would be broke.

SuggestionCache flag -c support in fsbuilder.exe [modified]memberMember 156850622 Nov '11 - 1:10 
I have been struggling with some cache issues on my embedded web site. Turns out the contractor who coded the original site used the <meta> tag (<META http-equiv="Pragma" content="no-cache">) to ensure that the browser did not cache certain pages. However, that doesn't work on IE and I am not sure FF is working properly either; I am seeing strange behaviour that can only be rectified by clearing the browser cache.
 
So I added the -c flag to the appropriate files in my "filelist" file, but the resulting code in wfsdata.c had not changed. I looked at the source of fsbuilder.cpp and although it says it supports the -c flag, it accepts as valid but does not do anything with it.
 
I think it would be relatively easy to support caching at file level. A simple mod to fsbuilder.cpp should ensure we could turn on the cache bit EMF_CACHE. Then in the function..
int wi_replyhdr(wi_sess * sess, int contentlen)
you could generate the following http headers for GET operations on non-cached files.
Cache-Control: no-cache
Pragma: no-cache
Well it sounds simple enough, anyone else come across this? I am just surprised no one else had mentioned it before?
 
Phil

modified 22 Nov '11 - 8:24.

QuestionBuilding webio for LinuxmemberPeter Dickinson8 Sep '11 - 3:12 
Hello,
 
I've been trying to build webio for Linux, but the Makefile (supplied in reply to a question from someone else) doesn't work on my system. I'm using Ubuntu 11, and I'm getting error messages from make about missing separators, and it doesn't perform the build.
 
Can you help with this please?
 
Peter
GeneralA couple of allocation/free bugsmemberjbartas17 Sep '10 - 13:44 
Adam Fullerton sent in a couple bugs, along with the fixes. It's really past time to spin a new code base for Webio.
 
If you use it before I get around to the update, make sure you fix these:
 
1) In webobjs.c oldsess->ws_formlist is not cleared before the wi_free(oldsess) call. the form list dshould be cleared in a loop, similar to ws_filelist and ws_txbufs list.
2) In webutils.c sess->ws_formlist->next is referenced right after the call "wi_free(sess->ws_formlist)". A temporary pointer should be used to save sess->ws_formlist->next before sess->ws_formlist is freed.
Questionmultiple user/password requestmemberpbisiac15 Apr '10 - 9:40 
Hi, perhaps my question has few to do with WebIO,
When I browse a page that's configured to ask user/password some browsers (Opera on IPhone and Chrome) refuse to load it but ask continuously same credentials.
Perhaps the access policy to those files should be different by design.
 
Any ideas ?
Thanks, regards,
Paolo
AnswerRe: multiple user/password requestmemberjbartas18 Apr '10 - 16:34 
I'd like to reproduce this and watch it with Wireshark. I don't have an IPhone (I'm about to buy an android Smile | :) ), but I can try Chrome on Windows or Linux. Do you see the problem with Chrome on either of those systems? -JB-
GeneralRe: multiple user/password requestmemberrcl18 Feb '11 - 9:09 
JB,
 
I am so glad to have your webio running on my small board(with 2M ram), and it works good so far.
 
I have same issue at beginning when I use chrome as browser, and I found that decode[80] in webutils.c - wi_decode_auth(...) seems not big enough for chrome, change it to decode[400] works.
 
Thanks again for the great job.
 
cren
SuggestionRe: multiple user/password requestmemberMember 15685064 Nov '11 - 0:37 
I found that the Stock Android browser (on Android 2.3.3) and the Blackberry (on 5.0) don't send the HTTP Authentication headers for every html file (marked as requiring authentication in "filelist") which is why you get multiple user/password request. WireShark was not much use when debugging Mobile browsers over Wifi, so I simply pushed the incoming HTTP requests out the Serial Debug port on my development board which confirmed this behaviour. The iPhone browser (3GS) works fine with authentication as do most of the Desktop browsers.
 
I really don't know whether this is a bug in those browsers or perhaps a feature of my mobile web pages or the way I have set up the authentication if "filelist".
 
I inherited the WebIo port and web site from a contractor so I am on a steep learning curve.
 
In the end the only way I could get around this was to remember the login credentials for any session from those browsers. I do check IP and MAC addresses and there is a session time-out as well, but I wouldn't recommend this solution as a secure. Also, my application only allows one session to login at anyone time.
GeneralSegmentation faultmemberbiana26 Nov '09 - 21:02 
When I run the server, it shows message below:
Webio server starting...
 
It seems the server is running. But when I try to brower to it, the webio terminal, and show this message: Segmentation fault
 
I run on embedded sytem.
Our System: mips chip, RAM 512MB, linux 2.6.22.19-12.
 
Our compile: gcc4
 
Thanks
GeneralRe: Segmentation fault [modified]memberlxlxlx15 Jun '10 - 2:14 
I have the same problem on my linux build. When I use in the debugger I get also a message
Program received signal SIGSEGV, Segmentation fault.
 
I also notice that sometimes when I press 'F5' (refresh) and keep it pressed on my browser an other error is reported:
Program received signal SIGPIPE, Broken pipe.
 
Build with GCC on Ubuntu (4.4.3)
Anyone a suggestion how to solve this?

modified on Tuesday, June 15, 2010 8:30 AM

GeneralHeap failure after some timememberpbisiac4 Nov '09 - 5:22 
Hello,
 
If I leave my embedded device webserver working for hours, with 4-10 clients (Internet Explorer) calling periodically a CGI function my heap keeps filling until I get an exception.
My opinion is that some sessions are not well closed and generate some memory leak.
I can reproduce this behaviour in this way:
start.....20%heap
open 10 IE clients....30% heap
keep working some time....
close 10 clients...HEAP doesnt reduce
open 10 new IE clients....40% heap
and so on
 
Any ideas ?
Thanks, regards
Paolo
GeneralRe: Heap failure after some timememberjbartas5 Nov '09 - 6:47 
Hi Paolo,
 
I sure does sound like a memory leak. I'll try to reproduce it over the next few days and see if I can spot it.
 
In your example above, when you "close 10 clients...", how do you close them? Do you lose the IE application with an exit, kill it from the process controller, or something else?
 
Please let me know,
 
-JB-
AnswerRe: Heap failure after some timememberpbisiac9 Nov '09 - 7:04 
Hi JB,
 
I made some more investigation and found a memory leak.
My webpage sends a GET avery 10 seconds to WebIO. Here some logs with caller routine in evidence:
 
****WI_MALLOC (0654) = 380031F4...wi_newsess:wi_alloc(sizeof(wi_sess)) = 380031FC
****WI_MALLOC (0024) = 3800D868...wi_buildform:wi_alloc(sizeof(total 24 bytes)) = 3800D870
****WI_MALLOC (001C) = 3800D894...em_fopen:wi_alloc(sizeof(EOFILE)) = 3800D89C
****WI_MALLOC (0824) = 38003850...wi_newfile:wi_alloc(sizeof(wi_file)) = 38003858
wi_newfile GetUpdate.cgi
****WI_FREE (3800D894)...em_fclose:wi_free(passedfd) = 3800D89C
wi_fclose
****WI_FREE (38003850)...wi_delfile:wi_free(delfile) = 38003858
wi_delfile
wi_txdone:socket close
wi_delsess:oldsess->ws_socket == INVALID_SOCKET
****WI_FREE (3800D868)...****WI_FREE (380031F4)...wi_delsess:wi_free(oldsess) = 380031FC
 
The form data (24 bytes) malloc never get freed. I think the problem in resourse freeing in wi_delsess:
 
.................
   /* Unlink from master session list */
   lastsess = NULL;
   for(tmpsess = wi_sessions; tmpsess; tmpsess = tmpsess->ws_next)
   {
         if(tmpsess == oldsess)      /* Found session to unlink? */
         {
            if(lastsess)
                  lastsess->ws_next = tmpsess->ws_next;
            else
                  wi_sessions   = tmpsess->ws_next;
            break;
         }
         lastsess= tmpsess;
   }
 
   /* Make sure there are no dangling resources */
   if(oldsess->ws_txbufs)
   {
         while(oldsess->ws_txbufs)
         {
            wi_txfree(oldsess->ws_txbufs);
         }
   }
   if(oldsess->ws_filelist)
   {
         if(oldsess->ws_filelist)   // THIS IS STRANGE INDEED
         {
            wi_fclose( oldsess->ws_filelist);
         }
   }
//     PB     09/11/2009 18.39.29
   if(oldsess->ws_formlist)
   {
         if(oldsess->ws_formlist)
         {
            wi_free( oldsess->ws_formlist);
         }
   }  
//     PB     09/11/2009 18.39.29
...........................
 
I added the oldsess->ws_formlist cleanup, but I'm not sure if you can have more than 1 form attached to a session; in that case, my correction would not be enough
 
I Wait for news from you, best regards,
 
Paolo
GeneralRe: Heap failure after some timememberjbartas15 Nov '09 - 16:40 
Yup, looks like a leak. Sorry about that.
I'm pretty sure the form is a linked list, so I'll have to come with some code that traverses the list, free()ing memory as it goes. What you've posted should be OK for pages with only one form.
I finally have the code on a public SVN server - I'll check in these fixes and publish the link in a note here as soon permissions are set up.
 
Thanks for the fix,
 
-JB-
GeneralRe: Heap failure after some timememberPanuO19 Apr '10 - 1:34 
Hi! Any news on this public SVN server? Or any other place to download newer than 1.1 release (www.praemio.com doesn't seem to exist any more) ?
Generalwi_putfilememberpbisiac8 Jul '09 - 21:20 
Hi, this is my (tested) upload routine:
 
/* wi_putfile()
*
* This is called when a session receives a PUT command.
*
*
* Returns: 0 if no error, else negative WIE_ error code.
*
*/
 
int
wi_putfile( wi_sess * sess)
{
   wi_file *fi;     /* info about current file */
 
   char *   cp;
   char *   cl;
   char *   rxend;
  
   int            res;
   long      b_read, b_remain;
  
   /* First find end of HTTP header */
   rxend = strstr(sess->ws_rxbuf, "\r\n\r\n" );  
  
   /* Extract the URL */
   cp = wi_nextarg(&sess->ws_rxbuf[3]);
   if(!cp)
   {
         wi_senderr(sess, 400);   /* Bad request */
         return WIE_CLIENT;
   }
   if(*cp == '/')
   {
         if(*(cp+1) == ' ')
            sess->ws_uri = wi_rootfile;
         else
            sess->ws_uri = cp+1;      /* strip leading slash */
   }
   else
         sess->ws_uri = cp;
 
   /* Extract other useful fields from header   */
   sess->ws_auth = wi_getline("Authorization:", cp);
   sess->ws_referer = wi_getline("Referer:", cp);
   sess->ws_host = wi_getline("Host:", cp);
 
   cl = wi_getline("Content-Length:", cp);
   if(cl)
         sess->ws_contentLength = atoi(cl);
   else
         sess->ws_contentLength = 0;   /* unset */       
       
   /* insert the null terminators in any strings in the rxbuf */
   if((sess->ws_uri > sess->ws_rxbuf) && (sess->ws_uri < rxend))
         wi_argterm(sess->ws_uri);         /* Null terminate the URI */
   if((sess->ws_auth > sess->ws_rxbuf) && (sess->ws_auth < rxend))
         wi_argterm(sess->ws_auth);      /* etc */
   if((sess->ws_referer > sess->ws_rxbuf) && (sess->ws_referer < rxend))
         wi_argterm(sess->ws_referer);
   if((sess->ws_uri > sess->ws_host) && (sess->ws_host < rxend))
         wi_argterm(sess->ws_host);
/*     ---------------------------------------------     */                
  
/*   Decode filename   */  
      wi_urldecode(sess->ws_uri);
     
/*   Inizio a leggere il file da scrivere nel mio filesystem     */
      b_remain = sess->ws_contentLength;
  
      res = wi_fopen(sess, fname_path, "w");
      if (res == 0)
      {    
      /*      file da scrivere */
        fi = sess->ws_filelist;
            
/*     vedo se nel primo pacchetto ci sono anche dati da scrivere     */
              if (sess->ws_data)
              {
                  b_read = sess->ws_rxsize - (sess->ws_data - sess->ws_rxbuf);
                  b_remain -= b_read;    
                  res = wi_fwrite(sess->ws_data, 1, b_read, fi);
              }
 
     if (b_remain > 0)
     do
            {
                  b_read = recv (sess->ws_socket, sess->ws_rxbuf,
                       sizeof(sess->ws_rxbuf), 0);
                   
                  if (b_read != -1)
           {
                 b_remain -= b_read;
                        res = wi_fwrite(sess->ws_rxbuf, 1, b_read, fi);
                        
           /*   all data written ? */
          if (res != b_read)
                        {
          /*   TODO: check, why not all data was written */
                b_remain = -1;     // segnalo abort
          }                   
           }
           else
           {
                 b_remain = -1;     // segnalo abort
          break;
           }
     }     //     do
     while (b_remain > 0);     
         
            if (b_remain == 0)
     {
                  wi_replyhdr(sess, sess->ws_contentLength);
                  wi_fclose(fi);
     }
     else
     {
           wi_fclose(fi);
           wi_fremove(sess->ws_uri);
                  wi_senderr(sess, 501);   /* Send "Internal server error" reply   */
     }
      }
      else
      {
     wi_senderr(sess, 503);   /* Send "Service Unavailable" reply   */
      }           
      return 0;         /* No Error */
}
 
Hope will be useful...
GeneralFile buffering and embedded systemmemberpbisiac7 Jul '09 - 6:03 
hi again,
 
if I ask Webio a "big" file (25Kbytes) it seems like wi_readfile allocates more and more sessions with
wi_txalloc(sess), until my embedded system runs out of memory.
Is that true ? could you help me modify this routine so no more than, say, 3 sessions get allocated at any time ?
I understand 26K is insignificant on Linux and Windows, but can be a problem in embedded system with limited heap...
GeneralRe: File buffering and embedded systemmemberjbartas7 Jul '09 - 18:32 
Hi -
 
Is this a binary file, or is a "normal" one which may have SSI-type includes?
 
This could be a bug (after all you've found 'em before Wink | ;-) ) or it could be an artifact of the way Webio handles the SSI-containing (I'll call them dynamic) files. To return the Content-Length of a dymanic file Webio reads the whole file, including files inside SSIs, into a chain of buffers; then measures the ready-to-send set of buffers to get the length. These buffers can eat a lot of space.
 
So - is the file binary?
 
Let me know,
-JB-
GeneralRe: File buffering and embedded systemmemberpbisiac7 Jul '09 - 21:06 
Hi, tanks for replying so fast,
 
it is a normal text file (license_lgpl.txt). If I change the extension to htm nothing changes. I guess It's treated as binary file.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 20 Jul 2008
Article Copyright 2008 by jbartas
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid