diff options
Diffstat (limited to 'src/daemon/daemon.c')
-rw-r--r-- | src/daemon/daemon.c | 1817 |
1 files changed, 396 insertions, 1421 deletions
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 <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <netdb.h> #include <string.h> #include <unistd.h> +#include <stdarg> #include <fcntl.h> #include <pthread.h> +#include <netinet/in.h> +#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); } |