A simple web server that can be bound to either the EthernetInterface or the WiflyInterface.

Dependents:   Smart-WiFly-WebServer WattEye X10Svr SSDP_Server

SW_HTTPServer.h

Committer:
WiredHome
Date:
2013-05-31
Revision:
0:729320f63c5c
Child:
1:54353af0d20a

File content as of revision 0:729320f63c5c:

/// SW_HTTPServer is a simple web server using the WiFly module.
/// 
/// Partially derived from nweb
///  http://www.ibm.com/developerworks/systems/library/es-nweb/sidefile1.html
///
/// Uses a modified WiflyInterface - a number of performance issues
/// were identified and resolved in the local version.
///
/// Also, if nothing visits its web page for a long time, it seems
/// to disable. Might be a sleep timer I'm not spotting.
///
/// Given: scheme://domain:port/path?query_string#fragment_id
/// @li scheme is "http"
/// @li domain is whatever IP the server has
/// @li port is the registered port
/// @li /path is the reference to the file (actual or logical) on the server
/// @li query_string is any combination of name=value pairs
/// @li fragment_id is a reference to an anchor on the page
///
/// @todo make it non-blocking.
/// @todo hunt down lengthy operations - there seems to be a long timeout somewhere.
/// @todo move part of the POST method handler to the registered handler, so
///       it can decide if it should allocate the needed memory.
/// @todo transform the pc serial interface to a log interface, which might
///       be more useful.
/// @todo parse the header similar to the query string, and then make
///       those parameters accessible - perhaps like environment vars.
///
/// @note Copyright © 2013 by Smartware Computing, all rights reserved.
///     Individuals may use this application for evaluation or non-commercial
///     purposes. Within this restriction, changes may be made to this application
///     as long as this copyright notice is retained. The user shall make
///     clear that their work is a derived work, and not the original.
///     Users of this application and sources accept this application "as is" and
///     shall hold harmless Smartware Computing, for any undesired results while
///     using this application - whether real or imagined.
///
/// @author David Smart, Smartware Computing
///
#ifndef SW_HTTPSERVER_H
#define SW_HTTPSERVER_H
#include "mbed.h"
//#include "MODSERIAL.h"    // would like to hook in mod serial for higher performance, less blocking
#include "Wifly.h"
#include "TCPSocketServer.h"
#include "TCPSocketConnection.h"

#ifdef MODSERIAL_H
#define PC MODSERIAL
#else
#define PC Serial
#endif


class HTTPServer
{
public:
    /**
    * namevalue pairs for parameters
    */
    typedef struct {
        char * name;
        char * value;
    } namevalue;

    /** 
    * callback prototype for custom handler
    *
    * @param svr is a handle to this class, so the callback has access to member functions
    * @param params is a pointer to an array of name value pairs
    * @paramcount is the number of parameters.
    */
    typedef void (* Handler)(HTTPServer * svr, const char * path, const namevalue *params, int paramcount);
    
    /**
    * Create the HTTPServer object.
    * 
    * @param wifly is the serial port with the wifly interface.
    * @param port is the optional parameter for the port number to use, default is 80.
    * @param webroot is a file system path to the root folder for the web space.
    * @param maxparams defines the maximum number of parameters to a dynamic function (and the memory to support them).
    * @param maxdynamicpages defines the maximum number of dynamic pages that can be registered.
    * @param pc is the serial port for debug information (I should transform this to a log interface)
    */
    HTTPServer(Wifly * wifly, int port = 80, const char * webroot = "/", int maxparams = 30, int maxdynamicpages = 10, PC * pc = NULL);
    
    /**
    * destructor, which can clean up a few things
    */
    ~HTTPServer();
    
    /**
    * the process to call whenever there is free time, as this basically does
    * all the work to monitor for connections and handle replies.
    */
    void ip_process();
    
    /**
    * Send a header back to the client and automatically appends a "\r\n". Each parameter must
    * send a "\r\n" as part of that string.
    *
    * @param code is the optional return code; 200 = OK, if not provided then 404 = Not found is returned
    * @param code_text is the text to align with the code (e.g. 404, "Not Found")
    * @param content_type is a pointer to "Content-Type: text/html\r\n" (for example)
    * @param optional_text is a pointer to any other text that is part of the header
    */
    void header(int code = 404, const char * code_text = "Not Found", const char * content_type = NULL, const char * optional_text = NULL);

    /**
    * Send text to the client
    *
    * @param msg is the text string to send
    * @param bytes is the number of bytes to send. If not set, then strlen is calculated.
    */
    void send(const char * msg, int bytes = -1);
    
    /**
    * Send a file to the client, including the header
    *
    * @param filename is the fully qualified path and filename
    * @param filetype is the header information (e.g. "Content-Type: text/html")
    * @return true if it thinks it sent ok, false otherwise.
    */
    bool SendFile(const char * filename, const char * filetype);
    
    /** 
    * register a handler for a specific URL.
    *
    * @param path to register
    * @param callback of type Handler
    * @return true if successfully registered
    */
    bool RegisterHandler(const char * path, Handler callback);
    
    /**
    * determine if the named file is a supported type (e.g. .htm, .jpg, ...)
    *
    * @param filename is the filename to test, based on the extension
    * @return pointer to a Content-Type string if supported, or NULL if not.
    */
    const char * GetSupportedType(const char * filename);

    /**
    * search the available parameters for 'name' and if found, return the 'value'
    *
    * @param name is the name to search for
    * @return pointer to the value, or NULL
    */
    const char * GetParameter(const char * name);

    /**
    * parse the text string into name=value parameters. This will directly
    * modify the referenced string. If there is a #fragment_id on the end
    * of the string, that will be removed.
    *
    * @param pString is a pointer to the string.
    */
    void ParseParameters(char * pString);
    
    /**
    * Unescape string converts a coded string "in place" into a normal string
    * this    "This%20is%20a%20question%3F%20and%20an%20answer."
    * becomes "This is a question? and an answer."
    * @note '+' is another form of space, so is converted to a space before the %xx
    *
    * @param encoded string to be converted
    */
    void UnescapeString(char * encoded);
    
    /**
    * Get the IP address of the remote node to which we are connected.
    * @caution this switches the module into, and out of, command mode
    *          which has quite a time penalty.
    */
    void GetRemoteAddr(char * str, int size);

    /** 
    * used to force a connection to close
    */
    void close_connection();

private:
    Wifly * wifly;
    char * webroot;
    PC * pc;
    Timer * timer;
    TCPSocketServer * server;
    TCPSocketConnection client;
    char * rewriteWithDefaultFile(char * queryString);
    char * rewritePrependWebroot(char * queryString);
    int maxparams;
    namevalue *params;
    int paramcount;
    
    typedef struct {
        char * path;
        Handler callback;
    } handler;
    int maxdynamicpages;
    handler *handlers;
    int handlercount;
    
    /**
    *  Extract the message from the record, by searching for the needle
    *  the string of interest follows the needle, and may be ' ' delimited
    *  Can damage haystack while processing it.
    *
    * @param haystack is the record to search
    * @param needle is the text to search for, which precedes the text to return
    * @param string is the text following the needle
    * @return true if it extracted something successfully
    */
    bool Extract(char * rec, char * needle, char ** string);

    int HexCharToInt(char c);
    char HexPairToChar(char * p);
};
#endif //SW_HTTPSERVER_H