Web Server
==========

BabbleMUD server.

Author:  Nick Gammon 
          http://www.gammon.com.au/ 

Written: 18th August 2004.

(C) Copyright Nick Gammon 2004. Permission to copy, use, modify, sell and
distribute this document is granted provided this copyright notice appears
in all copies. 


The BabbleMUD server has an inbuilt web server. This is intended for player support, eg. building or management, and could be used for general players, for instance to provide a list of who is online, without having to connect to the game.


How do web browsers communicate with web servers?
-------------------------------------------------

Web browser sessions are by their nature "stateless" which means that each page has to (re-)establish with the server what is required. There are five ways of a client sending information to a web server:


Here is an example:

  POST /mypage.htm?aaa=bbb&ccc=ddd HTTP/1.1
  Host: bacall.gammon:4080
  Cookie: session=2b4997d8156431dcf42e90887369177a
  [other headers here]
            <-- blank line here
  formdata1=xxx&formdata2=yyy

The five things above are:

  1. The URL: /mypage.htm
  2. The parameters after the URL: aaa=bbb, ccc=ddd
  3. Header information: Host: bacall.gammon:4080
  4. Cookies: session=2b4997d8156431dcf42e90887369177a
  5. Form data (for a POST): formdata1=xxx, formdata2=yyy

Since there is only one URL that is stored in a string (URL_).
The others, of which there may be many, are stored as name/value pairs in string maps:
       
Thus, you can extract them by direct lookup.
       
   headers_     <-- all headers (including cookies) - this is a list as there may be multiples
   
   getData_     <-- the parameters after the URL  - string map
   postData_    <-- the form POST data            - string map
   cookieData_  <-- cookies if present            - string map

Also, the version (probably HTTP/1.0 or HTTP/1.1) is stored in the version string (version_).

Headers, parameters and form data are URL-encoded which means that special characters
are converted into %xx and then spaces are turned into + signs. (This decoding has been done for you by time the data is accessed by the handler).

Also, the command (POST, HEAD, GET) is stored in command_.


Processing routines
-------------------

There is no specific requirement for a web client request to actually result in any disk files being read. In the case of the BabbleMUD server, the file suffixes "gif", "txt", "htm" or "html" result in the server trying to find a file of that name in the html subdirectory, reading it in, and sending it to the client. This to provide support for gif images, text files, and static pages.

However if a request ends in "php" then it is looked up in the "web_pages" section of the control file, to see if it is defined, and if so what its handler is.

Example:

  <web_pages>
  <item>
    <string
      handler="WebWhoList"
      name="who.php"
    />
  </item>
  
  <!--  others here  -->
  
  </web_pages>
  
This indicates that a request for "who.php" will result in the internal function WebWhoList being called, which will handle the request. This will result in HTML code being sent back to the browser. An example of a web handler function is:

  void WebExample (WebClient & w)
    {
    DoHeader (w, 200, "Example");
    w << "<h1>My Example Page</h1>\n";
    w << "<ul>\n";
    w << "<li>An item\n";
    w << "<li>" << FixHTML ("I want to see <brackets> on the web page") << "\n";
    w << "</ul>\n";   
    } // end of WebExample

The first thing it does is set up the web page header (with a "success" code of 200), specifing the word "Example" as the "title" text. The header is read from the control file, so that each page has a standard header.

Then you can send text to the browser with the << outputting operator. If you expect text to have HTML codes in it, like brackets or ampersands, then use FixHTML to convert them.

When the function exits a web page footer will be automatically sent (this is also on the control file).


Handling errors
---------------

If you hit a major problem you can throw an exception. This will be displayed in white-on-red text on the web page, and a web page footer sent. This should be done *after* the DoHeader line.

eg.

  if (x > 3)
    throw runtime_error ("X should not be > 3");
    
    
Processing form data
--------------------

The data from the URI, cookie or form can be accessed from the appropriate maps:

  string name     = w.getData_    ["name"];     // from URL
  string address  = w.postData_   ["address"];  // from posted form
  string token    = w.cookieData_ ["token"];    // from cookie
  
It is important to be aware of where the data originates, in order to pull out the correct one.

eg.

GET Data
--------

  URI: http://myhost/test.php?name=NickGammon     

In this case the data is from the URI, namely: name = "NickGammon"


POST Data
---------

  <form METHOD="post" ACTION="example.php">
    <input type="hidden" name="action" value="rhubarb" />
    <p><input Type="submit" Value="Save"/></p>
  </form>
  
In this case the data is from the form, namely: action = "rhubarb"


Cookie Data
-----------

  (from HTTP header)
  
  Cookie: session=2b4997d8156

In this case the data is from the cookie, namely: session = "2b4997d8156"


URL Name
--------

  (from URI itself)
  
   URL: http://myhost/test.php?name=FredNurk 

In this case the data is from the URL, namely: URL = "test.php"


Other headers
-------------

  (from HTTP header)
   
  User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)
  Host: localhost
  Connection: Keep-Alive
  
In this case you can extract this information by a scan of headers_.


Converting numbers
------------------

The helper routine convertString will convert a string to any data type (via templates). You send the string to be converted, and specify the desired return type. If sucessful, the result is stored, if not, an exception is thrown with the supplied message. eg.

  int vnum          = convertString<int>    (w.getData_    ["room"], "room number invalid");
  double percentage = convertString<double> (w.postData_   ["percentage"], "percentage invalid");
  bool blood        = convertString<bool>   (w.cookieData_ ["blood"], "bad flag");
