/**
 * @brief    This is the server component of the application I'm building.
 *           I am publishing it only because of the performance benchmarks
 *           in my blog.
 *
 *           This is an embedded purely asynchronous server that works
 *           as fast as Apache in serving simple GET requests.
 *
 *           This test server will only serve "index.htm" from the
 *           current folder. The full application will come later as I
 *           develop it.
 *
 * @author
 *      Comments/Bugs/Feedback: the.brown.dragon.blog@gmail.com
 *                              http://the-brown-dragon.com/
 *
 * @note
 *  Further improvements in performance may be possible if I build
 *  a custom memory allocator.
 *
 * @note
 *  Known bugs/issues are listed at the end of this file.
 */

/*****************************************************************************
 * BASIC
 ****************************************************************************/
/**
 * @brief Standard Includes
 */
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <memory.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>

/**
 * @brief C99-like typedefs.
 */
typedef void           Void;
typedef void*          vptr_t;
typedef int8_t         byte_t;
typedef int8_t*        byteptr_t;
typedef char           char_t;
typedef char_t*        strptr_t;
typedef int            bool_t;

/**
 * @brief Basic allocator - crashes when unable to allocate.
 *        (good enough strategy for this application).
 * @param[in] pSize The amount of memory to allocate in bytes.
 * @return void* a pointer to the allocated memory.
 * @note
 *  This function will exit() the application if it cannot
 *  succeed (i.e. we are not expecting to work in a
 *  limited-memory environment).
 */
static inline void *
xmalloc (size_t pSize)
{
        vptr_t  v;

    /* Allocate or die! */
    if (!(v = malloc (pSize))) {
        perror ("Out of Memory!");
        exit (EXIT_FAILURE);
    }

    /* Return triumphantly bearing memory */
    return v;
}

/**
 * @brief This macro performs the general function
 *        of expanding a given area in a block-wise
 *        fashion.
 *        This is useful in several cases. Some examples:
 *          - A string needs to resize as it is filled
 *          - An array needs to resize as it is filled
 *          - A keymap needs to resize as it is filled.
 * @param      w            'w'hat should be expanded.
 * @param        cs         'c'urrent 's'ize of whatever it is.
 * @param           us      'u'tilized 's'ize of whatever it is.
 * @param              exp  'exp'ansion block size.
 * @note
 *  Like all non-hygienic macros, this suffers from variable capture.
 *  The variables that will be captured are tmp_232 and tmpsz_232,
 *  which in practice should not conflict with actual names.
 */
#define XPAND_(w,cs,us,exp) do {\
                                if ((us)==(cs)) {\
                                        vptr_t    tmp_232 = (w);\
                                        uint32_t  tmpsz_232 = (cs);\
                                    (cs) += exp;\
                                    (w) = xmalloc ((cs));\
                                    memcpy ((w), tmp_232, tmpsz_232);\
                                    free (tmp_232);\
                                }\
                            } while (0)

/*****************************************************************************
 * HTTP SERVER
 ****************************************************************************/
/**
 * @brief The various states that the user connection
 *        function must handle.
 */
typedef enum {
    BD_CS_GOTCONNECTION,
    BD_CS_CONNECTIONCLOSED,
    BD_CS_GOTHEADER,
    BD_CS_LOADRESPONSE,
    BD_CS_RESPONSEDONE,
} bd_connstate_t;

/**
 * @brief This structure contains the data used
 *        by the users's connection function.
 */
typedef struct {

    /* This variable is set by the user if
     * he/she requires a per-connection context */
    vptr_t          uCTxT;

    /* The item requested */
    strptr_t        uRequest;

    /* When state is BD_LOADRESPONSE
     * the callback sets these values
     * and finally setting uResponseDataSize to -1
     * when finished writing.
     * It is recommended the callback does as
     * much as possible asynchronously to keep
     * the server responsive */
    vptr_t          uResponseData;
    int32_t         uResponseDataSize;

} bd_conn_t;


/**
 * @brief The size of the read buffer.
 */
#define BD_SVR_BUFSZ     256
/**
 * @brief The maximum number of connections
 *        handled.
 */
#define BD_SVR_POOL_SZ     8

/**
 * @brief The various states the server/a connection can be in.
 */
