summaryrefslogtreecommitdiff
path: root/src/daemon/session.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon/session.c')
-rw-r--r--src/daemon/session.c515
1 files changed, 515 insertions, 0 deletions
diff --git a/src/daemon/session.c b/src/daemon/session.c
new file mode 100644
index 00000000..9d17f12f
--- /dev/null
+++ b/src/daemon/session.c
@@ -0,0 +1,515 @@
+/*
+ This file is part of libmicrohttpd
+ (C) 2007 Daniel Pittman
+
+ libmicrohttpd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ libmicrohttpd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libmicrohttpd; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file session.c
+ * @brief Methods for managing sessions
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ * @version 0.1.0
+ */
+
+
+#include <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 "session.h"
+#include "response.h"
+#include "internal.h"
+#include "config.h"
+
+
+/**
+ * Get all of the headers from the request.
+ *
+ * @param iterator callback to call on each header;
+ * maybe NULL (then just count headers)
+ * @param iterator_cls extra argument to iterator
+ * @return number of entries iterated over
+ */
+int
+MHD_get_session_values(struct MHD_Session * session,
+ enum MHD_ValueKind kind,
+ MHD_KeyValueIterator * iterator,
+ void * iterator_cls) {
+ int ret;
+ struct MHD_HTTP_Header * pos;
+
+ if (session == NULL)
+ return -1;
+ ret = 0;
+ pos = session->headers_received;
+ while (pos != NULL) {
+ if (0 != (pos->kind & kind)) {
+ ret++;
+ if ( (iterator != NULL) &&
+ (MHD_YES != iterator(iterator_cls,
+ kind,
+ pos->header,
+ pos->value)) )
+ return ret;
+ }
+ pos = pos->next;
+ }
+ return ret;
+}
+
+
+/**
+ * Get a particular header value. If multiple
+ * values match the kind, return any one of them.
+ *
+ * @param key the header to look for
+ * @return NULL if no such item was found
+ */
+const char *
+MHD_lookup_session_value(struct MHD_Session * session,
+ enum MHD_ValueKind kind,
+ const char * key) {
+ struct MHD_HTTP_Header * pos;
+
+ if (session == NULL)
+ return NULL;
+ ret = 0;
+ pos = session->headers_received;
+ while (pos != NULL) {
+ if ( (0 != (pos->kind & kind)) &&
+ (0 == strcmp(key,
+ pos->header)) )
+ return pos->value;
+ pos = pos->next;
+ }
+ return NULL;
+}
+
+/**
+ * Queue a response to be transmitted to the client (as soon as
+ * possible).
+ *
+ * @param session the session identifying the client
+ * @param status_code HTTP status code (i.e. 200 for OK)
+ * @param response response to transmit
+ * @return MHD_NO on error (i.e. reply already sent),
+ * MHD_YES on success or if message has been queued
+ */
+int
+MHD_queue_response(struct MHD_Session * session,
+ unsigned int status_code,
+ struct MHD_Response * response) {
+ if ( (session == NULL) ||
+ (response == NULL) ||
+ (session->response != NULL) )
+ return MHD_NO;
+ MHD_increment_response_rc(response);
+ session->response = response;
+ session->responseCode = status_code;
+ return MHD_YES;
+}
+
+
+/**
+ * Obtain the select sets for this session
+ *
+ * @return MHD_YES on success
+ */
+int
+MHD_session_get_fdset(struct MHD_Session * session,
+ fd_set * read_fd_set,
+ fd_set * write_fd_set,
+ fd_set * except_fd_set,
+ int * max_fd) {
+ FD_SET(session->socket_fd, read_fd_set);
+ FD_SET(session->socket_fd, write_fd_set);
+ if (session->socket_fd > *max_fd)
+ *max_fd = session->socket_fd;
+ return MHD_YES;
+}
+
+
+
+/* FIXME: implement/fix code below this line! */
+
+
+
+/**
+ * This function is designed to parse the input buffer of a given session.
+ * It is assumed that the data being parsed originates at buffer location
+ * 0 (a valid assumption since the buffer is shifted after each message)
+ */
+static int
+MHD_parse_message(struct MHD_Session * session) {
+ const char * crlfcrlf = "\r\n\r\n";
+ const char * crlf = "\r\n";
+
+ char * saveptr;
+ char * saveptr1;
+
+ struct MHD_HTTP_Header * newHeader;
+ char * curTok;
+ char * curTok1;
+
+ int numBytes;
+
+ curTok = strstr(session->inbuf, crlfcrlf);
+
+ if(curTok == NULL) {
+ return -1;
+ }
+
+ memset(curTok+2, 0, 2);
+
+ numBytes = strlen(session->inbuf) + 2;
+
+ curTok = strtok_r(session->inbuf, crlf, &saveptr);
+
+ session->requestType = strtok_r(curTok, " ", &saveptr1);
+
+ newHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header));
+ if(newHeader == NULL) {
+ if(session->daemon->options & MHD_USE_DEBUG)
+ fprintf(stderr, "Error allocating memory!\n");
+ return -1;
+ }
+ newHeader->kind = MHD_GET_ARGUMENT_KIND;
+ newHeader->header = session->requestType;
+ newHeader->value = strtok_r(NULL, " ", &saveptr1);
+
+ session->headers[session->firstFreeHeader++] = newHeader;
+
+ curTok = strtok_r(NULL, crlf, &saveptr);
+ while(curTok != NULL && session->firstFreeHeader < MHD_MAX_HEADERS) {
+ curTok1 = strtok_r(curTok, ":", &saveptr1);
+ newHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header));
+ if(newHeader == NULL) {
+ if(session->daemon->options & MHD_USE_DEBUG)
+ fprintf(stderr, "Error allocating memory!\n");
+ return -1;
+ }
+ newHeader->header = curTok1;
+ newHeader->value = curTok + strlen(curTok1) + 2;
+ //For now, everything is a get!
+ newHeader->kind = MHD_GET_ARGUMENT_KIND;
+ session->headers[session->firstFreeHeader++] = newHeader;
+ curTok = strtok_r(NULL, crlf, &saveptr);
+ }
+
+ return numBytes;
+}
+
+
+/**
+ * This function needs to do a lot more (i.e. break up get arguments)
+ * but for now just seperates the prefix of the url from the document
+ * portion.
+ */
+static void
+MHD_parse_URL(struct MHD_Session * session) {
+ char * working;
+ int pos,i;
+
+ working = session->headers[0]->value;
+
+ pos = 0;
+ for(i = 0; i < strlen(working); i++) {
+ if(working[i] == '/')
+ pos = i+1;
+ }
+ if(pos >= strlen(working))
+ pos = 0;
+
+ session->documentName = session->headers[0]->value+pos;
+}
+
+/**
+ * This function handles a particular connection when it has been
+ * determined that there is data to be read off a socket. All implementations
+ * (multithreaded, external select, internal select) call this function
+ * to handle reads.
+ */
+static int
+MHD_session_handle_read(struct MHD_Session * session) {
+ int bytes_read,i;
+
+ if((daemon->options & MHD_USE_DEBUG) != 0) {
+ fprintf(stderr, "Enter MHD_handle_read\n");
+ }
+
+ if(daemon == NULL || daemon->connections[connection_id]==NULL) {
+ return MHD_NO;
+ }
+
+ if(daemon->connections[connection_id]->responsePending) {
+ return MHD_YES;
+ }
+
+ daemon->connections[connection_id]->firstFreeHeader = 0;
+ daemon->connections[connection_id]->requestType = NULL;
+
+ for(i = 0; i < MHD_MAX_HEADERS; i++) {
+ daemon->connections[connection_id]->headers[i] = NULL;
+ }
+
+
+
+ memmove(daemon->connections[connection_id]->inbuf, daemon->connections[connection_id]->inbuf+daemon->connections[connection_id]->messagePos, daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos);
+
+ memset(daemon->connections[connection_id]->inbuf + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos,
+ 0, MHD_MAX_BUF_SIZE - daemon->connections[connection_id]->bufPos + (daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos));
+
+ bytes_read = recv(daemon->connections[connection_id]->socket_fd,
+ daemon->connections[connection_id]->inbuf + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos,
+ MHD_MAX_BUF_SIZE - (daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos), 0);
+
+ daemon->connections[connection_id]->bufPos = bytes_read + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos;
+
+ if(bytes_read == 0) {
+ MHD_destroy_session(daemon->connections[connection_id]);
+ daemon->connections[connection_id] = NULL;
+ return MHD_NO;
+ } else {
+ fprintf(stderr, "\"%s\"\n", daemon->connections[connection_id]->inbuf);
+ i = MHD_parse_message(daemon->connections[connection_id]);
+ if(i == -1) {
+ daemon->connections[connection_id]->messagePos = daemon->connections[connection_id]->bufPos;
+ return MHD_YES;
+ } else {
+ daemon->connections[connection_id]->messagePos = i;
+ fprintf(stderr, "Number of bytes in header: %i\n", daemon->connections[connection_id]->messagePos);
+ }
+
+ daemon->connections[connection_id]->responsePending = 1;
+
+ MHD_parse_URL(daemon->connections[connection_id]);
+
+ for(i = 0; i < MHD_MAX_HANDLERS; i++) {
+ if(daemon->handlers[i] == NULL)
+ continue;
+
+ //header 0 will hold the url of the request
+ if(strstr(daemon->connections[connection_id]->headers[0]->value, daemon->handlers[i]->uri_prefix) != NULL){
+ return daemon->handlers[i]->dh(daemon->handlers[i]->dh_cls, daemon->connections[connection_id],
+ daemon->connections[connection_id]->documentName, daemon->connections[connection_id]->requestType, NULL, NULL);
+ }
+ }
+ return daemon->dh(daemon->dh_cls, daemon->connections[connection_id],
+ daemon->connections[connection_id]->documentName, daemon->connections[connection_id]->requestType, NULL, NULL);
+ }
+
+ return MHD_YES;
+}
+
+
+/**
+ * This function was created to handle writes to sockets when it has been
+ * determined that the socket can be written to. If there is no data
+ * to be written, however, the function call does nothing. All implementations
+ * (multithreaded, external select, internal select) call this function
+ */
+int
+MHD_session_handle_write(struct MHD_Session * session) {
+ struct MHD_Session * session;
+
+ struct MHD_Response * response;
+
+ int i;
+
+ char * buffer[2048];
+
+ char * responseMessage;
+ int numBytesInMessage;
+
+ if((daemon->options & MHD_USE_DEBUG) != 0) {
+ fprintf(stderr, "Enter MHD_handle_write\n");
+ }
+
+
+ session = daemon->connections[connection_id];
+
+ response = session->currentResponses[session->currentResponse];
+
+ numBytesInMessage = 25;
+
+ responseMessage = malloc(25);
+ if(responseMessage == NULL) {
+ if(daemon->options & MHD_USE_DEBUG)
+ fprintf(stderr, "Error allocating memory!\n");
+ return MHD_NO;
+ }
+
+ if(response == NULL)
+ return MHD_NO;
+
+ pthread_mutex_lock(&response->mutex);
+
+ if(!response->headersSent) {
+ sprintf(responseMessage, "HTTP/1.1 %i Go to hell!\r\n", response->responseCode);
+ fprintf(stderr, "%s\n", responseMessage);
+ if(send(session->socket_fd, responseMessage, strlen(responseMessage), 0) != strlen(responseMessage)) {
+ fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n");
+ pthread_mutex_unlock(&response->mutex);
+ return MHD_NO;
+ }
+
+ for(i = 0; i < MHD_MAX_HEADERS; i++) {
+ if(response->headers[i] == NULL)
+ continue;
+
+ if(strlen(response->headers[i]->header) + strlen(response->headers[i]->value) + 5 > numBytesInMessage) {
+ free(responseMessage);
+ responseMessage = malloc(strlen(response->headers[i]->header) + strlen(response->headers[i]->value) + 5);
+ if(responseMessage == NULL) {
+ if(daemon->options & MHD_USE_DEBUG)
+ fprintf(stderr, "Error allocating memory!\n");
+ pthread_mutex_unlock(&response->mutex);
+ return MHD_NO;
+ }
+ numBytesInMessage = strlen(response->headers[i]->header) + strlen(response->headers[i]->value) + 5;
+ }
+ sprintf(responseMessage, "%s: %s\r\n", response->headers[i]->header, response->headers[i]->value);
+ fprintf(stderr, "%s\n", responseMessage);
+ if(send(session->socket_fd, responseMessage, strlen(responseMessage), 0) != strlen(responseMessage)) {
+ fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n");
+ pthread_mutex_unlock(&response->mutex);
+ return MHD_NO;
+ }
+ }
+
+ response->headersSent = 1;
+ }
+
+ if(response->data != NULL) {
+ if(response->bytesSentSoFar == 0) {
+ if(numBytesInMessage < 32) {
+ free(responseMessage);
+ responseMessage = malloc(32);
+ if(responseMessage == NULL) {
+ if(daemon->options & MHD_USE_DEBUG)
+ fprintf(stderr, "Error allocating memory!\n");
+ pthread_mutex_unlock(&response->mutex);
+ return MHD_NO;
+ }
+ }
+ sprintf(responseMessage, "Content-length: %llu\r\n\r\n", (unsigned long long)response->size);
+ fprintf(stderr, "%s\n", responseMessage);
+ if(send(session->socket_fd, responseMessage, strlen(responseMessage),0)!= strlen(responseMessage)) {
+ fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n");
+ pthread_mutex_unlock(&response->mutex);
+ return MHD_NO;
+ }
+ }
+
+ i = send(session->socket_fd, response->data+response->bytesSentSoFar, response->size-response->bytesSentSoFar,0);
+ response->bytesSentSoFar += i;
+
+ fprintf(stderr, "Sent %i bytes of data\nTotal to send is %llu bytes\n", i, (unsigned long long)response->size);
+
+ if(response->bytesSentSoFar == response->size) {
+ session->currentResponses[session->currentResponse] = NULL;
+ session->currentResponse = (session->currentResponse + 1) % MHD_MAX_RESPONSE;
+ response->currentSession = NULL;
+
+ if(response->freeWhenFinished) {
+ pthread_mutex_unlock(&response->mutex);
+ MHD_destroy_response(response);
+ }
+ /*THIS NEEDS TO BE HANDLED ANOTHER WAY!!! TIMEOUT, ect..., as of now this is the only way to get test case to work
+ * since client never disconnects on their own!
+ */
+ if(session->currentResponses[session->currentResponse] == NULL) {
+ MHD_destroy_session(session);
+ daemon->connections[connection_id] = NULL;
+ return MHD_NO;
+ }
+ }
+ } else {
+ if(response->crc == NULL) {
+ pthread_mutex_unlock(&response->mutex);
+ return MHD_NO;
+ }
+
+ if(response->bytesSentSoFar == 0) {
+ if(send(session->socket_fd, "\r\n", response->size,0) != 2) {
+ fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n");
+ pthread_mutex_unlock(&response->mutex);
+ return MHD_NO;
+ }
+ }
+ memset(buffer, 0, 2048);
+
+ i = response->crc(response->crc_cls, response->bytesSentSoFar, (char *)buffer, 2048);
+
+ if(i == -1) {
+ pthread_mutex_unlock(&response->mutex);
+
+ session->currentResponses[session->currentResponse] = NULL;
+ session->currentResponse = (session->currentResponse + 1) % MHD_MAX_RESPONSE;
+ response->currentSession = NULL;
+
+ if(response->freeWhenFinished) {
+ pthread_mutex_unlock(&response->mutex);
+ MHD_destroy_response(response);
+ }
+ /*THIS NEEDS TO BE HANDLED ANOTHER WAY!!! TIMEOUT, ect..., as of now this is the only way to get test case to work
+ * since client never disconnects on their own!
+ */
+ if(session->currentResponses[session->currentResponse] == NULL) {
+ MHD_destroy_session(session);
+ daemon->connections[connection_id] = NULL;
+ return MHD_NO;
+ }
+
+ } else {
+ i = send(session->socket_fd, buffer, i,0);
+ response->bytesSentSoFar += i;
+ }
+ }
+ pthread_mutex_unlock(&response->mutex);
+ return MHD_YES;
+}
+
+
+
+
+
+
+
+
+
+/**
+ * @return -1 if no data uploaded; otherwise number of bytes
+ * read into buf; 0 for end of transmission
+ * Specification not complete at this time.
+ */
+int
+MHD_read_file_upload(struct MHD_Session * session,
+ void * buf,
+ size_t len) {
+ return -1; /* FIXME: not implemented */
+}
+
+