serverRun.c 8.79 KB
#include <sys/select.h> /* for select system call and related */
#include <string.h>     /* for memset and stuff */
#include <stdlib.h>     /* for exit */
#include <errno.h>      /* for errno */
#include <unistd.h>     /* for write */

#include <expat.h>

#include "../include/appConfig.h"
#include "../include/server.h"
#include "../include/client.h"
#include "../include/socket.h"
#include "../include/monitor.h"
#include "../include/logRotate.h"
#include "../include/signalHandling.h"
#include "../include/httpRequest.h"

#define RESPONSE  " 200 OK\r\nServer: xmlrpc\r\nStatus: 200\r\nContent-Length: 10\r\nContent-Type: text/plain\r\n\r\n0123456789"

int Depth;

void XMLCALL
start(void *data, const char *el, const char **attr) {
    int i;

    for (i = 0; i < Depth; i++)
        printf("  ");

    printf("%s", el);

    for (i = 0; attr[i]; i += 2) {
        printf(" %s='%s'", attr[i], attr[i + 1]);
    }

    printf("\n");
    Depth++;
}  /* End of start handler */

void XMLCALL
end(void *data, const char *el) {
    int i;

    Depth--;
    for (i = 0; i < Depth; i++)
        printf("  ");
    printf("--\n");
}  /* End of end handler */

void
serverRun(tServer * server)
{
    syslogMonitor(LOG_INFO, MON_INFO, "startup", "service started");

    while (!doShutdown) /* until error or signal  */
    {
        fd_set rfds;
        fd_set wfds;
        int    i;

        memcpy(&rfds, &(server->socks), sizeof(fd_set));

        FD_ZERO(&wfds);
        for (i=3; i<=server->maxFd; i++) {
            tClient * actClient = &(server->clients)[i];

            if (FD_ISSET(i, &(server->socks))
                    && NULL != actClient->writeBuffer
                    && 0    != strlen(actClient->writeBuffer)) {
                FD_SET(i, &wfds);
            }
        }
        
        /*
         * wait for handles to become ready
         */
        if (-1 == select((server->maxFd)+1, &rfds, &wfds, NULL, NULL))
        {
            switch (errno) {
                default:
                case EBADF:
                case EINVAL:
                case ENOMEM:
                     doShutdown = 1;
                     /* Fallthrough */

                case EINTR:
                     syslogMonitor(LOG_ERR, MON_INFO, "select",
                                   "select systemcall failed: [%s] - service terminated",
                                   strerror(errno));
                     continue; /* in while loop above */
            }
        }

        /*
         * handle accept
         */
        if (FD_ISSET(server->servSock, &rfds)) {
            int  fd;
            char remoteAddr[16] = "";

            if (-1 != (fd = acceptConnection(server->servSock, remoteAddr))) {
                (server->clients)[fd].socket = fd;   // save the socket handle within the client struct
                strncpy(
                        (server->clients)[fd].remoteAddr,
                        remoteAddr,
                        sizeof((server->clients)[fd].remoteAddr)-1);
                FD_SET(fd, &(server->socks));
                server->maxFd = MAX(fd, server->maxFd);

                (server->clients)[fd].parser = XML_ParserCreate("UTF-8");
                XML_SetElementHandler((server->clients)[fd].parser, &start, &end);

            }

            FD_CLR(server->servSock, &rfds);
        }

        /* handle reads (max 10 before next select, else we block accept for to long) */
        for (i=3; i<=server->maxFd; i++) {
//        for (i=3, count=0; i<=server->maxFd && count<10; i++, count++) {
            tClient * actClient = &(server->clients)[i];

            if (FD_ISSET(i, &rfds)) {
                switch (clientRead(actClient)) {
                    case -1: 
                        syslogMonitor(LOG_WARNING, MON_INFO, "socket.read",
                                "read on socket for %s returns -1: %s",
                                actClient->remoteAddr, strerror(errno));
                        /* FALLTHROUGH */

                    case 0:
                        clientClose(actClient);
                        FD_CLR(i, &(server->socks));
                        break;

                    default:
                        if (! httpHeaderIsComplete(&(actClient->httpHeader))) {
                            char         delim[] = "\r\n";
                            unsigned int len     = strlen(delim);
                            char *       line    = clientGetLine(actClient, delim, &len);

                            /* 
                             * len might be 0, thus indicatin an empty line
                             * this might happen in two cases
                             * 1. when a header is not already started
                             *    (could be identifies by a null req.method
                             * 2. if a header is started, then it indicates
                             *    the end of the header.
                             */

                            while (! httpHeaderIsStarted(&(actClient->httpHeader))) {

                                if (0 != len) {
                                    httpHeaderParseRequestLine(&(actClient->httpHeader), line, len);
                                    // TODO: do some error handling here
                                }

                                len = strlen(delim);
                                clientRemoveLine(actClient, delim, &len);  // TODO: do some error handling here
                                len = strlen(delim);
                                line = clientGetLine(actClient, delim, &len);

                            }

                            while (NULL != line) {
                                if (NULL == actClient->httpHeader.req.method) {
                                }
                            }
                        }

                        if (!httpHeaderIsComplete(&(actClient->httpHeader))) {
                            actClient->bodyLenRemaining = httpHeaderGet(
                                    &(actClient->readBuffer),
                                    &(actClient->readPos),
                                    &(actClient->httpHeader));
                        } 

                        if (httpHeaderIsComplete(&(actClient->httpHeader))) {
                            size_t          size   = MIN(actClient->bodyLenRemaining, actClient->readPos);
                            unsigned int    isLast = !(size - actClient->bodyLenRemaining);

                            enum XML_Status expatStat;

                            expatStat = XML_Parse(actClient->parser, actClient->readBuffer, size, isLast);

                            actClient->readPos          -= size;
                            actClient->bodyLenRemaining -= size;
                            memmove(actClient->readBuffer, actClient->readBuffer + size, actClient->readPos);
                            memset(actClient->readBuffer + actClient->readPos, 0, size);

                            if (isLast && NULL == actClient->writeBuffer) {

                                actClient->writeBuffer = calloc(
                                        strlen(actClient->httpHeader.req.httpVersion) +
                                        strlen(RESPONSE) + 3,
                                        sizeof(char));
                                sprintf(actClient->writeBuffer, "%s%s", 
                                        actClient->httpHeader.req.httpVersion,
                                        RESPONSE);

                                freeHttpHeader(&(actClient->httpHeader));
                                XML_ParserFree(actClient->parser);
                                actClient->parser = NULL;
                            }
                        }
                        break;
                }
            }
        }

        /* handle writes */
        for (i=3; i<=server->maxFd; i++) {
//        for (i=3, count=0; i<=server->maxFd && count<10; i++, count++) {
            tClient * actClient = &(server->clients)[i];

            // TODO: the && is only symptom fix...need to find real bug.
            if (FD_ISSET(i, &wfds) && NULL != actClient->writeBuffer) {
                int writeSize = 0;
                int toWrite = strlen(actClient->writeBuffer);

                writeSize = write(actClient->socket, actClient->writeBuffer, toWrite);

                if (0 < writeSize) {
                    if (writeSize == toWrite) {
                        free(actClient->writeBuffer);
                        actClient->writeBuffer = NULL;

                        clientClose(actClient);
                        FD_CLR(i, &(server->socks));
                    } else {
                        memmove(actClient->writeBuffer, actClient->writeBuffer + writeSize, toWrite - writeSize);
                        memset(actClient->writeBuffer + (toWrite - writeSize), 0, writeSize);
                    }
                }
            }
        }
    }
}