Writing an EHS application
---------------------------


Overview:
----------

EHS stands for Embedded HTTP Server and allows you to easily extend an existing
C++ class to have HTTP (web) server capabilities.


How to build EHS (on UNIX):
------------------
./configure --enable-warn

--with-ssl:      compile with HTTPS support. Requires OpenSSL library installed.
                 If not specified, configure will enable SSL support automatically,
                 if it finds OpenSSL.
--enable-debug:  prints out many debugging statements.  Recommended when you first
                 build a new application, but rather spammy.
--enable-warn:   Enable additional compiler warnings

For info about more options, run ./configure --help


Requirements:
--------------
Boost (libboost_regex)

OpenSSL is required for HTTPS support (./configure --with-ssl).  It can be 
found at http://www.openssl.org.

Writing a new program using EHS (or extending an existing one to use EHS)
----------------------------------------------------------------------------

The sample programs are probably your best bet for figuring out how to write an
EHS program.  They can be found in the Samples subdirectory.  

ehs_simple.cpp: about the simplest program you can write.  Shows how to use EHS
either single-threaded or multi-threaded.

ehs_test.cpp: Example of writing a small program with each of the three 
operation modes: singlethreaded, threadpool, onethreadperrequest.  Presents one
line at a time from the file specified on the command line and stores the word 
as a cookie in the browser to be shown on the next page loaded.  Also sets up a
path hierarchy.

ehs_https.cpp: same as ehs_simple, but creates an https connection.  Only built
if configured with the --with-ssl option.  

ehs_testharness.cpp: shows how to return a custom response to the client.  Also
used for unit testing EHS.

ehs_formtest.cpp: shows how to get form data out of the HttpRequest object.  
Runs in either HTTP or HTTPS mode based on command line arguments.

ehs_mirror.cpp: shows how to bind more than one port to the same EHS object 
using SetSourceEHS().  This example creates a custom EHS subclass and binds it 
to an HTTP port, then it creates a normal EHS object, sets the custom EHS 
subclass as its source, but binds to an HTTPS port.  The returned page reports 
whether or not the request was made over a secure connection.

ehs_uploader.cpp: shows how to use multi-part form attachments to upload a file
from the client to the server.  Useful for uploading configuration files to a 
server or pictures to a web page.  

* * Basics:
------------

Create an EHS object either statically or dynamically:
EHS oEHS;
EHS * poEHS = new EHS;

Create a parameter list for the server:
EHSServerParameters oSP;
oSP [ "port" ] = 4000;
oSP [ "https" ] = [1]; (if the https parameter is not specified, it defaults to
                        off)
oSP [ "certificate" ] = "server.pem"; (only if you set https and you must 
                                       generate your own certificate.  See the 
                                       CERTIFICATES section of this document 
                                       for help on generating your own 
                                       certificates)
oSP [ "passphrase" ] = "my_passphrase"; (only for https.  Passphrase for your 
                                         server certificate)

One of the following three must be specified (see Samples/ehs_test.cpp for 
examples on using each of the three modes)

oSP [ "mode" ] = "singethreaded" -- no dedicated thread for processing.  Main 
                                    process must call EHS::HandleData() in 
                                    order for web requests to be processed.

oSP [ "mode" ] = "threadpool" -- a set of dedicated threads will be set up for 
                                 handling web requests.  oSP [ "threadcount" ]=
                                 <number_of_threads> may be specified to set 
                                 the number of threads in the pool.  The 
                                 default is 1.  Note that setting this number 
                                 too high (>100?) may result in poor 
                                 performance.  

oSP [ "mode" ] = "onethreadperrequest" -- each time a request comes in, a new 
                                          thread is created.  When the request 
                                          has been processed, the thread is 
                                          destroyed.  This is *not* a good mode
                                          for dealing with lots of small 
                                          requests.  If you have bursts of 
                                          requests that can take a long time 
                                          (more than a few seconds), this may 
                                          be a good mode.  (I recommend being 
                                          very careful with this.  If you get  
                                          hit hard, it may bring your entire 
                                          system to a crawl with lots of 
                                          threads being spawned.)

oSP [ "norouterequest" ] = "1" -- means to disregard trying to route requests 
                                  through different EHS objects based on path.
                                  All requests will go to the EHS object that 
                                  had StartServer called on it.  You can still
                                  call RegisterEHS ( ) to set up a hierarchy,
                                  but child EHS objects will never be called.

oSP [ "maxrequestsize" ] = "262144" -- The maximum size of an incoming request.
                                       You may want to increase this, if you
                                       want to handle file uploads.
