[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[GNUnet-SVN] r4740 - libmicrohttpd/src/daemon
From: |
gnunet |
Subject: |
[GNUnet-SVN] r4740 - libmicrohttpd/src/daemon |
Date: |
Tue, 3 Apr 2007 18:02:54 -0600 (MDT) |
Author: dpittman
Date: 2007-04-03 18:02:54 -0600 (Tue, 03 Apr 2007)
New Revision: 4740
Modified:
libmicrohttpd/src/daemon/daemon.c
libmicrohttpd/src/daemon/daemontest.c
Log:
First draft implementation of libmicrohttpd API. Many features incomplete at
this time, some functionality has yet to be tested.
Modified: libmicrohttpd/src/daemon/daemon.c
===================================================================
--- libmicrohttpd/src/daemon/daemon.c 2007-04-03 23:04:23 UTC (rev 4739)
+++ libmicrohttpd/src/daemon/daemon.c 2007-04-04 00:02:54 UTC (rev 4740)
@@ -1,6 +1,6 @@
/*
This file is part of libmicrohttpd
- (C) 2007 YOUR NAME HERE
+ (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
@@ -20,44 +20,527 @@
/**
* @file daemon.c
- * @brief FIXME
- * @author FIXME
+ * @brief This is my implementation of the libmicrohttpd interface. Many
features incomplete at this time.
+ * @author Daniel Pittman
+ * @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 <fcntl.h>
+#include <pthread.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;
+};
+
+struct MHD_HTTP_Header {
+ char * header;
+ char * headerContent;
+ enum MHD_ValueKind kind;
+};
+
/**
- * 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
- * @param apc_cls extra argument to apc
- * @param dh default handler for all URIs
- * @param dh_cls extra argument to dh
- * @return NULL on error, handle to daemon on success
+ * Add a header line to the response.
+ *
+ * @return MHD_NO on error (i.e. invalid header or content format).
*/
-struct MHD_Daemon *
-MHD_start_daemon(unsigned int options,
- unsigned short port,
- MHD_AcceptPolicyCallback apc,
- void * apc_cls,
- MHD_AccessHandlerCallback dh,
- void * dh_cls) {
- return NULL;
+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;
+ }
+
+ if(response->firstFreeHeader >= MHD_MAX_HEADERS) {
+ return MHD_NO;
+ }
+
+ newHeader = (char *)malloc(strlen(header)+1);
+ newContent = (char *)malloc(strlen(content)+1);
+
+ if(newHeader == NULL || newContent == NULL) {
+ fprintf(stderr, "Error allocating memory!\n");
+ return MHD_NO;
+ }
+
+ sprintf(newHeader, "%s", header);
+ sprintf(newContent, "%s", content);
+
+
+ if(strtok_r(newHeader, " \t\r\n", &saveptr) != NULL) {
+ fprintf(stderr, "Malformed header!\n");
+ free(newContent);
+ free(newHeader);
+ return MHD_NO;
+ }
+
+ if(strtok_r(newContent, "\n", &saveptr) != NULL) {
+ fprintf(stderr, "Malformed content!\n");
+ free(newContent);
+ free(newHeader);
+ return MHD_NO;
+ }
+
+ struct MHD_HTTP_Header * newHTTPHeader = (struct MHD_HTTP_Header
*)malloc(sizeof(struct MHD_HTTP_Header));
+
+ if(newHTTPHeader == NULL) {
+ fprintf(stderr, "Error allocating memory!\n");
+ free(newContent);
+ free(newHeader);
+ return MHD_NO;
+ }
+
+ response->headers[response->firstFreeHeader]->header = newHeader;
+ response->headers[response->firstFreeHeader]->headerContent =
newContent;
+
+ //For now, everything is a HTTP Header... this needs to be improved!
+ response->headers[response->firstFreeHeader]->kind = MHD_HEADER_KIND;
+
+ 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;
}
+/**
+ * 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;
+
+ daemon->connections[first_free] = (struct MHD_Session
*)malloc(sizeof(struct MHD_Session));
+
+ if(daemon->connections[first_free] == NULL) {
+ 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 = NULL;
+ 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;
+ }
+
+ if(daemon->max_fd < daemon->connections[first_free]->socket_fd) {
+ daemon->max_fd = daemon->connections[first_free]->socket_fd;
+ }
+
+ return first_free;
+}
+
/**
- * Shutdown an http daemon.
+ * 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;
+ int i;
+
+
+ if(crc == NULL) {
+ 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) {
+ 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;
+ }
+
+ for(i = 0; i < MHD_MAX_HEADERS; i++) {
+ retVal->headers[i] = NULL;
+ }
+
+ retVal->data = NULL;
+ retVal->must_free = 0;
+
+ 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;
+ 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.
+ *
+ * @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_stop_daemon(struct MHD_Daemon * daemon) {
+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.
+ */
+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);
+}
+
+/**
* Obtain the select sets for this daemon.
*
* @return MHD_YES on success, MHD_NO if this
@@ -70,57 +553,65 @@
fd_set * write_fd_set,
fd_set * except_fd_set,
int * max_fd) {
- return 0;
+
+ 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;
}
/**
- * Run webserver operations (without blocking unless
- * in client callbacks). This method should be called
- * by clients in combination with MHD_get_fdset
- * if the client-controlled select method is used.
+ * Get all of the headers added to a response.
*
- * @return MHD_YES on success, MHD_NO if this
- * daemon was not started with the right
- * options for this call.
- */
+ * @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_run(struct MHD_Daemon * daemon) {
- return 0;
-}
+MHD_get_response_headers(struct MHD_Response * response,
+ MHD_KeyValueIterator * iterator,
+ void * iterator_cls) {
+ int i, numHeaders;
+ if(response == NULL) {
+ return -1;
+ }
-/**
- * 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
- */
-int
-MHD_register_handler(struct MHD_Daemon * daemon,
- const char * uri_prefix,
- MHD_AccessHandlerCallback dh,
- void * dh_cls) {
- return 0;
+ 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;
}
/**
- * 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) {
- return 0;
-}
-
-/**
* Get all of the headers from the request.
*
* @param iterator callback to call on each header;
@@ -132,9 +623,333 @@
MHD_get_session_values(struct MHD_Session * session,
enum MHD_ValueKind kind,
MHD_KeyValueIterator * iterator,
- void * iterator_cls);
+ 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
+ */
+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.
+}
+
+/**
+ * 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
+ */
+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: %i\r\n\r\n",
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 %i
bytes\n", i, 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.
*
@@ -145,10 +960,109 @@
MHD_lookup_session_value(struct MHD_Session * session,
enum MHD_ValueKind kind,
const char * key) {
- return NULL;
+ 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;
}
+
/**
+ * 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)
+ */
+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;
+}
+
+/**
+ * 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).
*
@@ -162,105 +1076,464 @@
MHD_queue_response(struct MHD_Session * session,
unsigned int status_code,
struct MHD_Response * response) {
- return 0;
+
+ //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;
}
-
/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
+ * @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
+ * in client callbacks). This method should be called
+ * by clients in combination with MHD_get_fdset
+ * if the client-controlled select method is used.
*
- * @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)
+ * @return MHD_YES on success, MHD_NO if this
+ * daemon was not started with the right
+ * options for this call.
*/
-struct MHD_Response *
-MHD_create_response_from_callback(size_t size,
- MHD_ContentReaderCallback crc,
- void * crc_cls,
- MHD_ContentReaderFreeCallback crfc) {
- return NULL;
+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);
+ }
}
+
/**
- * 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
- * @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)
+ * @param uri_prefix
+ * @return MRI_NO if a handler for this exact prefix
+ * already exists
*/
-struct MHD_Response *
-MHD_create_response_from_data(size_t size,
- void * data,
- int must_free,
- int must_copy) {
- return NULL;
+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;
+
}
/**
- * 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.
+ * 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_destroy_response(struct MHD_Response * response) {
+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;
}
/**
- * Add a header line to the response.
- *
- * @return MHD_NO on error (i.e. invalid header or content format).
+ * 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.
*/
-int
-MHD_add_response_header(struct MHD_Response * response,
- const char * header,
- const char * content) {
- return 0;
+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;
}
/**
- * Delete a header line from the response.
- *
- * @return MHD_NO on error (no such header known)
+ * 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
+ * @param apc_cls extra argument to apc
+ * @param dh default handler for all URIs
+ * @param dh_cls extra argument to dh
+ * @return NULL on error, handle to daemon on success
*/
-int
-MHD_del_response_header(struct MHD_Response * response,
- const char * header,
- const char * content) {
- return 0;
+struct MHD_Daemon *
+MHD_start_daemon(unsigned int options,
+ unsigned short port,
+ MHD_AcceptPolicyCallback apc,
+ 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 = NULL;
+
+ 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;
+
}
/**
- * 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) {
- return -1;
+ * Shutdown an http daemon.
+ */
+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 != NULL) {
+ pthread_join(daemon->pid, NULL);
+ }
+
+ for(i = 0; i < MHD_MAX_CONNECTIONS; i++) {
+ if(daemon->connections[i] != NULL) {
+ if(daemon->connections[i]->pid != NULL) {
+ 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);
}
+
/**
- * @return -1 if no data uploaded; otherwise number of bytes
- * read into buf; 0 for end of transmission
+ * 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_read_file_upload(struct MHD_Session * session,
- void * buf,
- size_t len) {
- return -1;
+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;
}
Modified: libmicrohttpd/src/daemon/daemontest.c
===================================================================
--- libmicrohttpd/src/daemon/daemontest.c 2007-04-03 23:04:23 UTC (rev
4739)
+++ libmicrohttpd/src/daemon/daemontest.c 2007-04-04 00:02:54 UTC (rev
4740)
@@ -32,7 +32,15 @@
#include <string.h>
static int testStartError() {
- return NULL != MHD_start_daemon(0, 0, NULL, NULL, NULL, NULL);
+ struct MHD_Daemon * d;
+
+ d = MHD_start_daemon(MHD_USE_DEBUG, 0, NULL, NULL, NULL, NULL);
+
+ if(d == NULL) {
+ return 0;
+ } else {
+ return 1;
+ }
}
static int apc_nothing(void * cls,
@@ -99,41 +107,184 @@
static int testStartStop() {
struct MHD_Daemon * d;
- d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY | MHD_USE_IPv4,
+ d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY | MHD_USE_IPv4 |
MHD_USE_DEBUG,
1080,
&apc_nothing,
NULL,
&ahc_nothing,
NULL);
+ if (d == NULL) {
+ return 1;
+ }
+ MHD_stop_daemon(d);
+ return 0;
+}
+
+static int testRun() {
+ struct MHD_Daemon * d;
+ fd_set read;
+ int maxfd;
+ int i;
+
+ d = MHD_start_daemon(MHD_USE_IPv4 | MHD_USE_DEBUG,
+ 1080,
+ &apc_all,
+ NULL,
+ &ahc_nothing,
+ NULL);
+
+ if(d == NULL) {
+ return 1;
+ }
+ fprintf(stderr, "Testing external select!\n");
+ i = 0;
+ while(i < 15) {
+ MHD_get_fdset(d, &read, &read, &read, &maxfd);
+ if(MHD_run(d) == MHD_NO) {
+ MHD_stop_daemon(d);
+ return 1;
+ }
+ sleep(1);
+ i++;
+ }
+ return 0;
+}
+
+static int testThread() {
+ struct MHD_Daemon * d;
+ d = MHD_start_daemon(MHD_USE_IPv4 | MHD_USE_DEBUG |
MHD_USE_SELECT_INTERNALLY,
+ 1081,
+ &apc_all,
+ NULL,
+ &ahc_nothing,
+ NULL);
+
+ if(d == NULL) {
+ return 1;
+ }
+
+ fprintf(stderr, "Testing internal select!\n");
+ if (MHD_run(d) == MHD_NO) {
+ return 1;
+ } else {
+ sleep(15);
+ MHD_stop_daemon(d);
+ }
+ return 0;
+}
+
+static int testMultithread() {
+ struct MHD_Daemon * d;
+ d = MHD_start_daemon(MHD_USE_IPv4 | MHD_USE_DEBUG |
MHD_USE_THREAD_PER_CONNECTION,
+ 1082,
+ &apc_all,
+ NULL,
+ &ahc_nothing,
+ NULL);
+
+ if(d == NULL) {
+ return 1;
+ }
+
+ fprintf(stderr, "Testing thread per connection!\n");
+ if (MHD_run(d) == MHD_NO) {
+ return 1;
+ } else {
+ sleep(15);
+ MHD_stop_daemon(d);
+ }
+ return 0;
+}
+
+static int testInternalGet() {
+ struct MHD_Daemon * d;
+ CURL * c;
+ char buf[2048];
+ struct CBC cbc;
+
+ cbc.buf = buf;
+ cbc.size = 2048;
+ cbc.pos = 0;
+ d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY | MHD_USE_IPv4 |
MHD_USE_DEBUG,
+ 1083,
+ &apc_all,
+ NULL,
+ &ahc_echo,
+ "GET");
if (d == NULL)
return 1;
+
+ if(MHD_run(d) == MHD_NO) {
+ MHD_stop_daemon(d);
+ return 2;
+ }
+
+ c = curl_easy_init();
+ curl_easy_setopt(c,
+ CURLOPT_URL,
+ "http://localhost:1083/hello_world");
+ curl_easy_setopt(c,
+ CURLOPT_WRITEFUNCTION,
+ ©Buffer);
+ curl_easy_setopt(c,
+ CURLOPT_WRITEDATA,
+ &cbc);
+ curl_easy_setopt(c,
+ CURLOPT_FAILONERROR,
+ 1);
+ curl_easy_setopt(c,
+ CURLOPT_CONNECTTIMEOUT,
+ 15L);
+ // NOTE: use of CONNECTTIMEOUT without also
+ // setting NOSIGNAL results in really weird
+ // crashes on my system!
+ curl_easy_setopt(c,
+ CURLOPT_NOSIGNAL,
+ 1);
+ if (CURLE_OK != curl_easy_perform(c))
+ return 3;
+
+ curl_easy_cleanup(c);
+
+ if (cbc.pos != strlen("hello_world"))
+ return 4;
+
+ if (0 != strncmp("hello_world",
+ cbc.buf,
+ strlen("hello_world")))
+ return 5;
+
MHD_stop_daemon(d);
+
return 0;
}
-static int testGet() {
+static int testMultithreadedGet() {
struct MHD_Daemon * d;
CURL * c;
- int ret;
char buf[2048];
struct CBC cbc;
cbc.buf = buf;
cbc.size = 2048;
cbc.pos = 0;
- ret = 0;
- d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY | MHD_USE_IPv4,
- 1080,
+ d = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION | MHD_USE_IPv4 |
MHD_USE_DEBUG,
+ 1084,
&apc_all,
NULL,
&ahc_echo,
"GET");
if (d == NULL)
return 1;
+
+ if(MHD_run(d) == MHD_NO)
+ return 2;
+
+
c = curl_easy_init();
curl_easy_setopt(c,
CURLOPT_URL,
- "http://localhost:1080/hello_world");
+ "http://localhost:1084/hello_world");
curl_easy_setopt(c,
CURLOPT_WRITEFUNCTION,
©Buffer);
@@ -146,31 +297,61 @@
curl_easy_setopt(c,
CURLOPT_CONNECTTIMEOUT,
15L);
- /* NOTE: use of CONNECTTIMEOUT without also
- setting NOSIGNAL results in really weird
- crashes on my system! */
+ // NOTE: use of CONNECTTIMEOUT without also
+ // setting NOSIGNAL results in really weird
+ // crashes on my system!
curl_easy_setopt(c,
CURLOPT_NOSIGNAL,
1);
if (CURLE_OK != curl_easy_perform(c))
- ret = 1;
+ return 3;
curl_easy_cleanup(c);
if (cbc.pos != strlen("hello_world"))
- ret = 2;
+ return 4;
+
if (0 != strncmp("hello_world",
cbc.buf,
strlen("hello_world")))
- ret = 3;
+ return 5;
+
MHD_stop_daemon(d);
- return ret;
+
+ return 0;
}
int main(int argc,
char * const * argv) {
unsigned int errorCount = 0;
-
+ fprintf(stderr, "***testStartError()***\n");
+ fprintf(stderr, "***This test verifies the start function responds to bad
arguments correctly***\n");
errorCount += testStartError();
+ fprintf(stderr, "errorCount is %i\n", errorCount);
+ fprintf(stderr, "***testStartStop()***\n");
+ fprintf(stderr, "***This test verifies that the daemon can be started and
stopped normally***\n");
errorCount += testStartStop();
- errorCount += testGet();
+ fprintf(stderr, "errorCount is %i\n", errorCount);
+ fprintf(stderr, "***testInternalGet()***\n");
+ fprintf(stderr, "***This test verifies the functionality of internal select
using a canned request***\n");
+ errorCount += testInternalGet();
+ fprintf(stderr, "errorCount is %i\n", errorCount);
+ fprintf(stderr, "***testMultithreadedGet()***\n");
+ fprintf(stderr, "***This test verifies the functionality of multithreaded
connections using a canned request***\n");
+ errorCount += testMultithreadedGet();
+ fprintf(stderr, "errorCount is %i\n", errorCount);
+ fprintf(stderr, "***testRun()***\n");
+ fprintf(stderr, "***This test verifies the functionality of external
select***\n");
+ fprintf(stderr, "***The sever will sit on the announced port for 15 seconds
and wait for external messages***\n");
+ errorCount += testRun();
+ fprintf(stderr, "errorCount is %i\n", errorCount);
+ fprintf(stderr, "***testThread()***\n");
+ fprintf(stderr, "***This test verifies the functionality of internal
select***\n");
+ fprintf(stderr, "***The sever will sit on the announced port for 15 seconds
and wait for external messages***\n");
+ errorCount += testThread();
+ fprintf(stderr, "errorCount is %i\n", errorCount);
+ fprintf(stderr, "***testMultithread()***\n");
+ fprintf(stderr, "***This test verifies the functionality of multithreaded
connections***\n");
+ fprintf(stderr, "***The sever will sit on the announced port for 15 seconds
and wait for external messages***\n");
+ errorCount += testMultithread();
+ fprintf(stderr, "errorCount is %i\n", errorCount);
return errorCount != 0; /* 0 == pass */
}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [GNUnet-SVN] r4740 - libmicrohttpd/src/daemon,
gnunet <=