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-06-02
Revision:
2:a29c32190037
Parent:
1:54353af0d20a
Child:
3:17928786bdb5

File content as of revision 2:a29c32190037:


#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

/// 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
///
/// Feature request list:
/// @todo make it non-blocking.
/// @todo hunt down lengthy operations - there seems to be a long timeout somewhere.
/// @todo parse the header similar to the query string, and then make
///       those parameters accessible - perhaps like environment vars.
/// @todo move part of the POST method handler to the registered handler, so
///       it can decide if it should allocate the needed memory.
/// @todo Add password capability to some web pages
/// @todo transform the pc serial interface to a log interface, which might
///       be more useful.
/// @todo Add ability to put WiFly in AP mode and then configuration pages
///       to find and join a network.
/// @todo Add ability to change/update SW in the WiFly module
/// @todo Add ability to upload a new application to the mbed
///
/// History:
/// @li 20130530 Initial version
/// @li 20130601 Renamed ip_process to Poll
///
/// @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
///
class HTTPServer
{
public:
    /**
    * namevalue pairs for parameters
    */
    typedef struct {
        char * name;
        char * value;
    } namevalue;
    
    /**
    * Indicates the purpose of the callback
    */
    typedef enum {
        CONTENT_LENGTH_REQUEST, ///<!- ask the user if they wish to accept the data
        DATA_TRANSFER,
        SEND_PAGE,              ///<! the activated method should now send the page
    } CallBackType;

    /** 
    * callback prototype for custom handler
    *
    * This callback gets overloaded for a few purposes, which can be identified by the CallBackType parameter
    * @li SEND_PAGE - the callback should now send the html page, using as many svr->send() as needed. 
    *       When the callback returns, it should always indicate true.
    * @li CONTENT_LENGTH_REQUEST - the server is asking the callback if it wants to receive the message,
    *        which may require significant memory. If the request is accepted, true should be returned.
    *        If the request is denied, false should be returned.
    * 
    * @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.
    * @return true if command was accepted
    */
    typedef bool (* Handler)(HTTPServer * svr, CallBackType type, 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.
    *
    * 20130601 Renamed from ip_process to Poll
    */
    void Poll();
    
    /**
    * 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