oSP [ "parsecontenttype" ] = "application/x-www-form-urlencoded"
                           -- By default, the request's POST body is always
                              parsed by scanning for URL-encoded form data.
                              Using this option, this can be limited to just
                              the given content type.
oSP [ "bindaddress" ] = "127.0.0.1" -- Specifies the address to bind to. The
                                       default is "0.0.0.0" which listens on
                                       all interfaces.

Start your server:
oEHS.StartServer ( oSP );
poEHS->StartServer ( oSP );

Now whenever you point a web browser at your specified port (4000 in above example), it will display the current time since the epoch.


Returning Custom Pages
-------------------------------

To return a custom page, you must subclass the EHS object and override 
HandleRequest(...).  

class MyEHS : public EHS {

	HttpResponse HandleRequest ( HttpRequest * ipoHttpRequest, 
                                 HttpResponse * ipoHttpResponse ) {

		HttpResponse->SetBody ( "Hello, HTTP World!", 
                                strlen ( "Hello, HTTP World!" ) );
		return HTTPRESPONSECODE_200_OK;

	}

}

The preceeding code will send the string "Hello, HTTP World!" with a 200 (OK) 
response code to the client.

The full list of response codes is in httpresponse.h.


Creating a virtual directory structure:
----------------------------------------

See Samples/ehs_test.cpp for an example of this.

NOTE: If the "norouterequest" option is sent to the server, the following
      code will NOT behave as described below.  In this case, no matter what
      the requested path, the A object will handle the request.  The following
      is the default behavior.

With EHS you can create class hierarchies that EHS automatically traverses 
based on the URL requested.  To do this, create multiple objects of classes 
that inherit from EHS.  Say they are variables A, B, and C.  To set up B as a
child of A with a path name of "childb", and then C as a child of B with a path
name of "childc", do 

the following:

MyEhsClass A, B, C;

A->RegisterEHS ( &B, "childb" );
B->RegisterEHS ( &C, "childc" );

To have a request sent automatically to the B object, create a URL similar to 
the following:

http://myserver.com/childb/

To have a request sent automatically to the C object, create a URL similar to 
the following:

http://myserver.com/childb/childc/

Note that the trailing / is required, as otherwise it doesn't denote a 
directory, it denotes a file.


Multi-part form attachments:
-----------------------------

See Samples/ehs_uploader.cpp for an example of how to use this feature.


Cookies ( as specified in RFC-2109 ):
--------------------

See: ehs_test.cpp for an example on using cookies.

Incoming:
EHS will break out cookies for you into the HttpRequest object's oCookieMap 
member.  The key of the map is the name of the cookie and the value of the map
is the value of the cookie.  Pretty simple.

Outgoing:
To set a cookie into a user's browser, set a CookieParameter object with the 
following fields:

The following fields are valid (required as noted):
Name (required): Name of the cookie
Value (required): Value of the cookie
Comment: Comment on the cookie.  For client-side use only
CommentURL: URL specifying a comment on the cookie.
Discard: Discard unconditionally when the user-agent terminates
Domain: Domain for which the cookie is valid
Max-Age: lifetime of the cookie in seconds (time from now, not since epoch)
Path: subset of URLs to which this cookie applies
Port: specifies teh port for which the cookie may be returned.  Requires quotes
      around the port list, but that will be added if not specified.
Secure: Has no value -- if specified, cookie should only be sent over secure 
        connection.  Client decides what is secure or not.
Version (required, but set to 1 if not specified): what version of state 
        management specification to which the cookie conforms.  
        1 is the current.

For example:

CookieParameters oCP;
oCP["name"]="mycookie";
oCP["value"]="myvalue";
oCP["max-age"]=60; // makes the cookie last a minute from now

Then call HttpResponse::SetCookie ( CookieParameters & );

as in:

ipoHttpResponse->SetCookie ( oCP );

For an actual example, see Samples/ehs_test.cpp.  



HTTPS:
------------

See Samples/ehs_https.cpp for an example of how to use this feature and see the
section in this document called "Certificates" for information on how to create
a certificate for HTTPS.  A certificate is REQUIRED for running an HTTPS 
server.


Certificates:
--------------
In order to use HTTPS, you must first have a server certificate to send to the
client.  This is how the server process that it is who it says it is and starts
the cryptography.

Generating a server certificate is not a quick process and involves a number of
steps.  The following is an example of how to make a server certificate that 
will allow an HTTPS connection to be established.

NOTE: I am not a certificate or crypto expert.  These steps work for me but I 
don't promise that it's the right way to do it.