typedef enum {
    BD_START    =   1,
    BD_MKSOCKET, BD_CONVERTIPADD, BD_BIND, BD_LISTEN, BD_FCTL,
    BD_SERVERSTATESENDS = 100,
    BD_ACCEPT_NONBLOCK, BD_GOTCONNECTION,
    BD_CONNECTIONCLOSED,
    BD_READHDR, BD_READLINE, BD_HANDLEREAD, BD_GOTLINE, BD_GOTHEADER,
    BD_PARSEHDR_ERR,
    BD_LOADRESPONSE, BD_WRITINGDATA, BD_RESPONSEDONE,
    /* User uses these states to control the server. */
    BD_CLOSECONNECTION, BD_SHUTDOWNSERVER, BD_CONTINUE,
} bd_svrstate_t;

/**
 * @brief A connection context.
 * @note
 *  This structure is also used
 *  as the server context when
 *  the states are < BD_SERVERSTATESENDS
 */
typedef struct {

    /* User-data */
    bd_conn_t       uUD;

    /* The current state of the connection */
    bd_svrstate_t  uState;

    /* Read buffer */
    byte_t          uReadData[BD_SVR_BUFSZ];
    int32_t         uRdSz;
    int32_t         uRP;
    strptr_t        uCurLine;
    uint32_t        uCLSz;
    uint32_t        uCP;

    /* Write Buffer (buffer in user data) */
    int32_t         uRsP;


} bd_conctx_t;

/**
 * @brief Parses the first line of a HTTP request.
 *        Basically skips the request type and
 *        URLDecodes the request.
 * @param[in] pHTTPReq The request line.
 * @return strptr_t A newly allocated string containing
 *                  the actual request.
 */
static strptr_t
sParseRequest (strptr_t pHTTPReq)
{
        int32_t  i  , j, k;
        char_t   v;
        strptr_t r;

    /* We service only GET requests right now
     * as that will let us proceed with other components.
     * POST request support can come later if needed. */
    if (strncmp (pHTTPReq, "GET ", 4)) return NULL;

    /* Skip over the GET part. We are now at the start
     * of the requested resource. */
    pHTTPReq += 4;

    /* Look for the end of the requested resource */
    for (i = 0; pHTTPReq [i] && pHTTPReq [i] != ' ';++i);
    /* Didn't find it! */
    if (!pHTTPReq [i]) return NULL;

    /* Copy the request into a newly created buffer */
    r = xmalloc (i+1);
    for (j = k = 0;j < i;++j,++k) {
        /* Case: URL-encoded value */
        if (pHTTPReq [j] == '%') {
            /* Error: URL-encoded must have a 2-char hex */
            if ((j + 3) > i) {
                free (r);
                return NULL;
            }

#define PR_CHAR2NUM do {\
            /* Maybe between 0-9 */\
            v = pHTTPReq [j] - '0';\
            /* No? */\
            if (v > 9) {\
                /* Maybe between a-f */\
                v = (pHTTPReq [j] | 0x20) - 'a';\
                /* No? - Quit with error */\
                if ((v < 0) || (v > 5)) {\
                    free (r);\
                    return NULL;\
                }\
                /* Ok - adjust to actual value */\
                v += 10;\
            }\
            /* Not in range - Quit with error */\
            if (v < 0) {\
                free (r);\
                return NULL;\
            }\
    } while (0)

            /* Convert the first character to a number */
            ++j; PR_CHAR2NUM;
            r [k] = v;
            /* And add the second character as a number */
            ++j; PR_CHAR2NUM;
            r [k] = (r[k] * 0x10) + v;

        } else {
            /* Copy source to destination */
            r [k] = pHTTPReq [j];
        }
    }
    /* NULL-terminate the string */
    r [i] = 0;
    /* Return it. */
    return r;

#undef PR_CHAR2NUM
}

/**
 * @brief This macro is used to define a state handling
 *        function for the server (sConnectionDo).
 */
#define BD_SVRSTATE_HANDLER(h) \
static bd_svrstate_t\
  s##h (\
        int pConn, bd_conctx_t *pCC,\
        int32_t (*pErrFunc)(bd_svrstate_t pState, vptr_t pCTxT),\
        int32_t (*pConnectionFunc) (bd_connstate_t pState,\
                                    bd_conn_t *pConn, vptr_t pCTxT),\
        vptr_t pCTxT)

/**
 * @brief BD_READHDR state handler.
 */
BD_SVRSTATE_HANDLER (BD_READHDR)
{
    /* If needed, allocate space for current line */
    if (!pCC->uCLSz) {
        assert (!pCC->uCurLine);
        pCC->uCLSz    = BD_SVR_BUFSZ;
        pCC->uCurLine = xmalloc (pCC->uCLSz);
        pCC->uCP      = 0;
    }
    /* Clean up old line and read header information */
    pCC->uCP   = 0;
    pCC->uRdSz = 0;
    pCC->uRP   = 0;
    free (pCC->uUD.uRequest);
    pCC->uUD.uRequest = NULL;
    /* Enter BD_READLINE state */
    return BD_READLINE;
}

