From 641853be5073b9b2dc5c60fa414271ccbec0c0aa Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 12 Jun 2007 06:48:11 +0000 Subject: cleaning up the code big time -- incomplete --- src/daemon/Makefile.am | 5 +- src/daemon/daemon.c | 1817 +++++++++++------------------------------------- src/daemon/internal.h | 111 +++ src/daemon/response.c | 333 +++++++++ src/daemon/response.h | 39 ++ src/daemon/session.c | 515 ++++++++++++++ src/daemon/session.h | 154 ++++ 7 files changed, 1552 insertions(+), 1422 deletions(-) create mode 100644 src/daemon/internal.h create mode 100644 src/daemon/response.c create mode 100644 src/daemon/response.h create mode 100644 src/daemon/session.c create mode 100644 src/daemon/session.h (limited to 'src/daemon') diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am index fb9b098e..892d0506 100644 --- a/src/daemon/Makefile.am +++ b/src/daemon/Makefile.am @@ -10,7 +10,10 @@ libmicrohttpd_la_LDFLAGS = \ # libmicrohttpd_la_LIBADD = \ # -lm libmicrohttpd_la_SOURCES = \ - daemon.c + daemon.c \ + internal.h \ + response.c response.h \ + session.c session.h # No curl, not testcases if HAVE_CURL diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c index dd22ce6d..8a602df4 100644 --- a/src/daemon/daemon.c +++ b/src/daemon/daemon.c @@ -20,544 +20,123 @@ /** * @file daemon.c - * @brief This is my implementation of the libmicrohttpd interface. Many features incomplete at this time. + * @brief A minimal-HTTP server library * @author Daniel Pittman + * @author Christian Grothoff * @version 0.1.0 */ -#include "config.h" -#include "microhttpd.h" - -#include #include #include #include #include #include +#include #include #include +#include +#include "microhttpd.h" +#include "internal.h" +#include "response.h" +#include "session.h" +#include "config.h" #define MHD_MAX_CONNECTIONS FD_SETSIZE -4 -#define MHD_MAX_BUF_SIZE 2048 -#define MHD_MAX_HEADERS 1024 -#define MHD_MAX_HANDLERS 1024 -#define MHD_MAX_RESPONSE 1024 - - -int MHD_handle_read(int, struct MHD_Daemon *); -int MHD_handle_write(int, struct MHD_Daemon *); -void * MHD_spawn_connections(void * data); -void * MHD_select(void * data); -int MHD_parse_message(struct MHD_Session * session); -void MHD_parse_URL(struct MHD_Session * session); - -struct MHD_Daemon { - unsigned int options; - - unsigned short port; - int socket_fd; - int max_fd; - - MHD_AcceptPolicyCallback apc; - void * apc_cls; - - fd_set read_fd_set; - fd_set write_fd_set; - fd_set except_fd_set; - - int shutdown; - pthread_t pid; - - struct MHD_Session * connections[MHD_MAX_CONNECTIONS]; - - int firstFreeHandler; - struct MHD_Access_Handler * handlers[MHD_MAX_HANDLERS]; - MHD_AccessHandlerCallback dh; - void * dh_cls; -}; - -struct MHD_Session { - struct sockaddr_in addr; - - int readLoc; - int writeLoc; - - int id; - int socket_fd; - pthread_t pid; - - struct MHD_Daemon * daemon; - - int bufPos; - int messagePos; - char inbuf[MHD_MAX_BUF_SIZE]; - - int firstFreeHeader; - char * requestType; - char * documentName; - struct MHD_HTTP_Header * headers[MHD_MAX_HEADERS]; - unsigned short responsePending; - int currentResponse; - struct MHD_Response * currentResponses[MHD_MAX_RESPONSE]; -}; - - -struct MHD_Response { - pthread_mutex_t mutex; - - unsigned int responseCode; - - int freeWhenFinished; - - unsigned int headersSent; - - size_t size; - void * data; - int bytesSentSoFar; - - int must_free; - MHD_ContentReaderCallback crc; - - void * crc_cls; - MHD_ContentReaderFreeCallback crfc; - - int firstFreeHeader; - struct MHD_HTTP_Header * headers[MHD_MAX_HEADERS]; - - struct MHD_Session * currentSession; -}; - -struct MHD_Access_Handler { - char * uri_prefix; - MHD_AccessHandlerCallback dh; - void * dh_cls; -}; +#define MHD_MAX_BUF_SIZE 2048 -struct MHD_HTTP_Header { - char * header; - char * headerContent; - enum MHD_ValueKind kind; -}; /** - * Add a header line to the response. - * - * @return MHD_NO on error (i.e. invalid header or content format). + * fprintf-like helper function for logging debug + * messages. */ -int -MHD_add_response_header(struct MHD_Response * response, - const char * header, - const char * content) { - //Note that as of this time this function will also return - //an error if the maximum number of headers allowed is exceeded. - - char * saveptr; - char * newHeader; - char * newContent; - int i; - - if(response == NULL || header == NULL || content == NULL || strlen(header) == 0 || strlen(content) == 0) { - return MHD_NO; - } - /* CG: use linked list to avoid limitation and over-allocation! */ - if(response->firstFreeHeader >= MHD_MAX_HEADERS) { - return MHD_NO; - } - - newHeader = (char *)malloc(strlen(header)+1); - newContent = (char *)malloc(strlen(content)+1); - /* CG: useless check! */ - if(newHeader == NULL || newContent == NULL) { - /* CG: printf! */ - fprintf(stderr, "Error allocating memory!\n"); - return MHD_NO; - } - - /* CG: do you mean strcpy/strdup? defer allocation - until you need to (after malformed checks!) */ - sprintf(newHeader, "%s", header); - sprintf(newContent, "%s", content); - - /* CG: why not use strstr? */ - if(strtok_r(newHeader, " \t\r\n", &saveptr) != NULL) { - fprintf(stderr, "Malformed header!\n"); - free(newContent); - free(newHeader); - return MHD_NO; - } - - /* CG: why not use strstr? */ - if(strtok_r(newContent, "\n", &saveptr) != NULL) { - fprintf(stderr, "Malformed content!\n"); - free(newContent); - free(newHeader); - return MHD_NO; - } - - /* CG: this is not C++ -- no need to cast after malloc! */ - struct MHD_HTTP_Header * newHTTPHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header)); - - if(newHTTPHeader == NULL) { - /* CG: useless check, printf */ - fprintf(stderr, "Error allocating memory!\n"); - free(newContent); - free(newHeader); - return MHD_NO; - } - - /* CG: strdup here, avoids free's above! */ - response->headers[response->firstFreeHeader]->header = newHeader; - response->headers[response->firstFreeHeader]->headerContent = newContent; - - //For now, everything is a HTTP Header... this needs to be improved! - /* CG: what else are you thinking about? Cookies? - sounds like you are proposing an API change!!! */ - - response->headers[response->firstFreeHeader]->kind = MHD_HEADER_KIND; - - /* CG: YUCK! Yet another reason for linked lists... - Why bother with the firstFreeHandler field if - you're O(n) anyway!? */ - response->firstFreeHeader=MHD_MAX_HEADERS; - for(i = 0; i < MHD_MAX_HEADERS; i++) { - if(response->headers[i] == NULL) { - response->firstFreeHeader = i; - break; - } - } - - return MHD_YES; +static void DLOG(const struct MHD_Daemon * daemon, + const char * format, + ...) { + va_list va; + + if ( (daemon->options & MHD_USE_DEBUG) == 0) + return; + va_start(va, format); + vfprintf(stderr, format, va); + va_end(va); } -/** - * This function accepts an incoming connection - * and creates the MHD_Session object for it. - * It also enforces policy by way of calling the accept policy callback - */ -int -MHD_create_connection(struct MHD_Daemon * daemon) { - int i, first_free, size; - - if(daemon == NULL) - return -1; - - first_free = -1; - for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { - if(daemon->connections[i] == NULL) { - first_free = i; - break; - } - } - - if(first_free == -1) - return -1; - - /* CG: delay allocation until at accept has succeeded! */ - daemon->connections[first_free] = (struct MHD_Session *)malloc(sizeof(struct MHD_Session)); - - if(daemon->connections[first_free] == NULL) { - /* CG: use MACRO or (static) helper function - instead of writing this option check everywhere! */ - if((daemon->options & MHD_USE_DEBUG) != 0) - fprintf(stderr, "Error allocating memory!\n"); - return -1; - } - - size = sizeof(struct sockaddr); - daemon->connections[first_free]->socket_fd = - - accept(daemon->socket_fd, (struct sockaddr *)&daemon->connections[first_free]->addr, - (socklen_t *)&size); - - if(daemon->connections[first_free]->socket_fd == -1) { - free(daemon->connections[first_free]); - daemon->connections[first_free] = NULL; - if((daemon->options & MHD_USE_DEBUG) != 0) { - fprintf(stderr, "Error accepting incoming connections!\n"); - } - return -1; - } - - if(daemon->apc != NULL && daemon->apc(daemon->apc_cls, (const struct sockaddr *)&daemon->connections[first_free]->addr, (socklen_t)sizeof(struct sockaddr_in))==MHD_NO) { - close(daemon->connections[first_free]->socket_fd); - free(daemon->connections[first_free]); - daemon->connections[first_free] = NULL; - if((daemon->options & MHD_USE_DEBUG) != 0) { - fprintf(stderr, "Connection denied based on accept policy callback!\n"); - } - return -1; - } - - daemon->connections[first_free]->id = first_free; - daemon->connections[first_free]->daemon = daemon; - daemon->connections[first_free]->pid = (pthread_t)-1; - daemon->connections[first_free]->bufPos = 0; - daemon->connections[first_free]->messagePos= 0; - daemon->connections[first_free]->responsePending = 0; - memset(daemon->connections[first_free]->inbuf, '\0', MHD_MAX_BUF_SIZE); - daemon->connections[first_free]->currentResponse = 0; - daemon->connections[first_free]->firstFreeHeader = 0; - - for(i = 0; i < MHD_MAX_HEADERS; i++) { - daemon->connections[first_free]->headers[i] = NULL; - } - - for(i = 0; i < MHD_MAX_RESPONSE; i++) { - daemon->connections[first_free]->currentResponses[i] = NULL; - } - /* CG: maybe better to re-compute max_fd closer to select; - also handles deletion mo re graceful, need to iterate over - all connections anyway for FD_SET... */ - if(daemon->max_fd < daemon->connections[first_free]->socket_fd) { - daemon->max_fd = daemon->connections[first_free]->socket_fd; - } - - return first_free; -} /** - * Create a response object. The response object can be extended with - * header information and then be used any number of times. + * Register an access handler for all URIs beginning with uri_prefix. * - * @param size size of the data portion of the response, -1 for unknown - * @param crc callback to use to obtain response data - * @param crc_cls extra argument to crc - * @param crfc callback to call to free crc_cls resources - * @return NULL on error (i.e. invalid arguments, out of memory) + * @param uri_prefix + * @return MRI_NO if a handler for this exact prefix + * already exists */ -struct MHD_Response * -MHD_create_response_from_callback(size_t size, - MHD_ContentReaderCallback crc, - void * crc_cls, - MHD_ContentReaderFreeCallback crfc) { - - struct MHD_Response * retVal; - int i; - - - if(crc == NULL) { - /* CG: printf! */ - fprintf(stderr, "A ContentReaderCallback must be provided to MHD_create_response_from_callback!\n"); - return NULL; - } - - retVal = (struct MHD_Response *) malloc(sizeof(struct MHD_Response)); - if(retVal == NULL) { - /* CG: printf, useless check, useless cast */ - fprintf(stderr, "Error allocating memory!\n"); - return NULL; - } - - retVal->size = size; - - retVal->crc = crc; - retVal->crc_cls = crc_cls; - - retVal->crfc = crfc; - - retVal->firstFreeHeader = 0; - - retVal->responseCode = 0; - - retVal->headersSent = 0; - - retVal->bytesSentSoFar = 0; - - retVal->freeWhenFinished = 0; - retVal->currentSession = NULL; - - if(pthread_mutex_init(&retVal->mutex, NULL) != 0) { - fprintf(stderr, "Error initializing mutex!\n"); - free(retVal); - return NULL; - } - /* CG: use memset? linked list!!? */ - for(i = 0; i < MHD_MAX_HEADERS; i++) { - retVal->headers[i] = NULL; - } - - retVal->data = NULL; - retVal->must_free = 0; - - return retVal; +int +MHD_register_handler(struct MHD_Daemon * daemon, + const char * uri_prefix, + MHD_AccessHandlerCallback dh, + void * dh_cls) { + struct MHD_Access_Handler * ah; + + if ( (daemon == NULL) || + (uri_prefix == NULL) || + (dh == NULL) ) + return MHD_NO; + ah = daemon->handlers; + while (ah != NULL) { + if (0 == strcmp(uri_prefix, + ah->uri_prefix)) + return MHD_NO; + ah = ah->next; + } + ah = malloc(sizeof(MHD_AccessHandlerCallback)); + ah->next = daemon->handlers; + ah->uri_prefix = strdup(uri_prefix); + ah->dh = dh; + ah->dh_cls = dh_cls; + daemon->handlers = ah; + return MHD_YES; } -/** - * Create a response object. The response object can be extended with - * header information and then be used any number of times. - * - * @param size size of the data portion of the response - * @param data the data itself - * @param must_free libmicrohttpd should free data when done - * @param must_copy libmicrohttpd must make a copy of data - * right away, the data maybe released anytime after - * this call returns - * @return NULL on error (i.e. invalid arguments, out of memory) - */ -struct MHD_Response * -MHD_create_response_from_data(size_t size, - void * data, - int must_free, - int must_copy) { - - struct MHD_Response * retVal; - int i; - - - if(data == NULL) { - fprintf(stderr, "data must be provided to MHD_Create_response_from_data!\n"); - return NULL; - } - - retVal = (struct MHD_Response *) malloc(sizeof(struct MHD_Response)); - if(retVal == NULL) { - fprintf(stderr, "Error allocating memory!\n"); - return NULL; - } - - retVal->size = size; - - retVal->crc = NULL; - retVal->crc_cls = NULL; - retVal->crfc = NULL; - - retVal->responseCode = 0; - - retVal->firstFreeHeader = 0; - retVal->freeWhenFinished = 0; - retVal->currentSession = NULL; - - retVal->headersSent = 0; - - retVal->bytesSentSoFar = 0; - - for(i = 0; i < MHD_MAX_HEADERS; i++) { - retVal->headers[i] = NULL; - } - - if(pthread_mutex_init(&retVal->mutex, NULL) != 0) { - fprintf(stderr, "Error initializing mutex!\n"); - free(retVal); - return NULL; - } - - if(must_copy) { - retVal->data = malloc(size); - if(retVal->data == NULL) { - fprintf(stderr, "Error allocating memory!\n"); - free(retVal); - return NULL; - } - memcpy(retVal->data, data, size); - retVal->must_free = 1; - } else { - retVal->data = data; - retVal->must_free = must_free; - } - - return retVal; -} /** - * Delete a header line from the response. + * Unregister an access handler for the URIs beginning with + * uri_prefix. * - * @return MHD_NO on error (no such header known) - */ -int -MHD_del_response_header(struct MHD_Response * response, - const char * header, - const char * content) { - int i; - - if(header == NULL || content == NULL) { - return MHD_NO; - } - - for(i = 0; i < MHD_MAX_HEADERS; i++) { - if(response->headers[i] != NULL && - strncmp(header, response->headers[i]->header, strlen(header)) == 0 && - strncmp(content, response->headers[i]->headerContent, strlen(header)) == 0) { - free(response->headers[i]->header); - free(response->headers[i]->headerContent); - free(response->headers[i]); - response->headers[i] = NULL; - return MHD_YES; - } - } - return MHD_NO; -} - -/** - * Destroy a response object and associated resources. Note that - * libmicrohttpd may keep some of the resources around if the response - * is still in the queue for some clients, so the memory may not - * necessarily be freed immediatley. - */ -void -MHD_destroy_response(struct MHD_Response * response) { - int i; - - if(response == NULL) { - return; - } - - pthread_mutex_lock(&response->mutex); - - if(response->currentSession != NULL) { - response->freeWhenFinished = 1; - pthread_mutex_unlock(&response->mutex); - return; - } - - if(response->must_free && response->data != NULL) { - free(response->data); - } - - if(response->crfc != NULL && response->crc_cls != NULL) { - response->crfc(response->crc_cls); - } - - for(i = 0; i < MHD_MAX_HEADERS; i++) { - if(response->headers[i] == NULL) - continue; - - free(response->headers[i]->header); - free(response->headers[i]->headerContent); - free(response->headers[i]); - } - - pthread_mutex_unlock(&response->mutex); - pthread_mutex_destroy(&response->mutex); - - free(response); -} - -/** - * Thi function is similar to destroy_response except - * that it was created to destroy the session object. + * @param uri_prefix + * @return MHD_NO if a handler for this exact prefix + * is not known for this daemon */ -void -MHD_destroy_session(struct MHD_Session * session) { - int i; - - for(i = 0; i < MHD_MAX_HEADERS; i++) { - if(session->headers[i] != NULL) { - free(session->headers[i]); - } - } - - for(i = 0; i < MHD_MAX_RESPONSE; i++) { - if(session->currentResponses[i] != NULL) { - pthread_mutex_lock(&session->currentResponses[i]->mutex); - session->currentResponses[i]->currentSession = NULL; - pthread_mutex_unlock(&session->currentResponses[i]->mutex); - } - } - - close(session->socket_fd); - free(session); +int +MHD_unregister_handler(struct MHD_Daemon * daemon, + const char * uri_prefix, + MHD_AccessHandlerCallback dh, + void * dh_cls) { + struct MHD_Access_Handler * prev; + struct MHD_Access_Handler * pos; + + if ( (daemon == NULL) || + (uri_prefix == NULL) || + (dh == NULL) ) + return MHD_NO; + pos = daemon->handlers; + prev = NULL; + while (pos != NULL) { + if ( (dh == ah->dh) && + (dh_cls == ah->dh_cls) && + (0 == strcmp(uri_prefix, + ah->uri_prefix)) ) { + if (prev == NULL) + daemon->handlers = pos->next; + else + prev->next = pos->next; + free(pos); + return MHD_YES; + } + prev = pos; + pos = pos->next; + } + return MHD_NO; } /** @@ -573,576 +152,233 @@ MHD_get_fdset(struct MHD_Daemon * daemon, fd_set * write_fd_set, fd_set * except_fd_set, int * max_fd) { - - int i; - - if(daemon == NULL || read_fd_set == NULL || write_fd_set == NULL || except_fd_set == NULL || max_fd == NULL) { - return MHD_NO; - } - - if((daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0) { - return MHD_NO; - } - - FD_ZERO(read_fd_set); - FD_ZERO(write_fd_set); - FD_ZERO(except_fd_set); - - FD_SET(daemon->socket_fd, &daemon->read_fd_set); - for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { - if(daemon->connections[i] != NULL) { - FD_SET(daemon->connections[i]->socket_fd, read_fd_set); - FD_SET(daemon->connections[i]->socket_fd, write_fd_set); - } - } - - *max_fd = daemon->max_fd; - - return MHD_YES; -} - -/** - * Get all of the headers added to a response. - * - * @param iterator callback to call on each header; - * maybe NULL (then just count headers) - * @param iterator_cls extra argument to iterator - * @return number of entries iterated over - */ -int -MHD_get_response_headers(struct MHD_Response * response, - MHD_KeyValueIterator * iterator, - void * iterator_cls) { - int i, numHeaders; - - if(response == NULL) { - return -1; - } - - numHeaders = 0; - for(i = 0; i < MHD_MAX_HEADERS; i++) { - if(response->headers[i] != NULL) { - if(iterator != NULL) { - (*iterator)(iterator_cls, response->headers[i]->kind, response->headers[i]->header, response->headers[i]->headerContent); - } - numHeaders++; - } - } - return numHeaders; + struct MHD_Session * pos; + + if ( (daemon == NULL) || + (read_fd_set == NULL) || + (write_fd_set == NULL) || + (except_fd_set == NULL) || + (max_fd == NULL) || + ( (daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0) ) + return MHD_NO; + FD_SET(daemon->socket_fd, + &daemon->read_fd_set); + if ( (*max_fd) < daemon->socket_fd) + *max_fd = daemon->socket_fd; + pos = daemon->session; + while (pos != NULL) { + if (MHD_YES != MHD_session_get_fdset(pos, + read_fd_set, + write_fd_set, + except_fd_set, + max_fd)) + return MHD_NO; + pos = pos->next; + } + return MHD_YES; } -/** - * Get all of the headers from the request. - * - * @param iterator callback to call on each header; - * maybe NULL (then just count headers) - * @param iterator_cls extra argument to iterator - * @return number of entries iterated over - */ -int -MHD_get_session_values(struct MHD_Session * session, - enum MHD_ValueKind kind, - MHD_KeyValueIterator * iterator, - void * iterator_cls) { - int i, numHeaders; - - if(session == NULL) { - return -1; - } - numHeaders = 0; - for(i = 0; i < MHD_MAX_HEADERS; i++) { - if(session->headers[i] != NULL && session->headers[i]->kind == kind) { - if(iterator != NULL) { - (*iterator)(iterator_cls, session->headers[i]->kind, session->headers[i]->header, session->headers[i]->headerContent); - } - numHeaders++; - } - } - return numHeaders; -} /** - * This function is intented to be called in the case of - * multithreaded connections. A thread will be spawned calling this - * function with a particular connection, and the thread will poll the connection - * (this should be improved) until there is something to do + * Main function of the thread that handles an individual + * connection. */ -void * +static void * MHD_handle_connection(void * data) { - struct MHD_Session * con; - int num_ready; - struct timeval timeout; - fd_set read; - fd_set write; - - con = data; - - if(con == NULL) - return NULL; - - do { - - FD_ZERO(&read); - FD_ZERO(&write); - - FD_SET(con->socket_fd, &read); - FD_SET(con->socket_fd, &write); - - timeout.tv_sec = 0; - timeout.tv_usec = 0; - - num_ready = select(con->socket_fd + 1, - &read, &write, NULL, &timeout); - - if(num_ready > 0) { - if(FD_ISSET(con->socket_fd, &read)) { - if(MHD_handle_read(con->id, con->daemon) == MHD_NO) { - pthread_detach(pthread_self()); - return NULL; - } - } - if (FD_ISSET(con->socket_fd, &write)) { - if(MHD_handle_write(con->id, con->daemon) == MHD_NO) { - pthread_detach(pthread_self()); - return NULL; - } - } - } - } while (!con->daemon->shutdown); - - return NULL; -} - -/** - * This function is created to handle the except file descriptor - * set, but it is doubtfull that it will ever be used. - */ -void -MHD_handle_except(int connection_id, struct MHD_Daemon * daemon) { - //It is unlikely that this function will ever need to be implemented. + struct MHD_Session * con = data; + int num_ready; + fd_set rs; + fd_set ws; + fd_set es; + int max; + + if (con == NULL) + abort(); + while (! con->daemon->shutdown) { + FD_ZERO(&rs); + FD_ZERO(&ws); + FD_ZERO(&es); + max = 0; + MHD_session_get_fdset(con, + &rs, + &ws, + &es, + &max); + num_ready = select(max + 1, + &rs, + &ws, + &es, + NULL); + if (num_ready <= 0) { + if (errno == EINTR) + continue; + break; + } + if ( ( (FD_ISSET(con->socket_fd, &rs)) && + (MHD_YES != MHD_session_handle_read(con)) ) || + ( (FD_ISSET(con->socket_fd, &ws)) && + (MHD_YES != MHD_session_handle_write(con)) ) ) + break; + } + close(con->socket_fd); + con->socket_fd = -1; + return NULL; } -/** - * This function handles a particular connection when it has been - * determined that there is data to be read off a socket. All implementations - * (multithreaded, external select, internal select) call this function - * to handle reads. - */ -int -MHD_handle_read(int connection_id, struct MHD_Daemon * daemon) { - int bytes_read,i; - - if((daemon->options & MHD_USE_DEBUG) != 0) { - fprintf(stderr, "Enter MHD_handle_read\n"); - } - - if(daemon == NULL || daemon->connections[connection_id]==NULL) { - return MHD_NO; - } - - if(daemon->connections[connection_id]->responsePending) { - return MHD_YES; - } - - daemon->connections[connection_id]->firstFreeHeader = 0; - daemon->connections[connection_id]->requestType = NULL; - - for(i = 0; i < MHD_MAX_HEADERS; i++) { - daemon->connections[connection_id]->headers[i] = NULL; - } - - - - memmove(daemon->connections[connection_id]->inbuf, daemon->connections[connection_id]->inbuf+daemon->connections[connection_id]->messagePos, daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos); - - memset(daemon->connections[connection_id]->inbuf + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos, - 0, MHD_MAX_BUF_SIZE - daemon->connections[connection_id]->bufPos + (daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos)); - - bytes_read = recv(daemon->connections[connection_id]->socket_fd, - daemon->connections[connection_id]->inbuf + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos, - MHD_MAX_BUF_SIZE - (daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos), 0); - - daemon->connections[connection_id]->bufPos = bytes_read + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos; - - if(bytes_read == 0) { - MHD_destroy_session(daemon->connections[connection_id]); - daemon->connections[connection_id] = NULL; - return MHD_NO; - } else { - fprintf(stderr, "\"%s\"\n", daemon->connections[connection_id]->inbuf); - i = MHD_parse_message(daemon->connections[connection_id]); - if(i == -1) { - daemon->connections[connection_id]->messagePos = daemon->connections[connection_id]->bufPos; - return MHD_YES; - } else { - daemon->connections[connection_id]->messagePos = i; - fprintf(stderr, "Number of bytes in header: %i\n", daemon->connections[connection_id]->messagePos); - } - - daemon->connections[connection_id]->responsePending = 1; - - MHD_parse_URL(daemon->connections[connection_id]); - - for(i = 0; i < MHD_MAX_HANDLERS; i++) { - if(daemon->handlers[i] == NULL) - continue; - - //header 0 will hold the url of the request - if(strstr(daemon->connections[connection_id]->headers[0]->headerContent, daemon->handlers[i]->uri_prefix) != NULL){ - return daemon->handlers[i]->dh(daemon->handlers[i]->dh_cls, daemon->connections[connection_id], - daemon->connections[connection_id]->documentName, daemon->connections[connection_id]->requestType, NULL, NULL); - } - } - return daemon->dh(daemon->dh_cls, daemon->connections[connection_id], - daemon->connections[connection_id]->documentName, daemon->connections[connection_id]->requestType, NULL, NULL); - } - - return MHD_YES; -} /** - * This function was created to handle writes to sockets when it has been - * determined that the socket can be written to. If there is no data - * to be written, however, the function call does nothing. All implementations - * (multithreaded, external select, internal select) call this function + * Accept an incoming connection and create the MHD_Session object for + * it. This function also enforces policy by way of checking with the + * accept policy callback. */ -int -MHD_handle_write(int connection_id, struct MHD_Daemon * daemon) { - struct MHD_Session * session; - - struct MHD_Response * response; - - int i; - - char * buffer[2048]; - - char * responseMessage; - int numBytesInMessage; - - if((daemon->options & MHD_USE_DEBUG) != 0) { - fprintf(stderr, "Enter MHD_handle_write\n"); - } - - - session = daemon->connections[connection_id]; - - response = session->currentResponses[session->currentResponse]; - - numBytesInMessage = 25; - - responseMessage = malloc(25); - if(responseMessage == NULL) { - if(daemon->options & MHD_USE_DEBUG) - fprintf(stderr, "Error allocating memory!\n"); - return MHD_NO; - } - - if(response == NULL) - return MHD_NO; - - pthread_mutex_lock(&response->mutex); - - if(!response->headersSent) { - sprintf(responseMessage, "HTTP/1.1 %i Go to hell!\r\n", response->responseCode); - fprintf(stderr, "%s\n", responseMessage); - if(send(session->socket_fd, responseMessage, strlen(responseMessage), 0) != strlen(responseMessage)) { - fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n"); - pthread_mutex_unlock(&response->mutex); - return MHD_NO; - } - - for(i = 0; i < MHD_MAX_HEADERS; i++) { - if(response->headers[i] == NULL) - continue; - - if(strlen(response->headers[i]->header) + strlen(response->headers[i]->headerContent) + 5 > numBytesInMessage) { - free(responseMessage); - responseMessage = malloc(strlen(response->headers[i]->header) + strlen(response->headers[i]->headerContent) + 5); - if(responseMessage == NULL) { - if(daemon->options & MHD_USE_DEBUG) - fprintf(stderr, "Error allocating memory!\n"); - pthread_mutex_unlock(&response->mutex); - return MHD_NO; - } - numBytesInMessage = strlen(response->headers[i]->header) + strlen(response->headers[i]->headerContent) + 5; - } - sprintf(responseMessage, "%s: %s\r\n", response->headers[i]->header, response->headers[i]->headerContent); - fprintf(stderr, "%s\n", responseMessage); - if(send(session->socket_fd, responseMessage, strlen(responseMessage), 0) != strlen(responseMessage)) { - fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n"); - pthread_mutex_unlock(&response->mutex); - return MHD_NO; - } - } - - response->headersSent = 1; - } - - if(response->data != NULL) { - if(response->bytesSentSoFar == 0) { - if(numBytesInMessage < 32) { - free(responseMessage); - responseMessage = malloc(32); - if(responseMessage == NULL) { - if(daemon->options & MHD_USE_DEBUG) - fprintf(stderr, "Error allocating memory!\n"); - pthread_mutex_unlock(&response->mutex); - return MHD_NO; - } - } - sprintf(responseMessage, "Content-length: %llu\r\n\r\n", (unsigned long long)response->size); - fprintf(stderr, "%s\n", responseMessage); - if(send(session->socket_fd, responseMessage, strlen(responseMessage),0)!= strlen(responseMessage)) { - fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n"); - pthread_mutex_unlock(&response->mutex); - return MHD_NO; - } - } - - i = send(session->socket_fd, response->data+response->bytesSentSoFar, response->size-response->bytesSentSoFar,0); - response->bytesSentSoFar += i; - - fprintf(stderr, "Sent %i bytes of data\nTotal to send is %llu bytes\n", i, (unsigned long long)response->size); - - if(response->bytesSentSoFar == response->size) { - session->currentResponses[session->currentResponse] = NULL; - session->currentResponse = (session->currentResponse + 1) % MHD_MAX_RESPONSE; - response->currentSession = NULL; - - if(response->freeWhenFinished) { - pthread_mutex_unlock(&response->mutex); - MHD_destroy_response(response); - } - /*THIS NEEDS TO BE HANDLED ANOTHER WAY!!! TIMEOUT, ect..., as of now this is the only way to get test case to work - * since client never disconnects on their own! - */ - if(session->currentResponses[session->currentResponse] == NULL) { - MHD_destroy_session(session); - daemon->connections[connection_id] = NULL; - return MHD_NO; - } - } - } else { - if(response->crc == NULL) { - pthread_mutex_unlock(&response->mutex); - return MHD_NO; - } - - if(response->bytesSentSoFar == 0) { - if(send(session->socket_fd, "\r\n", response->size,0) != 2) { - fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n"); - pthread_mutex_unlock(&response->mutex); - return MHD_NO; - } - } - memset(buffer, 0, 2048); - - i = response->crc(response->crc_cls, response->bytesSentSoFar, (char *)buffer, 2048); - - if(i == -1) { - pthread_mutex_unlock(&response->mutex); - - session->currentResponses[session->currentResponse] = NULL; - session->currentResponse = (session->currentResponse + 1) % MHD_MAX_RESPONSE; - response->currentSession = NULL; - - if(response->freeWhenFinished) { - pthread_mutex_unlock(&response->mutex); - MHD_destroy_response(response); - } - /*THIS NEEDS TO BE HANDLED ANOTHER WAY!!! TIMEOUT, ect..., as of now this is the only way to get test case to work - * since client never disconnects on their own! - */ - if(session->currentResponses[session->currentResponse] == NULL) { - MHD_destroy_session(session); - daemon->connections[connection_id] = NULL; - return MHD_NO; - } - - } else { - i = send(session->socket_fd, buffer, i,0); - response->bytesSentSoFar += i; - } - } - pthread_mutex_unlock(&response->mutex); - return MHD_YES; -} - - -/** - * Get a particular header value. If multiple - * values match the kind, return any one of them. - * - * @param key the header to look for - * @return NULL if no such item was found - */ -const char * -MHD_lookup_session_value(struct MHD_Session * session, - enum MHD_ValueKind kind, - const char * key) { - int i; - - for(i = 0; i < MHD_MAX_HEADERS; i++) { - if(session->headers[i] != NULL && - session->headers[i]->kind == kind && - strncmp(session->headers[i]->header, key, strlen(session->headers[i]->header)) == 0) { - return (const char *)session->headers[i]->headerContent; - } - } - - return NULL; +static int +MHD_accept_connection(struct MHD_Daemon * daemon) { + struct MHD_Session * session; + struct sockaddr addr; + socklen_t addrlen; + int s; + + addrlen = sizeof(struct sockaddr); + memset(&addr, + 0, + sizeof(struct sockaddr)); + if ( (0 != (s = accept(daemon->socket_fd, + &addr, + &addrlen))) || + (addrlen <= 0) ) { + DLOG(daemon, + "Error accepting connection: %s\n", + strerror(errno)); + return MHD_NO; + } + if (MHD_NO == daemon->apc(mhd->apc_cls, + &addr, + addrlen)) { + close(s); + return MHD_YES; + } + session = malloc(sizeof(struct MHD_Session)); + memset(session, + 0, + sizeof(struct MHD_Session)); + session->addr = malloc(addrlen); + memcpy(session->addr, + &addr, + addrlen); + session->addr_len = addrlen; + session->socket_fd = s; + session->daemon = daemon; + if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION) ) && + (0 != pthread_create(&session->pid, + NULL, + &MHD_handle_connection, + session)) ) { + DLOG(daemon, + "Failed to create a thread: %s\n", + strerror(errno)); + free(session->addr); + close(s); + free(session); + return MHD_NO; + } + session->next = daemon->connections; + daemon->connections = session; + return MHD_YES; } /** - * This function is designed to parse the input buffer of a given session. - * It is assumed that the data being parsed originates at buffer location - * 0 (a valid assumption since the buffer is shifted after each message) + * Free resources associated with all closed sessions. + * (destroy responses, free buffers, etc.). A session + * is known to be closed if the socket_fd is -1. */ -int -MHD_parse_message(struct MHD_Session * session) { - const char * crlfcrlf = "\r\n\r\n"; - const char * crlf = "\r\n"; - - char * saveptr; - char * saveptr1; - - struct MHD_HTTP_Header * newHeader; - char * curTok; - char * curTok1; - - int numBytes; - - curTok = strstr(session->inbuf, crlfcrlf); - - if(curTok == NULL) { - return -1; - } - - memset(curTok+2, 0, 2); - - numBytes = strlen(session->inbuf) + 2; - - curTok = strtok_r(session->inbuf, crlf, &saveptr); - - session->requestType = strtok_r(curTok, " ", &saveptr1); - - newHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header)); - if(newHeader == NULL) { - if(session->daemon->options & MHD_USE_DEBUG) - fprintf(stderr, "Error allocating memory!\n"); - return -1; - } - newHeader->kind = MHD_GET_ARGUMENT_KIND; - newHeader->header = session->requestType; - newHeader->headerContent = strtok_r(NULL, " ", &saveptr1); - - session->headers[session->firstFreeHeader++] = newHeader; - - curTok = strtok_r(NULL, crlf, &saveptr); - while(curTok != NULL && session->firstFreeHeader < MHD_MAX_HEADERS) { - curTok1 = strtok_r(curTok, ":", &saveptr1); - newHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header)); - if(newHeader == NULL) { - if(session->daemon->options & MHD_USE_DEBUG) - fprintf(stderr, "Error allocating memory!\n"); - return -1; - } - newHeader->header = curTok1; - newHeader->headerContent = curTok + strlen(curTok1) + 2; - //For now, everything is a get! - newHeader->kind = MHD_GET_ARGUMENT_KIND; - session->headers[session->firstFreeHeader++] = newHeader; - curTok = strtok_r(NULL, crlf, &saveptr); - } - - return numBytes; +static void +MHD_cleanup_sessions(struct MHD_Daemon * daemon) { + struct MHD_Session * pos; + struct MHD_Session * prev; + + pos = daemon->connections; + prev = NULL; + while (pos != NULL) { + if (pos->socket_fd == -1) { + if (prev == NULL) + daemon->connections = pos->next; + else + prev->next = pos->next; + free(pos->addr); + /* FIXME: more to free here! */ + free(pos); + } + prev = pos; + pos = pos->next; + } } -/** - * This function needs to do a lot more (i.e. break up get arguments) - * but for now just seperates the prefix of the url from the document - * portion. - */ -void -MHD_parse_URL(struct MHD_Session * session) { - char * working; - int pos,i; - - working = session->headers[0]->headerContent; - - pos = 0; - for(i = 0; i < strlen(working); i++) { - if(working[i] == '/') - pos = i+1; - } - if(pos >= strlen(working)) - pos = 0; - - session->documentName = session->headers[0]->headerContent+pos; -} /** - * Queue a response to be transmitted to the client (as soon as - * possible). - * - * @param session the session identifying the client - * @param status_code HTTP status code (i.e. 200 for OK) - * @param response response to transmit - * @return MHD_NO on error (i.e. reply already sent), - * MHD_YES on success or if message has been queued + * Main select call. + * + * @return MHD_NO on serious errors, MHD_YES on success */ -int -MHD_queue_response(struct MHD_Session * session, - unsigned int status_code, - struct MHD_Response * response) { - - //As of now this function can only support a fixed amount of queued responses, and will - //return MHD_NO if that queue is full - - int index; - - if(session == NULL || response == NULL) { - return MHD_NO; - } - - pthread_mutex_lock(&response->mutex); - - if(response->currentSession != NULL) { - return MHD_NO; - } - - if(session->currentResponses[session->currentResponse] == NULL) { - index = session->currentResponse; - } else if(session->currentResponses[session->currentResponse + 1 % MHD_MAX_RESPONSE] == NULL) { - index = session->currentResponse + 1 % MHD_MAX_RESPONSE; - } else { - pthread_mutex_unlock(&response->mutex); - return MHD_NO; - } - - response->responseCode = status_code; - session->currentResponses[index] = response; - response->currentSession = session; - session->responsePending = 0; - - pthread_mutex_unlock(&response->mutex); - - return MHD_YES; +static int +MHD_select(struct MHD_Daemon * daemon) { + struct MHD_Session * pos; + int num_ready; + fd_set rs; + fd_set ws; + fd_set es; + int max; + + if(daemon == NULL) + abort(); + FD_ZERO(&rs); + FD_ZERO(&ws); + FD_ZERO(&es); + max = 0; + + if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) { + /* single-threaded, go over everything */ + if (MHD_NO == MHD_get_fdset(daemon, + &rs, + &ws, + &es, + &max)) + return MHD_NO; + } else { + /* accept only, have one thread per connection */ + max = daemon->socket_fd; + FD_SET(daemon->socket_fd, &rs); + } + num_ready = select(max + 1, + &rs, + &ws, + &es, + NULL); + if (num_ready < 0) { + if (errno == EINTR) + return MHD_YES; + DLOG(daemon, + "Select failed: %s\n", + strerror(errno)); + return MHD_NO; + } + if (FD_ISSET(daemon->socket_fd, + &rs)) + MHD_accept_connection(daemon); + if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) { + /* do not have a thread per connection, process all connections now */ + pos = daemon->connections; + while (pos != NULL) { + if (FD_ISSET(pos->socket_fd, &rs)) + MHD_session_handle_read(pos); + if (FD_ISSET(pos->socket_fd, &ws)) + MHD_session_handle_write(pos); + pos = pos->next; + } + } + return MHD_YES; } -/** - * @return -1 if no data uploaded; otherwise number of bytes - * read into buf; 0 for end of transmission - * Specification not complete at this time. - */ -int -MHD_read_file_upload(struct MHD_Session * session, - void * buf, - size_t len) { - //This function will not be implemented until the specification is completed. - return -1; -} /** * Run webserver operations (without blocking unless @@ -1156,190 +392,34 @@ MHD_read_file_upload(struct MHD_Session * session, */ int MHD_run(struct MHD_Daemon * daemon) { - - if((daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0) { - daemon->shutdown = 0; - if(pthread_create(&daemon->pid, NULL, (void *) &MHD_spawn_connections, (void *)daemon) == 0) { - return MHD_YES; - } else { - return MHD_NO; - } - } else if((daemon->options & MHD_USE_SELECT_INTERNALLY) != 0) { - daemon->shutdown = 0; - if(pthread_create(&daemon->pid, NULL, (void *) &MHD_select, (void *)daemon) == 0) { - return MHD_YES; - } else { - return MHD_NO; - } - } else { - daemon->shutdown = 1; - return (MHD_select((void *)daemon) == NULL); - } + if ( (daemon->shutdown != 0) || + (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) || + (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) ) + return MHD_NO; + MHD_select(daemon); + MHD_cleanup_sessions(daemon); + return MHD_YES; } /** - * Register an access handler for all URIs beginning with uri_prefix. - * - * @param uri_prefix - * @return MRI_NO if a handler for this exact prefix - * already exists + * Thread that runs the select loop until the daemon + * is explicitly shut down. */ -int -MHD_register_handler(struct MHD_Daemon * daemon, - const char * uri_prefix, - MHD_AccessHandlerCallback dh, - void * dh_cls) { - int i; - - //This function will also return MHD_NO if the maximum number of supported handlers is exceeded - - if(daemon == NULL || uri_prefix == NULL || dh == NULL) { - return MHD_NO; - } - - if(daemon->firstFreeHandler >= MHD_MAX_HANDLERS) { - return MHD_NO; - } - - daemon->handlers[daemon->firstFreeHandler] = malloc(sizeof(struct MHD_Access_Handler)); - - if(daemon->handlers[daemon->firstFreeHandler] == NULL) { - if((daemon->options & MHD_USE_DEBUG) != 0) - fprintf(stderr, "Error allocating memory!\n"); - return MHD_NO; - } - - daemon->handlers[daemon->firstFreeHandler]->uri_prefix = malloc(strlen(uri_prefix)+1); - if(daemon->handlers[daemon->firstFreeHandler]->uri_prefix == NULL) { - if((daemon->options & MHD_USE_DEBUG) != 0) { - free(daemon->handlers[daemon->firstFreeHandler]); - fprintf(stderr, "Error allocating memory!\n"); - } - return MHD_NO; - } - sprintf(daemon->handlers[daemon->firstFreeHandler]->uri_prefix, "%s", uri_prefix); - - daemon->handlers[daemon->firstFreeHandler]->dh = dh; - daemon->handlers[daemon->firstFreeHandler]->dh_cls = dh_cls; - - daemon->firstFreeHandler = MHD_MAX_HANDLERS; - for(i = 0; i < MHD_MAX_HANDLERS; i++) { - if(daemon->handlers[i] == NULL) { - daemon->firstFreeHandler = i; - break; - } - } - - return MHD_YES; - +static void * +MHD_select_thread(void * cls) { + struct MHD_Daemon * daemon = cls; + while (daemon->shutdown == 0) { + MHD_select(daemon); + MHD_cleanup_sessions(daemon); + } + return NULL; } -/** - * This function is the entry point for either internal or external select. - * The only differences between the two forms of running is whether the call is - * made from a new thread or the main thread, and whether the initial value - * of shutdown is 0 or 1 (1 for loop, 0 for one time pass) - */ -void * -MHD_select(void * data) { - struct MHD_Daemon * daemon; - int i, num_ready; - struct timeval timeout; - - daemon = data; - if(daemon == NULL) { - return NULL; - } - do { - FD_ZERO(&daemon->read_fd_set); - FD_ZERO(&daemon->write_fd_set); - FD_ZERO(&daemon->except_fd_set); - - FD_SET(daemon->socket_fd, &daemon->read_fd_set); - for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { - if(daemon->connections[i] != NULL) { - FD_SET(daemon->connections[i]->socket_fd, &daemon->read_fd_set); - FD_SET(daemon->connections[i]->socket_fd, &daemon->write_fd_set); - } - } - - timeout.tv_sec = 0; - timeout.tv_usec = 0; - - num_ready = select(daemon->max_fd + 1, - &(daemon->read_fd_set), &(daemon->write_fd_set), &(daemon->except_fd_set), &timeout); - - if(num_ready > 0) { - for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { - if(daemon->connections[i] != NULL) { - if(FD_ISSET(daemon->connections[i]->socket_fd, &(daemon->read_fd_set))) { - if(MHD_handle_read(i, daemon) == MHD_NO) - continue; - } - if (FD_ISSET(daemon->connections[i]->socket_fd, &(daemon->write_fd_set))) { - if(MHD_handle_write(i, daemon) == MHD_NO) - continue; - } - if (FD_ISSET(daemon->connections[i]->socket_fd, &(daemon->except_fd_set))) { - MHD_handle_except(i, daemon); - } - } - } - if(FD_ISSET(daemon->socket_fd, &(daemon->read_fd_set))) { - if(MHD_create_connection(daemon) == -1) { - continue; - } - } - } - } while (!daemon->shutdown); - - return NULL; -} - -/** - * This function was created for the case of multithreaded connections. - * A thread will spawned to sit on this function, and in turn spawns more - * threads, one per connection. - */ -void * -MHD_spawn_connections(void * data) { - struct MHD_Daemon * daemon; - int con, num_ready; - struct timeval timeout; - fd_set read; - - daemon = data; - if(daemon == NULL) { - return NULL; - } - - do { - FD_ZERO(&read); - FD_SET(daemon->socket_fd, &read); - - timeout.tv_sec = 0; - timeout.tv_usec = 0; - - num_ready = select(daemon->socket_fd + 1,&read, NULL, NULL, &timeout); - - if(num_ready > 0) { - con = MHD_create_connection(daemon); - if(con == -1) - continue; - - if(pthread_create(&daemon->connections[con]->pid, NULL, (void *) &MHD_handle_connection, (void *)daemon->connections[con]) != 0) { - if((daemon->options & MHD_USE_DEBUG) != 0) - fprintf(stderr, "Error creating connection handler!.\n"); - } - } - } while (!daemon->shutdown); - - return NULL; -} /** * Start a webserver on the given port. + * * @param port port to bind to * @param apc callback to call to check which clients * will be allowed to connect @@ -1355,128 +435,73 @@ MHD_start_daemon(unsigned int options, void * apc_cls, MHD_AccessHandlerCallback dh, void * dh_cls) { - - - struct MHD_Daemon * retVal = NULL; - int socket_fd, opt, res, i; - struct sockaddr_in servaddr; - struct hostent *hostptr; - char hostid[32]; - - if((options & MHD_USE_SSL) != 0) { - if((options & MHD_USE_DEBUG) != 0) - fprintf(stderr, "SSL at this time is unsupported.\n"); - return NULL; - } - if((options & MHD_USE_IPv6) != 0) { - if((options & MHD_USE_DEBUG) != 0) - fprintf(stderr, "IP version 6 is not supported at this time.\n"); - return NULL; - } - - if((options & MHD_USE_IPv4) != 0) { - if((options & MHD_USE_DEBUG) != 0) - fprintf(stderr, "Enter MHD_start_daemon. Starting Daemon on port %i\n", port); - - if(port < 1) { - if((options & MHD_USE_DEBUG) != 0) - fprintf(stderr, "Invalid port: %i!\n", port); - return NULL; - } - - if(dh == NULL) { - if((options & MHD_USE_DEBUG) != 0) - fprintf(stderr, "A default access handler must be provided\n"); - return NULL; - } - - retVal = (struct MHD_Daemon *)malloc(sizeof(struct MHD_Daemon)); - if(retVal == NULL) { - if((options & MHD_USE_DEBUG) != 0) - fprintf(stderr, "Error allocating memory!\n"); - return NULL; - } - - retVal->options = options; - retVal->port = port; - retVal->apc = apc; - retVal->apc_cls = apc_cls; - retVal->dh = dh; - retVal->dh_cls = dh_cls; - retVal->shutdown = 0; - retVal->pid = (pthread_t)-1; - - retVal->firstFreeHandler = 0; - for(i = 0; i < MHD_MAX_HANDLERS; i++) { - retVal->handlers[i] = NULL; - } - - FD_ZERO(&retVal->read_fd_set); - FD_ZERO(&retVal->write_fd_set); - FD_ZERO(&retVal->except_fd_set); - - for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { - retVal->connections[i] = NULL; - } - - socket_fd = socket(AF_INET, SOCK_STREAM, 0); - if(socket_fd < 0) { - if((options & MHD_USE_DEBUG) != 0) - perror("Error creating socket!"); - return NULL; - } - - memset((void *)&servaddr, 0, (size_t)sizeof(servaddr)); - - if (gethostname(hostid,32) < 0){ - if((options & MHD_USE_DEBUG) != 0) - perror("server_tcp:gethostname"); - return NULL; - } - - if ((hostptr = gethostbyname(hostid)) == NULL){ - if((options & MHD_USE_DEBUG) != 0) - fprintf(stderr, "invalid host name, %s\n",hostid); - return NULL; - } - - servaddr.sin_family = AF_INET; - memcpy((void *)&(servaddr.sin_addr), (void *)(hostptr->h_addr), hostptr->h_length); - servaddr.sin_port = htons(port); - - - if (bind(socket_fd, (struct sockaddr *)&servaddr, (socklen_t)sizeof(servaddr)) < 0) { - if((options & MHD_USE_DEBUG) != 0) - perror("server:bind"); - return NULL; - } - - if(listen(socket_fd, 20) < 0) { - if((options & MHD_USE_DEBUG) != 0) - perror("server:bind"); - return NULL; - } - - retVal->socket_fd = socket_fd; - retVal->max_fd = socket_fd; - FD_SET(socket_fd, &retVal->read_fd_set); - - opt = fcntl(socket_fd, F_GETFL, 0); - res = fcntl(socket_fd, F_SETFL, opt | O_NONBLOCK); - if(res < 0) { - if((options & MHD_USE_DEBUG) != 0) - perror("Error disabling block on socket!"); - return NULL; - } - - return retVal; - } - - if((options & MHD_USE_DEBUG) != 0) - fprintf(stderr, "No options given to start_daemon!\n"); - - return NULL; - + struct MHD_Daemon * retVal; + int socket_fd; + int opt; + int res; + struct sockaddr_in servaddr; + + if ((options & MHD_USE_SSL) != 0) + return NULL; + if ((options & MHD_USE_IPv6) != 0) + return NULL; + if ((options & MHD_USE_IPv4) == 0) + return NULL; + if ( (port == 0) || + (dh == NULL) ) + return NULL; + socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (socket_fd < 0) { + /* FIXME: log error */ + return NULL; + } + memset(&servaddr, + 0, + sizeof(struct sockaddr_in)); + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(port); + if (bind(socket_fd, + (struct sockaddr *)&servaddr, + sizeof(struct sockaddr_in)) < 0) { + /* FIXME: log error */ + close(socket_fd); + return NULL; + } + if (listen(socket_fd, 20) < 0) { + /* FIXME: log error */ + return NULL; + } + opt = fcntl(socket_fd, F_GETFL, 0); + res = fcntl(socket_fd, F_SETFL, opt | O_NONBLOCK); + if (res < 0) { + /* FIXME: log error */ + close(socket_fd); + return NULL; + } + + retVal = malloc(sizeof(struct MHD_Daemon)); + memset(retVal, + 0, + sizeof(struct MHD_Daemon)); + retVal->options = options; + retVal->port = port; + retVal->apc = apc; + retVal->apc_cls = apc_cls; + retVal->dh = dh; + retVal->dh_cls = dh_cls; + retVal->socket_fd = socket_fd; + if ( ( (0 != (options & MHD_USE_THREAD_PER_CONNECTION)) || + (0 != (options & MHD_USE_SELECT_INTERNALLY)) ) && + (0 != pthread_create(&daemon->pid, + NULL, + &MHD_select_thread, + daemon)) ) { + /* FIXME: log error */ + free(retVal); + close(socket_fd); + return NULL; + } + return retVal; } /** @@ -1484,76 +509,26 @@ MHD_start_daemon(unsigned int options, */ void MHD_stop_daemon(struct MHD_Daemon * daemon) { - int i, j; - - if(daemon == NULL) { - return; - } - - if((daemon->options & MHD_USE_DEBUG) != 0) - fprintf(stderr, "Enter MHD_stop_daemon. Stopping daemon on port %i\n", daemon->port); - - daemon->shutdown = 1; - - if(daemon->pid != (pthread_t)-1) { - pthread_join(daemon->pid, NULL); - } - - for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { - if(daemon->connections[i] != NULL) { - if(daemon->connections[i]->pid != (pthread_t)-1) { - pthread_join(daemon->connections[i]->pid, NULL); - } - - for(j = 0; j < MHD_MAX_RESPONSE; j++) { - if(daemon->connections[i]->currentResponses[j] != NULL) { - MHD_destroy_response(daemon->connections[i]->currentResponses[j]); - } - } - MHD_destroy_session(daemon->connections[i]); - } - } - - for(i = 0; i < MHD_MAX_HANDLERS; i++) { - if(daemon->handlers[i] != NULL) { - free(daemon->handlers[i]->uri_prefix); - free(daemon->handlers[i]); - } - } - - close(daemon->socket_fd); - - free(daemon); -} - - -/** - * Unregister an access handler for the URIs beginning with - * uri_prefix. - * - * @param uri_prefix - * @return MHD_NO if a handler for this exact prefix - * is not known for this daemon - */ -int -MHD_unregister_handler(struct MHD_Daemon * daemon, - const char * uri_prefix, - MHD_AccessHandlerCallback dh, - void * dh_cls) { - int i; - - for(i = 0; i < MHD_MAX_HANDLERS; i++) { - if(daemon->handlers[i] != NULL) { - if(strncmp(daemon->handlers[i]->uri_prefix, uri_prefix, strlen(daemon->handlers[i]->uri_prefix)) == 0) { - if(daemon->handlers[i]->dh == dh && daemon->handlers[i]->dh_cls == dh_cls) { - free(daemon->handlers[i]->uri_prefix); - free(daemon->handlers[i]); - return MHD_YES; - } - } - } - } - - return MHD_NO; + void * unused; + + if (daemon == NULL) + return; + daemon->shutdown = 1; + close(daemon->socket_fd); + daemon->socket_fd = -1; + if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) || + (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) ) + pthread_join(daemon->pid, &unused); + + while (daemon->connections != NULL) { + if (-1 != daemon->connections->socket_fd) { + close(daemon->connections->socket_fd); + daemon->connections->socket_fd = -1; + } + pthread_join(daemon->connections->pid, &unused); + + MHD_cleanup_sessions(daemon); + } + free(daemon); } diff --git a/src/daemon/internal.h b/src/daemon/internal.h new file mode 100644 index 00000000..e09acf3b --- /dev/null +++ b/src/daemon/internal.h @@ -0,0 +1,111 @@ +/* + This file is part of libmicrohttpd + (C) 2007 Daniel Pittman + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + libmicrohttpd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file internal.h + * @brief internal shared structures + * @author Daniel Pittman + * @author Christian Grothoff + * @version 0.1.0 + */ + +#ifndef INTERNAL_H +#define INTERNAL_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "microhttpd.h" +#include "config.h" + + +/** + * Header or cookie in HTTP request or response. + */ +struct MHD_HTTP_Header { + struct MHD_HTTP_Header * next; + + char * header; + + char * value; + + enum MHD_ValueKind kind; +}; + + +struct MHD_Access_Handler { + struct MHD_Access_Handler * next; + + char * uri_prefix; + + MHD_AccessHandlerCallback dh; + + void * dh_cls; +}; + + +struct MHD_Daemon { + + struct MHD_Access_Handler * handlers; + + MHD_AccessHandlerCallback default_handler; + + struct MHD_Session * connections; + + void * dh_cls; + + MHD_AcceptPolicyCallback apc; + + void * apc_cls; + + /** + * PID of the select thread (if we have internal select) + */ + pthread_t pid; + + /** + * Listen socket. + */ + int socket_fd; + + /** + * Are we shutting down? + */ + int shutdown; + + /** + * Daemon's options. + */ + enum MHD_OPTION options; + + unsigned short port; + +}; + + +#endif diff --git a/src/daemon/response.c b/src/daemon/response.c new file mode 100644 index 00000000..a26b003b --- /dev/null +++ b/src/daemon/response.c @@ -0,0 +1,333 @@ +/* + This file is part of libmicrohttpd + (C) 2007 Daniel Pittman + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + libmicrohttpd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file response.c + * @brief Methods for managing response objects + * @author Daniel Pittman + * @author Christian Grothoff + * @version 0.1.0 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "microhttpd.h" +#include "response.h" +#include "internal.h" +#include "config.h" + + +/** + * Representation of a response. + */ +struct MHD_Response { + + /** + * Headers to send for the response. Initially + * the linked list is created in inverse order; + * the order should be inverted before sending! + */ + struct MHD_HTTP_Header * first_header; + + /** + * Buffer pointing to data that we are supposed + * to send as a response. + */ + void * data; + + /** + * Closure to give to the content reader + * free callback. + */ + void * crc_cls; + + /** + * How do we get more data? NULL if we are + * given all of the data up front. + */ + MHD_ContentReaderCallback crc; + + /** + * NULL if data must not be freed, otherwise + * either user-specified callback or "&free". + */ + MHD_ContentReaderFreeCallback crfc; + + /** + * Mutex to synchronize access to data/size and + * reference counts. + */ + pthread_mutex_t mutex; + + /** + * Reference count for this response. Free + * once the counter hits zero. + */ + unsigned int reference_count; + + /** + * Set to -1 if size is not known. + */ + size_t total_size; + + /** + * Size of data. + */ + size_t data_size; + + /** + * At what offset in the stream is the + * beginning of data located? + */ + size_t data_start; + +}; + + + +/** + * Add a header line to the response. + * + * @return MHD_NO on error (i.e. invalid header or content format). + */ +int +MHD_add_response_header(struct MHD_Response * response, + const char * header, + const char * content) { + struct MHD_HTTP_Header * hdr; + + if ( (response == NULL) || + (header == NULL) || + (content == NULL) || + (strlen(header) == 0) || + (strlen(content) == 0) || + (NULL != strstr(header, "\t")) || + (NULL != strstr(header, "\r")) || + (NULL != strstr(header, "\n")) || + (NULL != strstr(content, "\t")) || + (NULL != strstr(content, "\r")) || + (NULL != strstr(content, "\n")) ) + return MHD_NO; + hdr = malloc(sizeof(MHD_HTTP_Header)); + hdr->header = STRDUP(header); + hdr->value = STRDUP(content); + hdr->kind = MHD_HEADER_KIND; + hdr->next = response->first_header; + response->first_header = hdr; + return MHD_YES; +} + +/** + * Delete a header line from the response. + * + * @return MHD_NO on error (no such header known) + */ +int +MHD_del_response_header(struct MHD_Response * response, + const char * header, + const char * content) { + struct MHD_HTTP_Header * pos; + struct MHD_HTTP_Header * prev; + + if ( (header == NULL) || + (content == NULL) ) + return MHD_NO; + prev = NULL; + pos = response->first_header; + while (pos != NULL) { + if ( (0 == strcmp(header, pos->header)) && + (0 == strcmp(content, pos->value)) ) { + free(pos->header); + free(pos->value); + if (prev == NULL) + response->first_header = pos->next; + else + prev->next = pos->next; + free(pos); + return MHD_YES; + } + prev = pos; + pos = pos->next; + } + return MHD_NO; +} + +/** + * Get all of the headers added to a response. + * + * @param iterator callback to call on each header; + * maybe NULL (then just count headers) + * @param iterator_cls extra argument to iterator + * @return number of entries iterated over + */ +int +MHD_get_response_headers(struct MHD_Response * response, + MHD_KeyValueIterator * iterator, + void * iterator_cls) { + struct MHD_HTTP_Header * pos; + int numHeaders = 0; + pos = response->first_header; + while (pos != NULL) { + numHeaders++; + if ( (iterator != NULL) && + (MHD_YES != iterator(iterator_cls, + pos->kind, + pos->header, + pos->value)) ) + break; + pos = pos->next; + } + return numHeaders; +} + + +/** + * Create a response object. The response object can be extended with + * header information and then be used any number of times. + * + * @param size size of the data portion of the response, -1 for unknown + * @param crc callback to use to obtain response data + * @param crc_cls extra argument to crc + * @param crfc callback to call to free crc_cls resources + * @return NULL on error (i.e. invalid arguments, out of memory) + */ +struct MHD_Response * +MHD_create_response_from_callback(size_t size, + MHD_ContentReaderCallback crc, + void * crc_cls, + MHD_ContentReaderFreeCallback crfc) { + struct MHD_Response * retVal; + + if (crc == NULL) + return NULL; + retVal = malloc(sizeof(struct MHD_Response)); + memset(retVal, + 0, + sizeof(struct MHD_Response)); + if (pthread_mutex_init(&retVal->mutex, NULL) != 0) { + free(retVal); + return NULL; + } + retVal->crc = crc; + retVal->crfc = crfc; + retVal->crc_cls = crc_cls; + retVal->reference_count = 1; + retVal->total_size = size; + return retVal; +} + +/** + * Create a response object. The response object can be extended with + * header information and then be used any number of times. + * + * @param size size of the data portion of the response + * @param data the data itself + * @param must_free libmicrohttpd should free data when done + * @param must_copy libmicrohttpd must make a copy of data + * right away, the data maybe released anytime after + * this call returns + * @return NULL on error (i.e. invalid arguments, out of memory) + */ +struct MHD_Response * +MHD_create_response_from_data(size_t size, + void * data, + int must_free, + int must_copy) { + struct MHD_Response * retVal; + void * tmp; + + if ( (data == NULL) && + (size > 0) ) + return NULL; + retVal = malloc(sizeof(struct MHD_Response)); + memset(retVal, + 0, + sizeof(struct MHD_Response)); + if (pthread_mutex_init(&retVal->mutex, NULL) != 0) { + free(retVal); + return NULL; + } + if ( (must_copy) && + (size > 0) ) { + tmp = malloc(size); + memcpy(tmp, + data, + size); + must_free = 1; + data = tmp; + } + retVal->crc = NULL; + retVal->crfc = must_free ? &free : NULL; + retVal->crc_cls = must_free ? data : NULL; + retVal->reference_count = 1; + retVal->total_size = size; + retVal->data = data; + retVal->data_size = size; + return retVal; +} + +/** + * Destroy a response object and associated resources. Note that + * libmicrohttpd may keep some of the resources around if the response + * is still in the queue for some clients, so the memory may not + * necessarily be freed immediatley. + */ +void +MHD_destroy_response(struct MHD_Response * response) { + struct MHD_HTTP_Header * pos; + + if (response == NULL) + return; + pthread_mutex_lock(&response->mutex); + if (0 != --response->reference_count) { + pthread_mutex_unlock(&response->mutex); + return; + } + pthread_mutex_unlock(&response->mutex); + pthread_mutex_destroy(&response->mutex); + while (response->first_header != NULL) { + pos = response->first_header; + response->first_header = pos->next; + free(pos->header); + free(pos->value); + free(pos); + } + free(response); +} + + +void +MHD_increment_response_rc(struct MHD_Response * response) { + pthread_mutex_lock(&response->mutex); + response->reference_count++; + pthread_mutex_unlock(&response->mutex); +} + + +/* end of response.c */ diff --git a/src/daemon/response.h b/src/daemon/response.h new file mode 100644 index 00000000..0f0b3745 --- /dev/null +++ b/src/daemon/response.h @@ -0,0 +1,39 @@ +/* + This file is part of libmicrohttpd + (C) 2007 Daniel Pittman + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + libmicrohttpd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file response.h + * @brief Methods for managing response objects + * @author Daniel Pittman + * @author Christian Grothoff + * @version 0.1.0 + */ + +#ifndef RESPONSE_H +#define RESPONSE_H + +/** + * Increment response RC. Should this be part of the + * public API? + */ +void MHD_increment_response_rc(struct MHD_Response * response); + + +#endif diff --git a/src/daemon/session.c b/src/daemon/session.c new file mode 100644 index 00000000..9d17f12f --- /dev/null +++ b/src/daemon/session.c @@ -0,0 +1,515 @@ +/* + This file is part of libmicrohttpd + (C) 2007 Daniel Pittman + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + libmicrohttpd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file session.c + * @brief Methods for managing sessions + * @author Daniel Pittman + * @author Christian Grothoff + * @version 0.1.0 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "microhttpd.h" +#include "session.h" +#include "response.h" +#include "internal.h" +#include "config.h" + + +/** + * Get all of the headers from the request. + * + * @param iterator callback to call on each header; + * maybe NULL (then just count headers) + * @param iterator_cls extra argument to iterator + * @return number of entries iterated over + */ +int +MHD_get_session_values(struct MHD_Session * session, + enum MHD_ValueKind kind, + MHD_KeyValueIterator * iterator, + void * iterator_cls) { + int ret; + struct MHD_HTTP_Header * pos; + + if (session == NULL) + return -1; + ret = 0; + pos = session->headers_received; + while (pos != NULL) { + if (0 != (pos->kind & kind)) { + ret++; + if ( (iterator != NULL) && + (MHD_YES != iterator(iterator_cls, + kind, + pos->header, + pos->value)) ) + return ret; + } + pos = pos->next; + } + return ret; +} + + +/** + * Get a particular header value. If multiple + * values match the kind, return any one of them. + * + * @param key the header to look for + * @return NULL if no such item was found + */ +const char * +MHD_lookup_session_value(struct MHD_Session * session, + enum MHD_ValueKind kind, + const char * key) { + struct MHD_HTTP_Header * pos; + + if (session == NULL) + return NULL; + ret = 0; + pos = session->headers_received; + while (pos != NULL) { + if ( (0 != (pos->kind & kind)) && + (0 == strcmp(key, + pos->header)) ) + return pos->value; + pos = pos->next; + } + return NULL; +} + +/** + * Queue a response to be transmitted to the client (as soon as + * possible). + * + * @param session the session identifying the client + * @param status_code HTTP status code (i.e. 200 for OK) + * @param response response to transmit + * @return MHD_NO on error (i.e. reply already sent), + * MHD_YES on success or if message has been queued + */ +int +MHD_queue_response(struct MHD_Session * session, + unsigned int status_code, + struct MHD_Response * response) { + if ( (session == NULL) || + (response == NULL) || + (session->response != NULL) ) + return MHD_NO; + MHD_increment_response_rc(response); + session->response = response; + session->responseCode = status_code; + return MHD_YES; +} + + +/** + * Obtain the select sets for this session + * + * @return MHD_YES on success + */ +int +MHD_session_get_fdset(struct MHD_Session * session, + fd_set * read_fd_set, + fd_set * write_fd_set, + fd_set * except_fd_set, + int * max_fd) { + FD_SET(session->socket_fd, read_fd_set); + FD_SET(session->socket_fd, write_fd_set); + if (session->socket_fd > *max_fd) + *max_fd = session->socket_fd; + return MHD_YES; +} + + + +/* FIXME: implement/fix code below this line! */ + + + +/** + * This function is designed to parse the input buffer of a given session. + * It is assumed that the data being parsed originates at buffer location + * 0 (a valid assumption since the buffer is shifted after each message) + */ +static int +MHD_parse_message(struct MHD_Session * session) { + const char * crlfcrlf = "\r\n\r\n"; + const char * crlf = "\r\n"; + + char * saveptr; + char * saveptr1; + + struct MHD_HTTP_Header * newHeader; + char * curTok; + char * curTok1; + + int numBytes; + + curTok = strstr(session->inbuf, crlfcrlf); + + if(curTok == NULL) { + return -1; + } + + memset(curTok+2, 0, 2); + + numBytes = strlen(session->inbuf) + 2; + + curTok = strtok_r(session->inbuf, crlf, &saveptr); + + session->requestType = strtok_r(curTok, " ", &saveptr1); + + newHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header)); + if(newHeader == NULL) { + if(session->daemon->options & MHD_USE_DEBUG) + fprintf(stderr, "Error allocating memory!\n"); + return -1; + } + newHeader->kind = MHD_GET_ARGUMENT_KIND; + newHeader->header = session->requestType; + newHeader->value = strtok_r(NULL, " ", &saveptr1); + + session->headers[session->firstFreeHeader++] = newHeader; + + curTok = strtok_r(NULL, crlf, &saveptr); + while(curTok != NULL && session->firstFreeHeader < MHD_MAX_HEADERS) { + curTok1 = strtok_r(curTok, ":", &saveptr1); + newHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header)); + if(newHeader == NULL) { + if(session->daemon->options & MHD_USE_DEBUG) + fprintf(stderr, "Error allocating memory!\n"); + return -1; + } + newHeader->header = curTok1; + newHeader->value = curTok + strlen(curTok1) + 2; + //For now, everything is a get! + newHeader->kind = MHD_GET_ARGUMENT_KIND; + session->headers[session->firstFreeHeader++] = newHeader; + curTok = strtok_r(NULL, crlf, &saveptr); + } + + return numBytes; +} + + +/** + * This function needs to do a lot more (i.e. break up get arguments) + * but for now just seperates the prefix of the url from the document + * portion. + */ +static void +MHD_parse_URL(struct MHD_Session * session) { + char * working; + int pos,i; + + working = session->headers[0]->value; + + pos = 0; + for(i = 0; i < strlen(working); i++) { + if(working[i] == '/') + pos = i+1; + } + if(pos >= strlen(working)) + pos = 0; + + session->documentName = session->headers[0]->value+pos; +} + +/** + * This function handles a particular connection when it has been + * determined that there is data to be read off a socket. All implementations + * (multithreaded, external select, internal select) call this function + * to handle reads. + */ +static int +MHD_session_handle_read(struct MHD_Session * session) { + int bytes_read,i; + + if((daemon->options & MHD_USE_DEBUG) != 0) { + fprintf(stderr, "Enter MHD_handle_read\n"); + } + + if(daemon == NULL || daemon->connections[connection_id]==NULL) { + return MHD_NO; + } + + if(daemon->connections[connection_id]->responsePending) { + return MHD_YES; + } + + daemon->connections[connection_id]->firstFreeHeader = 0; + daemon->connections[connection_id]->requestType = NULL; + + for(i = 0; i < MHD_MAX_HEADERS; i++) { + daemon->connections[connection_id]->headers[i] = NULL; + } + + + + memmove(daemon->connections[connection_id]->inbuf, daemon->connections[connection_id]->inbuf+daemon->connections[connection_id]->messagePos, daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos); + + memset(daemon->connections[connection_id]->inbuf + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos, + 0, MHD_MAX_BUF_SIZE - daemon->connections[connection_id]->bufPos + (daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos)); + + bytes_read = recv(daemon->connections[connection_id]->socket_fd, + daemon->connections[connection_id]->inbuf + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos, + MHD_MAX_BUF_SIZE - (daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos), 0); + + daemon->connections[connection_id]->bufPos = bytes_read + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos; + + if(bytes_read == 0) { + MHD_destroy_session(daemon->connections[connection_id]); + daemon->connections[connection_id] = NULL; + return MHD_NO; + } else { + fprintf(stderr, "\"%s\"\n", daemon->connections[connection_id]->inbuf); + i = MHD_parse_message(daemon->connections[connection_id]); + if(i == -1) { + daemon->connections[connection_id]->messagePos = daemon->connections[connection_id]->bufPos; + return MHD_YES; + } else { + daemon->connections[connection_id]->messagePos = i; + fprintf(stderr, "Number of bytes in header: %i\n", daemon->connections[connection_id]->messagePos); + } + + daemon->connections[connection_id]->responsePending = 1; + + MHD_parse_URL(daemon->connections[connection_id]); + + for(i = 0; i < MHD_MAX_HANDLERS; i++) { + if(daemon->handlers[i] == NULL) + continue; + + //header 0 will hold the url of the request + if(strstr(daemon->connections[connection_id]->headers[0]->value, daemon->handlers[i]->uri_prefix) != NULL){ + return daemon->handlers[i]->dh(daemon->handlers[i]->dh_cls, daemon->connections[connection_id], + daemon->connections[connection_id]->documentName, daemon->connections[connection_id]->requestType, NULL, NULL); + } + } + return daemon->dh(daemon->dh_cls, daemon->connections[connection_id], + daemon->connections[connection_id]->documentName, daemon->connections[connection_id]->requestType, NULL, NULL); + } + + return MHD_YES; +} + + +/** + * This function was created to handle writes to sockets when it has been + * determined that the socket can be written to. If there is no data + * to be written, however, the function call does nothing. All implementations + * (multithreaded, external select, internal select) call this function + */ +int +MHD_session_handle_write(struct MHD_Session * session) { + struct MHD_Session * session; + + struct MHD_Response * response; + + int i; + + char * buffer[2048]; + + char * responseMessage; + int numBytesInMessage; + + if((daemon->options & MHD_USE_DEBUG) != 0) { + fprintf(stderr, "Enter MHD_handle_write\n"); + } + + + session = daemon->connections[connection_id]; + + response = session->currentResponses[session->currentResponse]; + + numBytesInMessage = 25; + + responseMessage = malloc(25); + if(responseMessage == NULL) { + if(daemon->options & MHD_USE_DEBUG) + fprintf(stderr, "Error allocating memory!\n"); + return MHD_NO; + } + + if(response == NULL) + return MHD_NO; + + pthread_mutex_lock(&response->mutex); + + if(!response->headersSent) { + sprintf(responseMessage, "HTTP/1.1 %i Go to hell!\r\n", response->responseCode); + fprintf(stderr, "%s\n", responseMessage); + if(send(session->socket_fd, responseMessage, strlen(responseMessage), 0) != strlen(responseMessage)) { + fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n"); + pthread_mutex_unlock(&response->mutex); + return MHD_NO; + } + + for(i = 0; i < MHD_MAX_HEADERS; i++) { + if(response->headers[i] == NULL) + continue; + + if(strlen(response->headers[i]->header) + strlen(response->headers[i]->value) + 5 > numBytesInMessage) { + free(responseMessage); + responseMessage = malloc(strlen(response->headers[i]->header) + strlen(response->headers[i]->value) + 5); + if(responseMessage == NULL) { + if(daemon->options & MHD_USE_DEBUG) + fprintf(stderr, "Error allocating memory!\n"); + pthread_mutex_unlock(&response->mutex); + return MHD_NO; + } + numBytesInMessage = strlen(response->headers[i]->header) + strlen(response->headers[i]->value) + 5; + } + sprintf(responseMessage, "%s: %s\r\n", response->headers[i]->header, response->headers[i]->value); + fprintf(stderr, "%s\n", responseMessage); + if(send(session->socket_fd, responseMessage, strlen(responseMessage), 0) != strlen(responseMessage)) { + fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n"); + pthread_mutex_unlock(&response->mutex); + return MHD_NO; + } + } + + response->headersSent = 1; + } + + if(response->data != NULL) { + if(response->bytesSentSoFar == 0) { + if(numBytesInMessage < 32) { + free(responseMessage); + responseMessage = malloc(32); + if(responseMessage == NULL) { + if(daemon->options & MHD_USE_DEBUG) + fprintf(stderr, "Error allocating memory!\n"); + pthread_mutex_unlock(&response->mutex); + return MHD_NO; + } + } + sprintf(responseMessage, "Content-length: %llu\r\n\r\n", (unsigned long long)response->size); + fprintf(stderr, "%s\n", responseMessage); + if(send(session->socket_fd, responseMessage, strlen(responseMessage),0)!= strlen(responseMessage)) { + fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n"); + pthread_mutex_unlock(&response->mutex); + return MHD_NO; + } + } + + i = send(session->socket_fd, response->data+response->bytesSentSoFar, response->size-response->bytesSentSoFar,0); + response->bytesSentSoFar += i; + + fprintf(stderr, "Sent %i bytes of data\nTotal to send is %llu bytes\n", i, (unsigned long long)response->size); + + if(response->bytesSentSoFar == response->size) { + session->currentResponses[session->currentResponse] = NULL; + session->currentResponse = (session->currentResponse + 1) % MHD_MAX_RESPONSE; + response->currentSession = NULL; + + if(response->freeWhenFinished) { + pthread_mutex_unlock(&response->mutex); + MHD_destroy_response(response); + } + /*THIS NEEDS TO BE HANDLED ANOTHER WAY!!! TIMEOUT, ect..., as of now this is the only way to get test case to work + * since client never disconnects on their own! + */ + if(session->currentResponses[session->currentResponse] == NULL) { + MHD_destroy_session(session); + daemon->connections[connection_id] = NULL; + return MHD_NO; + } + } + } else { + if(response->crc == NULL) { + pthread_mutex_unlock(&response->mutex); + return MHD_NO; + } + + if(response->bytesSentSoFar == 0) { + if(send(session->socket_fd, "\r\n", response->size,0) != 2) { + fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n"); + pthread_mutex_unlock(&response->mutex); + return MHD_NO; + } + } + memset(buffer, 0, 2048); + + i = response->crc(response->crc_cls, response->bytesSentSoFar, (char *)buffer, 2048); + + if(i == -1) { + pthread_mutex_unlock(&response->mutex); + + session->currentResponses[session->currentResponse] = NULL; + session->currentResponse = (session->currentResponse + 1) % MHD_MAX_RESPONSE; + response->currentSession = NULL; + + if(response->freeWhenFinished) { + pthread_mutex_unlock(&response->mutex); + MHD_destroy_response(response); + } + /*THIS NEEDS TO BE HANDLED ANOTHER WAY!!! TIMEOUT, ect..., as of now this is the only way to get test case to work + * since client never disconnects on their own! + */ + if(session->currentResponses[session->currentResponse] == NULL) { + MHD_destroy_session(session); + daemon->connections[connection_id] = NULL; + return MHD_NO; + } + + } else { + i = send(session->socket_fd, buffer, i,0); + response->bytesSentSoFar += i; + } + } + pthread_mutex_unlock(&response->mutex); + return MHD_YES; +} + + + + + + + + + +/** + * @return -1 if no data uploaded; otherwise number of bytes + * read into buf; 0 for end of transmission + * Specification not complete at this time. + */ +int +MHD_read_file_upload(struct MHD_Session * session, + void * buf, + size_t len) { + return -1; /* FIXME: not implemented */ +} + + diff --git a/src/daemon/session.h b/src/daemon/session.h new file mode 100644 index 00000000..5e8be40c --- /dev/null +++ b/src/daemon/session.h @@ -0,0 +1,154 @@ +/* + This file is part of libmicrohttpd + (C) 2007 Daniel Pittman + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + libmicrohttpd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file session.h + * @brief Methods for managing sessions + * @author Daniel Pittman + * @author Christian Grothoff + * @version 0.1.0 + */ + +#ifndef SESSION_H +#define SESSION_H + + +struct MHD_Session { + struct MHD_Session * next; + + struct MHD_Daemon * daemon; + + struct MHD_HTTP_Header * headers_received; + + struct MHD_Response * response; + + char * requestType; + + /** + * Buffer for reading requests. + */ + void * read_buffer; + + /** + * Buffer for writing response. + */ + void * write_buffer; + + /** + * Foreign address (of length addr_len). + */ + struct sockaddr_in * addr; + + /** + * Thread for this session (if we are using + * one thread per connection). + */ + pthread_t pid; + + size_t read_buffer_size; + + size_t readLoc; + + size_t write_buffer_size; + + size_t writeLoc; + + /** + * Current write position in the actual response + * (excluding headers, content only; should be 0 + * while sending headers). + */ + size_t messagePos; + + /** + * Length of the foreign address. + */ + socklen_t addr_len; + + /** + * Socket for this connection. Set to -1 if + * this connection has died (daemon should clean + * up in that case). + */ + int socket_fd; + + /** + * Have we finished receiving all of the headers yet? + * Set to 1 once we are done processing all of the + * headers. Note that due to pipelining, it is + * possible that the NEXT request is already + * (partially) waiting in the read buffer. + */ + int headersReceived; + + /** + * Have we finished receiving the data from a + * potential file-upload? + */ + int bodyReceived; + + /** + * Have we finished sending all of the headers yet? + */ + int headersSent; + + /** + * HTTP response code. Only valid if response object + * is already set. + */ + unsigned int responseCode; + +}; + + +/** + * Obtain the select sets for this session. + * + * @return MHD_YES on success + */ +int +MHD_session_get_fdset(struct MHD_Session * session, + fd_set * read_fd_set, + fd_set * write_fd_set, + fd_set * except_fd_set, + int * max_fd); + + +/** + * This function handles a particular connection when it has been + * determined that there is data to be read off a socket. All implementations + * (multithreaded, external select, internal select) call this function + * to handle reads. + */ +int +MHD_session_handle_read(struct MHD_Session * session); + + +/** + * This function was created to handle writes to sockets when it has been + * determined that the socket can be written to. If there is no data + * to be written, however, the function call does nothing. All implementations + * (multithreaded, external select, internal select) call this function + */ +int +MHD_session_handle_write(struct MHD_Session * session); + + +#endif -- cgit v1.2.3