Creating a root CA:
A CA is a Certificate Authority.  Verisign is an example of a CA. This is the 
base certificate that is used to verify all levels beneath it.  This 
certificate should ideally be distributed securely to the clients.  This is 
commonly done with the browser, but we don't have that option.  When you 
initially connect the browser to your HTTPS server, it will loudly complain 
that the certificate cannot be verified.  If you pay for a Versign (or other 
standard certificate authority) to sign your certificate, this won't happen.

> openssl req -newkey rsa:1024 -sha1 -keyout rootkey.pem -out rootreq.pem
This generates a new certificate request and a new private key.
  -newkey rsa:1024 creates a 1024-bit private key
  -sha1 specifies the message digest with which to sign the request
  -keyout rootkey.pem specifies the filename to write the private key to
  -out rootreq.pem specifies the file to write output to (the certificate 
   request)
  
Answer all the questions it gives you.

> openssl x509 -req -in rootreq.pem -sha1 -extensions v3_ca -signkey 
  rootkey.pem -out rootcert.pem

  -req says a certificate request instead of an actual certificate will be 
     provided (what we created in the previous step)
  -in rootreq.pem specifies the input filename from which to read a certificate
     request
  -sha1 specifies the digest to use.  
  -extensions v3_ca is required for self-signed CA certificates
  -signkey rootkey.pem says to sign this certificate using our previously 
     generated private key
  -out rootcert.pem writes our certificate out to rootcert.pem

> cat rootcert.pem rootkey.pem > root.pem

This puts the new certificate along with our encrypted private key into a 
single file.


Creating a server CA and signing it with our root CA:
Now that we have a root CA, we need a server CA.  I don't know why/if you have 
to do this, but if you do this, it will work.  Basically we're going to do the
same thing we just did, but with some different filenames.

> openssl req -newkey rsa:1024 -sha1 -keyout serverCAkey.pem -out 
  serverCAreq.pem
Answer all the same questions again.

> openssl x509 -req -in serverCAreq.pem -sha1 -extensions v3_ca -CA root.pem 
  -CAkey root.pem -CAcreateserial -out serverCAcert.pem

  -CA root.pem specifies the CA certificate to be used for signing.  x509 
     behaves like a 'mini CA'
  -CAkey root.pem specifies the CA private key to sign the certificate with.  
     (not necessary?)
  -CAcreateserial creates CA serial number file if it does not exist.  

> cat serverCAcert.pem serverCAkey.pem rootcert.pem > serverCA.pem


Now we actually create the server's certificate and sign it with the server CA.
This is the certificate we will actually load into EHS to send to the client.
Again, it looks an awful lot like the stuff we've done before.

> openssl req -newkey rsa:1024 -sha1 -keyout serverkey.pem -out serverreq.pem

> openssl x509 -req -in serverreq.pem -sha1 -extensions usr_cert -CA 
  serverCA.pem -CAkey serverCA.pem -CAcreateserial -out servercert.pem

Note that we're using a different extension here: usr_cert.  

> cat servercert.pem serverkey.pem serverCAcert.pem rootcert.pem > server.pem

The file, 'server.pem', is what we send to EHS in the 'certificate' parameter.
The passphrase set for in this last step will be the passphrase you will pass 
into EHS.  


ehs-stress.pl
--------------

ehs-stress is a tool contributed by Michal Pleban for creating many 
simulataneous HTTP requests.  It creates multiple processes, each of which 
stream multiple HTTP requests to a web server (EHS or other).  It can either
connect once per request, or use the same connection for all requests from
each process (-k option).  

Note: Apache bench (ab) does similar things, but I've never been able to make
        it work properly.  ab 2.0.40 doesn't seem to like the reply from EHS,
        but it also hangs against my Apache server, as well, so I don't put
        much faith in it.  Any input here would be appreciated, especially
        if EHS's behaviour is incorrect in some way.

Usage: ehs-stress.pl hostname:port [-p <num>] [-c <count>] [-k]
	   -p: specifies number of processes to run.  Each does <count> requests
	         Default: 10
	   -c: specifies how many requests each process should do.  Default: 10
	   -k: keep-alive.  Use persistent connections.  Default: No


ehs-stress was contributed by Michal Pleban.  It requires you to install some
perl modules.  This is most easily accomplished as root.  It can be done as a 
normal user, but I don't know how, so I'll just describe how to do it as root:
Note: You may have these installed.  Just try running it.  If it works, you
        already have them and should skip the install described below.

su -
perl -MCPAN -e shell
<it might ask you if you want to do manual configuration -- say no (unless you 
 want to say yes)>

install LWP::UserAgent
<it will ask if you want to install a bunch of stuff.  I'd say yes>
<it may ask if you want to add a list of dependencies to the install queue.  
 Say yes>
<going with the defaults should be safe>

And that should do it. 