/**
 * @brief BD_READLINE state handler.
 */
BD_SVRSTATE_HANDLER (BD_READLINE)
{
        ssize_t        rd;
        bd_svrstate_t ua;

    /* Non-blocking read */
    if ((rd=read (pConn, pCC->uReadData, BD_SVR_BUFSZ)) != -1) {
        /* Read something - enter BD_HANDLEREAD */
        pCC->uRdSz = rd;
        pCC->uRP   = 0;
        return BD_HANDLEREAD;
    }

    /* Nothing read? Is it an i/o block? Continue with other things */
    if (errno == EAGAIN) return BD_CONTINUE;

    /* Is it a connection problem? Close the connection */
    if (errno == ECONNRESET) return BD_CLOSECONNECTION;
    if (errno == ENOTCONN) return BD_CLOSECONNECTION;
    if (errno == ETIMEDOUT) return BD_CLOSECONNECTION;

    /* No? Inform user about error and do what he asks */
    ua = pErrFunc (pCC->uState, pCTxT);
    if ((ua == BD_CLOSECONNECTION) || (ua == BD_SHUTDOWNSERVER)) return ua;

    /* Ok just continue in current state */
    return pCC->uState;
}

/**
 * @brief BD_HANDLEREAD  state handler.
 */
BD_SVRSTATE_HANDLER (BD_HANDLEREAD)
{
    /* Walk read and unprocessed data */
    while (pCC->uRP < pCC->uRdSz) {

        /* Ignore CR as specified in RFC 1945 Appendix B */
        if (pCC->uReadData [pCC->uRP] != '\r') {
            /* Expand buffer if needed */
            XPAND_(pCC->uCurLine, pCC->uCLSz, pCC->uCP, BD_SVR_BUFSZ);
            /* EOL? */
            if (pCC->uReadData [pCC->uRP] == '\n') {
                /* End string */
                pCC->uCurLine [pCC->uCP] = 0;
                ++pCC->uRP;
                /* Enter BD_GOTLINE state */
                return BD_GOTLINE;

            /* Not EOL? */
            } else {
                /* Copy into buffer */
                pCC->uCurLine [pCC->uCP] = pCC->uReadData [pCC->uRP];
            }
            /* Point to next location */
            ++pCC->uCP;
        }
        /* Continue walk */
        ++pCC->uRP;
    }

    /* Walk done? Read more data */
    return BD_READLINE;
}

/**
 * @brief BD_GOTLINE state handler.
 */
BD_SVRSTATE_HANDLER (BD_GOTLINE)
{
    /* Read buffer empty? Signals end of header */
    if (!*pCC->uCurLine) return BD_GOTHEADER;

    /* Is this first line? */
    if (!pCC->uUD.uRequest) {
        /* Parse first line */
        pCC->uUD.uRequest = sParseRequest (pCC->uCurLine);
        /* If we fail to parse request - close the connection */
        if (!pCC->uUD.uRequest) {
            pErrFunc (BD_PARSEHDR_ERR, pCTxT);
            return BD_CLOSECONNECTION;
        }

    /* Otherwise */
    } else {
         /* TODO: Parse name/value pairs */
    }
    /* Finished processing content */
    pCC->uCP = 0;

    /* Enter BD_HANDLEREAD state to process remaining read data */
    return BD_HANDLEREAD;
}

/**
 * @brief BD_GOTHEADER state handler.
 */
BD_SVRSTATE_HANDLER (BD_GOTHEADER)
{
        bd_svrstate_t  ua;

    /* Call handler function and adjust flow if needed */
    ua = pConnectionFunc (BD_CS_GOTHEADER, (bd_conn_t*)pCC, pCTxT);
    if ((ua == BD_CLOSECONNECTION) || (ua == BD_SHUTDOWNSERVER)) return ua;

    /* Switch state to BD_LOADRESPONSE */
    return BD_LOADRESPONSE;
}

/**
 * @brief BD_LOADRESPONSE state handler.
 */
BD_SVRSTATE_HANDLER (BD_LOADRESPONSE)
{
        bd_svrstate_t  ua;

    /* Call handler function and adjust flow if needed
     * NB: User must change state here to BD_RESPONSEDONE
     *     otherwise he will stay here forever (we can't
     *     know when user has finished writing). */
    ua = pConnectionFunc (BD_CS_LOADRESPONSE, (bd_conn_t*)pCC, pCTxT);
    if ((ua == BD_CLOSECONNECTION) || (ua == BD_SHUTDOWNSERVER)) return ua;
    /* Reset response pointer */
    pCC->uRsP = 0;
    /* If user has finished writing,
     * we change state to BD_RESPONSEDONE */
    if (pCC->uUD.uResponseDataSize == -1) {
        pCC->uUD.uResponseDataSize = 0;
        return BD_RESPONSEDONE;
    }
    /* Otherwise we just write the current response (BD_WRITINGDATA) */
    return BD_WRITINGDATA;
}

/**
 * @brief BD_WRITINGDATA state handler.
 */
BD_SVRSTATE_HANDLER (BD_WRITINGDATA)
{
        ssize_t        wr;
        bd_svrstate_t  ua;

    /* Nothing left to write? Go back to BD_LOADRESPONSE to get more data */
    if (pCC->uRsP == pCC->uUD.uResponseDataSize) return BD_LOADRESPONSE;
    assert (pCC->uRsP < pCC->uUD.uResponseDataSize);

    /* Non blocking write */
    if ((wr = write (pConn,
                    ((byteptr_t)pCC->uUD.uResponseData) + pCC->uRsP,
                    pCC->uUD.uResponseDataSize - pCC->uRsP)) != -1) {
        /* Update written amount */
        pCC->uRsP += wr;
        /* Just continue in same state */
        return pCC->uState;
    }

    /* Write failed. Is it an i/o block? Continue with other things */
    if (errno == EAGAIN) return BD_CONTINUE;

    /* Is it a connection error? Close the connection */
    if (errno == ECONNRESET) return BD_CLOSECONNECTION;
    if (errno == EPIPE) return BD_CLOSECONNECTION;

    /* Call error function and adjust flow if needed */
    ua = pErrFunc (pCC->uState, pCTxT);
    if ((ua == BD_CLOSECONNECTION) || (ua == BD_SHUTDOWNSERVER)) return ua;
    /* Just keep writing */
    return pCC->uState;
}

/**
 * @brief BD_RESPONSEDONE state handler.
 */
BD_SVRSTATE_HANDLER (BD_RESPONSEDONE)
{
        bd_svrstate_t ua;

    /* BD_RESPONSEDONE is simply the ending state of BD_WRITINGDATA */
    ua = sBD_WRITINGDATA (pConn, pCC, pErrFunc, pConnectionFunc, pCTxT);

    /* BD_WRITINGDATA is not yet done - continue */
    if (ua != BD_LOADRESPONSE) return ua;

    /* Inform user to clean up his data */
    ua = pConnectionFunc (BD_CS_RESPONSEDONE, (bd_conn_t*)pCC, pCTxT);
    if ((ua == BD_CLOSECONNECTION) || (ua == BD_SHUTDOWNSERVER)) return ua;

    /* Go back to reading next request */
    return BD_READHDR;
}

/**
 * @brief BD_RunServer helper function than handles each connection.
 *        The state of each connection is managed in it's context
 *        and this function is basically a state machine.
 * @param[in,out]   pConn       The Connection.
 * @param[in,out]   pCC         The state of the connection.
 * @param[in]       pErrFunc    Callback for handling errors.
 * @param[in]       pConnectionFunc Callback to handle connections.
 * @param[in]       pCTxT       User context (passed to callbacks).
 * @return int  Passes through the return values of the error function
 *              (pErrFunc) and the connection function (pConnectionFunc).
 * @note
 * The return values control the flow of the server:
 *     BD_CONTINUE        - the server will continue normal flow
 *     BD_CLOSECONNECTION - the server will discard the current connection
 *     BD_SHUTDOWNSERVER  - the server will shutdown
 *     any other value will simply be ignored and the server will continue.
 */
static int
sConnectionDo (
        int pConn, bd_conctx_t *pCC,
        int32_t (*pErrFunc)(bd_svrstate_t pState, vptr_t pCTxT),
        int32_t (*pConnectionFunc) (bd_connstate_t pState,
                                    bd_conn_t *pConn, vptr_t pCTxT),
        vptr_t pCTxT)
{
        bd_svrstate_t  ns;

#define CD_SM_HANDLE(st) case st: \
                            ns = s##st (pConn, pCC,\
                                        pErrFunc, pConnectionFunc,\
                                        pCTxT);\
                         break

    /* This is the SM that processes the current connection
     * Everything happens here and we only exit if explicitly
     * asked by the user or if any operation would result
     * in a blocking call.
     * NB: Users should also not block here depending on their
     *     volume requirements because it would slow down
     *     processing of other requests (if any). */
    for (;;) {
        /* State Machine */
        switch (pCC->uState) {
            CD_SM_HANDLE (BD_READHDR);
            CD_SM_HANDLE (BD_READLINE);
            CD_SM_HANDLE (BD_HANDLEREAD);
            CD_SM_HANDLE (BD_GOTLINE);
            CD_SM_HANDLE (BD_GOTHEADER);
            CD_SM_HANDLE (BD_LOADRESPONSE);
            CD_SM_HANDLE (BD_WRITINGDATA);
            /* CD_SM_HANDLE (BD_RESPONSEDONE); */
            case BD_RESPONSEDONE:
                ns = sBD_RESPONSEDONE (pConn, pCC,
                                       pErrFunc, pConnectionFunc,
                                       pCTxT);
                if (ns == BD_READHDR) {
                    /* This line makes our server a simple HTTP 1.0 server
                     * - it closes the the connection after each response.
                     * Note that it is simple to extend this framework to
                     * become HTTP 1.1 compliant if needed. */
                    return BD_CLOSECONNECTION;
                }
            break;
            default:
                /* Should never reach here! */
                assert (0);
        }
        switch (ns) {
            case BD_CLOSECONNECTION:
            case BD_SHUTDOWNSERVER:
            case BD_CONTINUE:
                /* These states break out of the state machine */
                return ns;
            default:
                /* Continue with new state */
                pCC->uState = ns;
        }
    }
#undef CD_SM_HANDLE
}

/**
 * @brief Start and run a HTTP server.
 * @param[in]   pIP         IP to bind to (NULL implies INADDR_ANY)
 * @param[in]   pPort       Port to bind to.
 * @param[in]   pErrFunc    Callback for handling errors.
 * @param[in]   pBackgroundTask Callback to perform background tasks.
 * @param[in]   pConnectionFunc Callback to handle connections.
 * @param[in]   pCTxT       User context (passed to callbacks).
 * @return int  EXIT_SUCCESS on completion, else EXIT_FAILURE
 * @note
 * The error function (pErrFunc) and connection function (pConnectionFunc)
 * can return values to control the flow of the server.
 * If they return:
 *     BD_CONTINUE        - the server will continue normal flow
 *     BD_CLOSECONNECTION - the server will discard the current connection
 *     BD_SHUTDOWNSERVER  - the server will shutdown
 *     any other value will simply be ignored and the server will continue.
 */
int BD_RunServer (
        strptr_t pIP,
        int32_t pPort,
        int32_t (*pErrFunc)(bd_svrstate_t pState, vptr_t pCTxT),
        void (*pBackgroundTask) (vptr_t pCTxT),
        int32_t (*pConnectionFunc) (bd_connstate_t pState,
                                    bd_conn_t *pConn, vptr_t pCTxT),
        vptr_t pCTxT
        )
{
        int                 sd;
        struct sockaddr_in  sa;
        int                 cc;
        int                 cd[BD_SVR_POOL_SZ + 1];   /* +1 = guard value */
        bd_conctx_t        *cctx;
        bool_t              dosleep;

    /* This macro is helpful in setting the current state */
#define BD_SETCURSTATE(s) (cctx[cc].uState = (s))
    /* This macro is useful when handling user control flow */
#define BD_DOUSERFLOW(f) \
    switch ((f)) {\
        case BD_CLOSECONNECTION:\
            /* Inform user about abrupt closure */\
            (Void)pConnectionFunc (BD_CS_CONNECTIONCLOSED,\
                                  (bd_conn_t*)&cctx[cc], pCTxT);\
            /* Flush any pending data */\
            (Void)shutdown (cd[cc], SHUT_RDWR);\
            /* Release associated resources */\
            (Void)close (cd[cc]);\
            /* Clean up */\
            cd[cc] = 0;\
            memset (&cctx[cc], 0, sizeof (bd_conctx_t));\
            break;\
        case BD_SHUTDOWNSERVER:\
            /* Refer [religious] comment on goto usage */\
            goto done;\
    }
    /* This macro helps in socket cleanup */
#define BD_SHUTDOWNCONNECTIONS do {\
    for (cc = 0;cc < BD_SVR_POOL_SZ;++cc) {\
        if (cd[cc]) {\
            (Void)shutdown (cd[cc], SHUT_RDWR);\
            (Void)close (cd[cc]);\
            cd[cc] = 0;\
        }\
    }\
} while (0)

    /* Set up the connection context pool */
    cctx = xmalloc (sizeof (bd_conctx_t) * BD_SVR_POOL_SZ);
    memset (cctx, 0, sizeof (bd_conctx_t) * BD_SVR_POOL_SZ);

    /* Initialize the connection descriptors */
    memset (cd, 0, sizeof (cd));
    /* Start off with the first connection context
     * The error callbacks will recognize this as a server context
     * because the states will be < BD_SERVERSTATESENDS */
    cc = 0;

    /* Create a socket */
    BD_SETCURSTATE (BD_MKSOCKET);
    if ((sd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) goto err;
    /* Bind to the port and Ip given */
    memset (&sa, 0, sizeof (sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons (pPort);
    if (pIP) {
        BD_SETCURSTATE (BD_CONVERTIPADD);
        if (!inet_pton (AF_INET, pIP, &sa.sin_addr)) goto err;
    } else {
        sa.sin_addr.s_addr = INADDR_ANY;
    }
    BD_SETCURSTATE (BD_BIND);
    if (bind (sd, (const void*)&sa, sizeof (sa)) == -1) goto err;
    /* Prepare for stream-based connections */
    BD_SETCURSTATE (BD_LISTEN);
    if (listen (sd, SOMAXCONN) == -1) goto err;
    /* Set mode to non-blocking so we don't need multiple threads */
    BD_SETCURSTATE (BD_FCTL);
    if (fcntl (sd, F_SETFL, O_NONBLOCK) == -1) goto err;

    /* Enter main loop to handle requests */
    for (;;) {

        /* Find a currently free connection slot */
        for (cc = 0;cd [cc];++cc);
        /* If we have not hit the guard value
         * we have slots left for accepting connections */
        if (cc < BD_SVR_POOL_SZ) {
            /* Check if there is an incoming connection */
            BD_SETCURSTATE (BD_ACCEPT_NONBLOCK);
            if ((cd [cc] = accept (sd, NULL, NULL)) == -1) {
                /* No? Clear connection */
                cd [cc] = 0;
                /* Is it an error */
                if (errno != EAGAIN) {
                    /* Inform caller and do what he asks */
                    if (pErrFunc(cctx[cc].uState, pCTxT)==BD_SHUTDOWNSERVER) {
                        /* Springing out of the loop ( with apologies to
                         * Wirth/Dijkstra! :-D )
                         * NB: If you have religious problems with the use
                         * of GOTO, please re-read the original paper.
                         * This usage of goto does not invalidate any
                         * independent coordinates (i.e. it is structured).*/
                        goto done;
                    }
                }
            } else {
                /* Got a connection! Inform caller and do what he asks */
                BD_SETCURSTATE (BD_GOTCONNECTION);
                BD_DOUSERFLOW (pConnectionFunc (BD_CS_GOTCONNECTION,
                                                (bd_conn_t*)&cctx[cc],
                                                pCTxT));
                /* If the connection is still valid we enter reading state */
                if (cd[cc]) BD_SETCURSTATE (BD_READHDR);
            }
        }

        /* Let's give our caller a chance to do background stuff */
        if (pBackgroundTask) pBackgroundTask (pCTxT);

        /* Prepare to sleep if we don't do anything */
        dosleep = 1;
        /* Ok - let's perform read/writes on valid contexts */
        for (cc = 0;cc < BD_SVR_POOL_SZ;++cc) {
            if (cd [cc]) {
                BD_DOUSERFLOW (sConnectionDo (cd [cc], &cctx [cc],
                               pErrFunc, pConnectionFunc, pCTxT));
                /* In the middle of doing something - no sleeping on the job */
                if (cctx[cc].uState != BD_READHDR) dosleep = 0;
            }
        }

        /* We introduce a miniscule delay to stop the CPU spinning */
        if (dosleep) usleep (1);
        /* And so we go again.... */
    }

done:
    /* Done path */
    BD_SHUTDOWNCONNECTIONS;
    (Void)close (sd);
    free (cctx);
    return EXIT_SUCCESS;

err:
    /* Error path */
    if (pErrFunc) (Void)pErrFunc (cctx[cc].uState, pCTxT);
    BD_SHUTDOWNCONNECTIONS;
    if (sd != -1) (Void)close (sd);
    free (cctx);
    return EXIT_FAILURE;

#undef BD_SHUTDOWNCONNECTIONS
#undef BD_DOUSERFLOW
#undef BD_SETCURSTATE

}

/*****************************************************************************
 * APPLICATION
 ****************************************************************************/
/**
 * @brief Context state.
 */
typedef enum {
    AS_START = 0,
    AS_GOTFILE,
    AS_DONE,

    AS_ERRORS = 1024,
    AS_ERR_GOTFILE,
    AS_ERR_GOTFILEASYNC,
    AS_ERR_GOTFILEINFO,
    AS_ERR_GOTEMPTYFILE,
} bd_appconstate_t;

/**
 * @brief Application context information per connection.
 */
typedef struct {
    /* Connection state */
    bd_appconstate_t uState;

    /* Currently serving this file */
    int              uFile;
    struct stat      uFInfo;
} bd_appcon_t;

/**
 * @brief The server error function.
 * @param[in] pState    The state of the server.
 * @param[in/out] pCTxT The application context.
 * @return int32_t Server control value (see note).
 * @note
 *     BD_CONTINUE        - the server will continue normal flow
 *     BD_CLOSECONNECTION - the server will discard the current connection
 *     BD_SHUTDOWNSERVER  - the server will shutdown
 *     any other value will simply be ignored and the server will continue.
 */
static int32_t
sErrFunc (bd_svrstate_t pState, vptr_t pCTxT)
{
        char_t        msg[128];
        strptr_t      s;

    switch (pState) {

        case BD_START            : s = "BD_START"; break;
        case BD_MKSOCKET         : s = "BD_MKSOCKET"; break;
        case BD_CONVERTIPADD     : s = "BD_CONVERTIPADD"; break;
        case BD_BIND             : s = "BD_BIND"; break;
        case BD_LISTEN           : s = "BD_LISTEN"; break;
        case BD_FCTL             : s = "BD_FCTL"; break;
        case BD_SERVERSTATESENDS : s = "BD_SERVERSTATESENDS"; break;
        case BD_ACCEPT_NONBLOCK  : s = "BD_ACCEPT_NONBLOCK"; break;
        case BD_GOTCONNECTION    : s = "BD_GOTCONNECTION"; break;
        case BD_READHDR          : s = "BD_READHDR"; break;
        case BD_READLINE         : s = "BD_READLINE"; break;
        case BD_HANDLEREAD       : s = "BD_HANDLEREAD"; break;
        case BD_GOTLINE          : s = "BD_GOTLINE"; break;
        case BD_PARSEHDR_ERR     : s = "BD_PARSEHDR_ERR"; break;
        case BD_GOTHEADER        : s = "BD_GOTHEADER"; break;
        case BD_LOADRESPONSE     : s = "BD_LOADRESPONSE"; break;
        case BD_WRITINGDATA      : s = "BD_WRITINGDATA"; break;
        case BD_RESPONSEDONE     : s = "BD_RESPONSEDONE"; break;

        default: assert (0); s = NULL;
    }

    if (s) {
        sprintf (msg, "ERROR: [State %s]", s);
    } else {
        sprintf (msg, "ERROR: [State %d]: Please contact developer!", pState);
    }

    /* Display error */
    perror (msg);

    /* Just continue */
    return BD_CONTINUE;
}


#define BD_APPCONSTATE_HANDLER(h) \
static bd_appconstate_t s##h (bd_conn_t *pConn,\
                               bd_appcon_t *pCCTxT,\
                               vptr_t ign)

BD_APPCONSTATE_HANDLER (BD_CS_GOTCONNECTION)
{
    /* Since we have a new connection, we'll populate it with a context */
    pConn->uCTxT = xmalloc (sizeof (bd_appcon_t));
    memset (pConn->uCTxT, 0, sizeof (bd_appcon_t));
    /* Ready to rock & roll! */
    return AS_START;
}

BD_APPCONSTATE_HANDLER (BD_CS_RESPONSEDONE)
{
    /* Clean up response */
    if (pConn->uResponseData) free (pConn->uResponseData);
    pConn->uResponseData = NULL;

    /* Clean up currently serving file */
    if (pCCTxT->uFile && pCCTxT->uFile != -1) close (pCCTxT->uFile);
    pCCTxT->uFile = 0;

    /* Continue from beginning */
    return AS_START;
}

BD_APPCONSTATE_HANDLER (BD_CS_CONNECTIONCLOSED)
{
    /* Clean up response */
    (Void)sBD_CS_RESPONSEDONE (pConn, pCCTxT, ign);

    /* Clean up context itself */
    free (pConn->uCTxT);
    pConn->uCTxT = NULL;

    /* All done! */
    return AS_DONE;
}

BD_APPCONSTATE_HANDLER (BD_CS_GOTHEADER)
{
    /* Check the request */
    if (!strcmp (pConn->uRequest, "/")) {
        /* Handle the default request */
        pCCTxT->uFile = open ("index.htm", O_RDONLY);
    } else {
        pCCTxT->uFile = open (pConn->uRequest + 1, O_RDONLY);
    }
    /* Failed to get file to serve */
    if (pCCTxT->uFile == -1) return AS_ERR_GOTFILE;
    /* Set in async mode */
    if (fcntl (pCCTxT->uFile,
               F_SETFL, O_NONBLOCK) == -1) return AS_ERR_GOTFILEASYNC;
    /* Get file information */
    if (fstat (pCCTxT->uFile,
               &pCCTxT->uFInfo) == -1) return AS_ERR_GOTFILEINFO;
    /* Empty file! */
    if (!pCCTxT->uFInfo.st_size) return AS_ERR_GOTEMPTYFILE;
    /* Set up response data */
    pConn->uResponseData = xmalloc (pCCTxT->uFInfo.st_size);

    /* Success! */
    return AS_GOTFILE;
}

BD_APPCONSTATE_HANDLER (BD_CS_LOADRESPONSE)
{
        strptr_t e  = NULL;
        int      rd;

    switch (pCCTxT->uState) {
        case AS_DONE:
            /* Inform server */
            pConn->uResponseDataSize = -1;
            return pCCTxT->uState;
        case AS_GOTFILE:
            /* Reset response data */
            pConn->uResponseDataSize = 0;
            /* All done? */
            if (!pCCTxT->uFInfo.st_size) return AS_DONE;
            /* Read as much as possible without blocking */
            if ((rd = read (pCCTxT->uFile, pConn->uResponseData,
                            pCCTxT->uFInfo.st_size)) != -1) {
                /* Ok - decrease amount left to read */
                pCCTxT->uFInfo.st_size -= rd;
                /* Inform server how much we have read */
                pConn->uResponseDataSize = rd;
                /* Keep going */
                return pCCTxT->uState;
            }
            /* Didn't read anything because i/o not ready */
            if (errno == EAGAIN) return pCCTxT->uState;
            /* Error! */
            e = "Failed to read file!";
            /* Fall-through */
        case AS_ERR_GOTFILE:
            if (!e) e = "Failed to open file!";
            /* Fall-through */
        case AS_ERR_GOTFILEASYNC:
            if (!e) e = "Failed to set Async on file!";
            /* Fall-through */
        case AS_ERR_GOTFILEINFO:
            if (!e) e = "Failed to get file size!";
            /* Fall-through */
        case AS_ERR_GOTEMPTYFILE:
            if (!e) e = "File is empty!";
            pConn->uResponseData = xmalloc (strlen (e) + 1);
            pConn->uResponseDataSize = sprintf (pConn->uResponseData,
                    "%s", e);
            return AS_DONE;
        default:
            /* @warning This block has potential for buffer overrun
             *          but it should never reach here anyway! */
            pConn->uResponseData = xmalloc (128);
            pConn->uResponseDataSize = sprintf (pConn->uResponseData,
                    "Invalid state! Please contact developer! (%d)",
                    pCCTxT->uState);
            return AS_DONE;
    }
}

static int32_t
sConnectionFunc (bd_connstate_t pState,
                 bd_conn_t *pConn, vptr_t pCTxT)
{
        bd_appconstate_t ns;

#define CF_SM_HANDLE(st) case st: \
                            ns = s##st (pConn, pConn->uCTxT, NULL);\
                         break
    switch (pState) {
        CF_SM_HANDLE (BD_CS_GOTCONNECTION);
        CF_SM_HANDLE (BD_CS_CONNECTIONCLOSED);
        CF_SM_HANDLE (BD_CS_GOTHEADER);
        CF_SM_HANDLE (BD_CS_LOADRESPONSE);
        CF_SM_HANDLE (BD_CS_RESPONSEDONE);
        default:
            /* Should never reach here */
            assert (0);
    }

    /* Set new state (if not cleaned up) */
    if (pConn->uCTxT) ((bd_appcon_t *)pConn->uCTxT)->uState = ns;
    assert ((pConn->uCTxT) || (pState == BD_CS_CONNECTIONCLOSED));

    /* And continue */
    return BD_CONTINUE;

#undef CF_SM_HANDLE
}

/*****************************************************************************
 * MAIN ENTRY POINT
 ****************************************************************************/
int main (int argc, char* argv[])
{
    /* Run the HTTP server */
    return BD_RunServer (NULL, 8899,
                         sErrFunc,
                         NULL,
                         sConnectionFunc,
                         NULL);
}

/**
 * @brief Known bugs/issues.
 *
 * TODO: Support POST requests
 * TODO: Support HTTP 1.1
 */


