aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2007-06-12 06:48:11 +0000
committerChristian Grothoff <christian@grothoff.org>2007-06-12 06:48:11 +0000
commit641853be5073b9b2dc5c60fa414271ccbec0c0aa (patch)
tree7da8c1810f45ea5cd07923808ed158662c1aadc6
parentc09343fb06be96edac01bbe48d8a269d898c91fd (diff)
downloadlibmicrohttpd-641853be5073b9b2dc5c60fa414271ccbec0c0aa.tar.gz
libmicrohttpd-641853be5073b9b2dc5c60fa414271ccbec0c0aa.zip
cleaning up the code big time -- incomplete
-rw-r--r--INSTALL69
-rw-r--r--src/daemon/Makefile.am5
-rw-r--r--src/daemon/daemon.c1817
-rw-r--r--src/daemon/internal.h111
-rw-r--r--src/daemon/response.c333
-rw-r--r--src/daemon/response.h39
-rw-r--r--src/daemon/session.c515
-rw-r--r--src/daemon/session.h154
8 files changed, 1583 insertions, 1460 deletions
diff --git a/INSTALL b/INSTALL
index 23e5f25d..54caf7c1 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,16 +1,13 @@
1Installation Instructions 1Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
2************************* 2Foundation, Inc.
3 3
4Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free 4 This file is free documentation; the Free Software Foundation gives
5Software Foundation, Inc.
6
7This file is free documentation; the Free Software Foundation gives
8unlimited permission to copy, distribute and modify it. 5unlimited permission to copy, distribute and modify it.
9 6
10Basic Installation 7Basic Installation
11================== 8==================
12 9
13These are generic installation instructions. 10 These are generic installation instructions.
14 11
15 The `configure' shell script attempts to guess correct values for 12 The `configure' shell script attempts to guess correct values for
16various system-dependent variables used during compilation. It uses 13various system-dependent variables used during compilation. It uses
@@ -70,9 +67,9 @@ The simplest way to compile this package is:
70Compilers and Options 67Compilers and Options
71===================== 68=====================
72 69
73Some systems require unusual options for compilation or linking that the 70 Some systems require unusual options for compilation or linking that
74`configure' script does not know about. Run `./configure --help' for 71the `configure' script does not know about. Run `./configure --help'
75details on some of the pertinent environment variables. 72for details on some of the pertinent environment variables.
76 73
77 You can give `configure' initial values for configuration parameters 74 You can give `configure' initial values for configuration parameters
78by setting variables in the command line or in the environment. Here 75by setting variables in the command line or in the environment. Here
@@ -85,7 +82,7 @@ is an example:
85Compiling For Multiple Architectures 82Compiling For Multiple Architectures
86==================================== 83====================================
87 84
88You can compile the package for more than one kind of computer at the 85 You can compile the package for more than one kind of computer at the
89same time, by placing the object files for each architecture in their 86same time, by placing the object files for each architecture in their
90own directory. To do this, you must use a version of `make' that 87own directory. To do this, you must use a version of `make' that
91supports the `VPATH' variable, such as GNU `make'. `cd' to the 88supports the `VPATH' variable, such as GNU `make'. `cd' to the
@@ -102,19 +99,19 @@ for another architecture.
102Installation Names 99Installation Names
103================== 100==================
104 101
105By default, `make install' installs the package's commands under 102 By default, `make install' will install the package's files in
106`/usr/local/bin', include files under `/usr/local/include', etc. You 103`/usr/local/bin', `/usr/local/man', etc. You can specify an
107can specify an installation prefix other than `/usr/local' by giving 104installation prefix other than `/usr/local' by giving `configure' the
108`configure' the option `--prefix=PREFIX'. 105option `--prefix=PATH'.
109 106
110 You can specify separate installation prefixes for 107 You can specify separate installation prefixes for
111architecture-specific files and architecture-independent files. If you 108architecture-specific files and architecture-independent files. If you
112pass the option `--exec-prefix=PREFIX' to `configure', the package uses 109give `configure' the option `--exec-prefix=PATH', the package will use
113PREFIX as the prefix for installing programs and libraries. 110PATH as the prefix for installing programs and libraries.
114Documentation and other data files still use the regular prefix. 111Documentation and other data files will still use the regular prefix.
115 112
116 In addition, if you use an unusual directory layout you can give 113 In addition, if you use an unusual directory layout you can give
117options like `--bindir=DIR' to specify different values for particular 114options like `--bindir=PATH' to specify different values for particular
118kinds of files. Run `configure --help' for a list of the directories 115kinds of files. Run `configure --help' for a list of the directories
119you can set and what kinds of files go in them. 116you can set and what kinds of files go in them.
120 117
@@ -125,7 +122,7 @@ option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
125Optional Features 122Optional Features
126================= 123=================
127 124
128Some packages pay attention to `--enable-FEATURE' options to 125 Some packages pay attention to `--enable-FEATURE' options to
129`configure', where FEATURE indicates an optional part of the package. 126`configure', where FEATURE indicates an optional part of the package.
130They may also pay attention to `--with-PACKAGE' options, where PACKAGE 127They may also pay attention to `--with-PACKAGE' options, where PACKAGE
131is something like `gnu-as' or `x' (for the X Window System). The 128is something like `gnu-as' or `x' (for the X Window System). The
@@ -140,11 +137,11 @@ you can use the `configure' options `--x-includes=DIR' and
140Specifying the System Type 137Specifying the System Type
141========================== 138==========================
142 139
143There may be some features `configure' cannot figure out automatically, 140 There may be some features `configure' cannot figure out
144but needs to determine by the type of machine the package will run on. 141automatically, but needs to determine by the type of machine the package
145Usually, assuming the package is built to be run on the _same_ 142will run on. Usually, assuming the package is built to be run on the
146architectures, `configure' can figure that out, but if it prints a 143_same_ architectures, `configure' can figure that out, but if it prints
147message saying it cannot guess the machine type, give it the 144a message saying it cannot guess the machine type, give it the
148`--build=TYPE' option. TYPE can either be a short name for the system 145`--build=TYPE' option. TYPE can either be a short name for the system
149type, such as `sun4', or a canonical name which has the form: 146type, such as `sun4', or a canonical name which has the form:
150 147
@@ -159,7 +156,7 @@ where SYSTEM can have one of these forms:
159need to know the machine type. 156need to know the machine type.
160 157
161 If you are _building_ compiler tools for cross-compiling, you should 158 If you are _building_ compiler tools for cross-compiling, you should
162use the option `--target=TYPE' to select the type of system they will 159use the `--target=TYPE' option to select the type of system they will
163produce code for. 160produce code for.
164 161
165 If you want to _use_ a cross compiler, that generates code for a 162 If you want to _use_ a cross compiler, that generates code for a
@@ -170,9 +167,9 @@ eventually be run) with `--host=TYPE'.
170Sharing Defaults 167Sharing Defaults
171================ 168================
172 169
173If you want to set default values for `configure' scripts to share, you 170 If you want to set default values for `configure' scripts to share,
174can create a site shell script called `config.site' that gives default 171you can create a site shell script called `config.site' that gives
175values for variables like `CC', `cache_file', and `prefix'. 172default values for variables like `CC', `cache_file', and `prefix'.
176`configure' looks for `PREFIX/share/config.site' if it exists, then 173`configure' looks for `PREFIX/share/config.site' if it exists, then
177`PREFIX/etc/config.site' if it exists. Or, you can set the 174`PREFIX/etc/config.site' if it exists. Or, you can set the
178`CONFIG_SITE' environment variable to the location of the site script. 175`CONFIG_SITE' environment variable to the location of the site script.
@@ -181,7 +178,7 @@ A warning: not all `configure' scripts look for a site script.
181Defining Variables 178Defining Variables
182================== 179==================
183 180
184Variables not defined in a site shell script can be set in the 181 Variables not defined in a site shell script can be set in the
185environment passed to `configure'. However, some packages may run 182environment passed to `configure'. However, some packages may run
186configure again during the build, and the customized values of these 183configure again during the build, and the customized values of these
187variables may be lost. In order to avoid this problem, you should set 184variables may be lost. In order to avoid this problem, you should set
@@ -189,18 +186,14 @@ them in the `configure' command line, using `VAR=value'. For example:
189 186
190 ./configure CC=/usr/local2/bin/gcc 187 ./configure CC=/usr/local2/bin/gcc
191 188
192causes the specified `gcc' to be used as the C compiler (unless it is 189will cause the specified gcc to be used as the C compiler (unless it is
193overridden in the site shell script). Here is a another example: 190overridden in the site shell script).
194
195 /bin/bash ./configure CONFIG_SHELL=/bin/bash
196
197Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
198configuration-related scripts to be executed by `/bin/bash'.
199 191
200`configure' Invocation 192`configure' Invocation
201====================== 193======================
202 194
203`configure' recognizes the following options to control how it operates. 195 `configure' recognizes the following options to control how it
196operates.
204 197
205`--help' 198`--help'
206`-h' 199`-h'
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
index fb9b098e..892d0506 100644
--- a/src/daemon/Makefile.am
+++ b/src/daemon/Makefile.am
@@ -10,7 +10,10 @@ libmicrohttpd_la_LDFLAGS = \
10# libmicrohttpd_la_LIBADD = \ 10# libmicrohttpd_la_LIBADD = \
11# -lm 11# -lm
12libmicrohttpd_la_SOURCES = \ 12libmicrohttpd_la_SOURCES = \
13 daemon.c 13 daemon.c \
14 internal.h \
15 response.c response.h \
16 session.c session.h
14 17
15# No curl, not testcases 18# No curl, not testcases
16if HAVE_CURL 19if HAVE_CURL
diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c
index dd22ce6d..8a602df4 100644
--- a/src/daemon/daemon.c
+++ b/src/daemon/daemon.c
@@ -20,544 +20,123 @@
20 20
21/** 21/**
22 * @file daemon.c 22 * @file daemon.c
23 * @brief This is my implementation of the libmicrohttpd interface. Many features incomplete at this time. 23 * @brief A minimal-HTTP server library
24 * @author Daniel Pittman 24 * @author Daniel Pittman
25 * @author Christian Grothoff
25 * @version 0.1.0 26 * @version 0.1.0
26 */ 27 */
27 28
28#include "config.h"
29#include "microhttpd.h"
30
31#include <netinet/in.h>
32#include <stdio.h> 29#include <stdio.h>
33#include <stdlib.h> 30#include <stdlib.h>
34#include <netdb.h> 31#include <netdb.h>
35#include <string.h> 32#include <string.h>
36#include <unistd.h> 33#include <unistd.h>
34#include <stdarg>
37#include <fcntl.h> 35#include <fcntl.h>
38#include <pthread.h> 36#include <pthread.h>
37#include <netinet/in.h>
39 38
39#include "microhttpd.h"
40#include "internal.h"
41#include "response.h"
42#include "session.h"
43#include "config.h"
40 44
41#define MHD_MAX_CONNECTIONS FD_SETSIZE -4 45#define MHD_MAX_CONNECTIONS FD_SETSIZE -4
42#define MHD_MAX_BUF_SIZE 2048
43#define MHD_MAX_HEADERS 1024
44#define MHD_MAX_HANDLERS 1024
45#define MHD_MAX_RESPONSE 1024
46
47
48int MHD_handle_read(int, struct MHD_Daemon *);
49int MHD_handle_write(int, struct MHD_Daemon *);
50void * MHD_spawn_connections(void * data);
51void * MHD_select(void * data);
52int MHD_parse_message(struct MHD_Session * session);
53void MHD_parse_URL(struct MHD_Session * session);
54
55struct MHD_Daemon {
56 unsigned int options;
57
58 unsigned short port;
59 int socket_fd;
60 int max_fd;
61
62 MHD_AcceptPolicyCallback apc;
63 void * apc_cls;
64
65 fd_set read_fd_set;
66 fd_set write_fd_set;
67 fd_set except_fd_set;
68
69 int shutdown;
70 pthread_t pid;
71
72 struct MHD_Session * connections[MHD_MAX_CONNECTIONS];
73
74 int firstFreeHandler;
75 struct MHD_Access_Handler * handlers[MHD_MAX_HANDLERS];
76 MHD_AccessHandlerCallback dh;
77 void * dh_cls;
78};
79
80struct MHD_Session {
81 struct sockaddr_in addr;
82
83 int readLoc;
84 int writeLoc;
85
86 int id;
87 int socket_fd;
88 pthread_t pid;
89
90 struct MHD_Daemon * daemon;
91
92 int bufPos;
93 int messagePos;
94 char inbuf[MHD_MAX_BUF_SIZE];
95
96 int firstFreeHeader;
97 char * requestType;
98 char * documentName;
99 struct MHD_HTTP_Header * headers[MHD_MAX_HEADERS];
100 46
101 unsigned short responsePending; 47#define MHD_MAX_BUF_SIZE 2048
102 int currentResponse;
103 struct MHD_Response * currentResponses[MHD_MAX_RESPONSE];
104};
105
106
107struct MHD_Response {
108 pthread_mutex_t mutex;
109
110 unsigned int responseCode;
111
112 int freeWhenFinished;
113
114 unsigned int headersSent;
115
116 size_t size;
117 void * data;
118 int bytesSentSoFar;
119
120 int must_free;
121 MHD_ContentReaderCallback crc;
122
123 void * crc_cls;
124 MHD_ContentReaderFreeCallback crfc;
125
126 int firstFreeHeader;
127 struct MHD_HTTP_Header * headers[MHD_MAX_HEADERS];
128
129 struct MHD_Session * currentSession;
130};
131
132struct MHD_Access_Handler {
133 char * uri_prefix;
134 MHD_AccessHandlerCallback dh;
135 void * dh_cls;
136};
137 48
138struct MHD_HTTP_Header {
139 char * header;
140 char * headerContent;
141 enum MHD_ValueKind kind;
142};
143 49
144/** 50/**
145 * Add a header line to the response. 51 * fprintf-like helper function for logging debug
146 * 52 * messages.
147 * @return MHD_NO on error (i.e. invalid header or content format).
148 */ 53 */
149int 54static void DLOG(const struct MHD_Daemon * daemon,
150MHD_add_response_header(struct MHD_Response * response, 55 const char * format,
151 const char * header, 56 ...) {
152 const char * content) { 57 va_list va;
153 //Note that as of this time this function will also return 58
154 //an error if the maximum number of headers allowed is exceeded. 59 if ( (daemon->options & MHD_USE_DEBUG) == 0)
155 60 return;
156 char * saveptr; 61 va_start(va, format);
157 char * newHeader; 62 vfprintf(stderr, format, va);
158 char * newContent; 63 va_end(va);
159 int i;
160
161 if(response == NULL || header == NULL || content == NULL || strlen(header) == 0 || strlen(content) == 0) {
162 return MHD_NO;
163 }
164 /* CG: use linked list to avoid limitation and over-allocation! */
165 if(response->firstFreeHeader >= MHD_MAX_HEADERS) {
166 return MHD_NO;
167 }
168
169 newHeader = (char *)malloc(strlen(header)+1);
170 newContent = (char *)malloc(strlen(content)+1);
171 /* CG: useless check! */
172 if(newHeader == NULL || newContent == NULL) {
173 /* CG: printf! */
174 fprintf(stderr, "Error allocating memory!\n");
175 return MHD_NO;
176 }
177
178 /* CG: do you mean strcpy/strdup? defer allocation
179 until you need to (after malformed checks!) */
180 sprintf(newHeader, "%s", header);
181 sprintf(newContent, "%s", content);
182
183 /* CG: why not use strstr? */
184 if(strtok_r(newHeader, " \t\r\n", &saveptr) != NULL) {
185 fprintf(stderr, "Malformed header!\n");
186 free(newContent);
187 free(newHeader);
188 return MHD_NO;
189 }
190
191 /* CG: why not use strstr? */
192 if(strtok_r(newContent, "\n", &saveptr) != NULL) {
193 fprintf(stderr, "Malformed content!\n");
194 free(newContent);
195 free(newHeader);
196 return MHD_NO;
197 }
198
199 /* CG: this is not C++ -- no need to cast after malloc! */
200 struct MHD_HTTP_Header * newHTTPHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header));
201
202 if(newHTTPHeader == NULL) {
203 /* CG: useless check, printf */
204 fprintf(stderr, "Error allocating memory!\n");
205 free(newContent);
206 free(newHeader);
207 return MHD_NO;
208 }
209
210 /* CG: strdup here, avoids free's above! */
211 response->headers[response->firstFreeHeader]->header = newHeader;
212 response->headers[response->firstFreeHeader]->headerContent = newContent;
213
214 //For now, everything is a HTTP Header... this needs to be improved!
215 /* CG: what else are you thinking about? Cookies?
216 sounds like you are proposing an API change!!! */
217
218 response->headers[response->firstFreeHeader]->kind = MHD_HEADER_KIND;
219
220 /* CG: YUCK! Yet another reason for linked lists...
221 Why bother with the firstFreeHandler field if
222 you're O(n) anyway!? */
223 response->firstFreeHeader=MHD_MAX_HEADERS;
224 for(i = 0; i < MHD_MAX_HEADERS; i++) {
225 if(response->headers[i] == NULL) {
226 response->firstFreeHeader = i;
227 break;
228 }
229 }
230
231 return MHD_YES;
232} 64}
233 65
234/**
235 * This function accepts an incoming connection
236 * and creates the MHD_Session object for it.
237 * It also enforces policy by way of calling the accept policy callback
238 */
239int
240MHD_create_connection(struct MHD_Daemon * daemon) {
241 int i, first_free, size;
242
243 if(daemon == NULL)
244 return -1;
245
246 first_free = -1;
247 for(i = 0; i < MHD_MAX_CONNECTIONS; i++) {
248 if(daemon->connections[i] == NULL) {
249 first_free = i;
250 break;
251 }
252 }
253
254 if(first_free == -1)
255 return -1;
256
257 /* CG: delay allocation until at accept has succeeded! */
258 daemon->connections[first_free] = (struct MHD_Session *)malloc(sizeof(struct MHD_Session));
259
260 if(daemon->connections[first_free] == NULL) {
261 /* CG: use MACRO or (static) helper function
262 instead of writing this option check everywhere! */
263 if((daemon->options & MHD_USE_DEBUG) != 0)
264 fprintf(stderr, "Error allocating memory!\n");
265 return -1;
266 }
267
268 size = sizeof(struct sockaddr);
269 daemon->connections[first_free]->socket_fd =
270
271 accept(daemon->socket_fd, (struct sockaddr *)&daemon->connections[first_free]->addr,
272 (socklen_t *)&size);
273
274 if(daemon->connections[first_free]->socket_fd == -1) {
275 free(daemon->connections[first_free]);
276 daemon->connections[first_free] = NULL;
277 if((daemon->options & MHD_USE_DEBUG) != 0) {
278 fprintf(stderr, "Error accepting incoming connections!\n");
279 }
280 return -1;
281 }
282
283 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) {
284 close(daemon->connections[first_free]->socket_fd);
285 free(daemon->connections[first_free]);
286 daemon->connections[first_free] = NULL;
287 if((daemon->options & MHD_USE_DEBUG) != 0) {
288 fprintf(stderr, "Connection denied based on accept policy callback!\n");
289 }
290 return -1;
291 }
292
293 daemon->connections[first_free]->id = first_free;
294 daemon->connections[first_free]->daemon = daemon;
295 daemon->connections[first_free]->pid = (pthread_t)-1;
296 daemon->connections[first_free]->bufPos = 0;
297 daemon->connections[first_free]->messagePos= 0;
298 daemon->connections[first_free]->responsePending = 0;
299 memset(daemon->connections[first_free]->inbuf, '\0', MHD_MAX_BUF_SIZE);
300 daemon->connections[first_free]->currentResponse = 0;
301 daemon->connections[first_free]->firstFreeHeader = 0;
302
303 for(i = 0; i < MHD_MAX_HEADERS; i++) {
304 daemon->connections[first_free]->headers[i] = NULL;
305 }
306
307 for(i = 0; i < MHD_MAX_RESPONSE; i++) {
308 daemon->connections[first_free]->currentResponses[i] = NULL;
309 }
310 /* CG: maybe better to re-compute max_fd closer to select;
311 also handles deletion mo re graceful, need to iterate over
312 all connections anyway for FD_SET... */
313 if(daemon->max_fd < daemon->connections[first_free]->socket_fd) {
314 daemon->max_fd = daemon->connections[first_free]->socket_fd;
315 }
316
317 return first_free;
318}
319 66
320/** 67/**
321 * Create a response object. The response object can be extended with 68 * Register an access handler for all URIs beginning with uri_prefix.
322 * header information and then be used any number of times.
323 * 69 *
324 * @param size size of the data portion of the response, -1 for unknown 70 * @param uri_prefix
325 * @param crc callback to use to obtain response data 71 * @return MRI_NO if a handler for this exact prefix
326 * @param crc_cls extra argument to crc 72 * already exists
327 * @param crfc callback to call to free crc_cls resources
328 * @return NULL on error (i.e. invalid arguments, out of memory)
329 */ 73 */
330struct MHD_Response * 74int
331MHD_create_response_from_callback(size_t size, 75MHD_register_handler(struct MHD_Daemon * daemon,
332 MHD_ContentReaderCallback crc, 76 const char * uri_prefix,
333 void * crc_cls, 77 MHD_AccessHandlerCallback dh,
334 MHD_ContentReaderFreeCallback crfc) { 78 void * dh_cls) {
335 79 struct MHD_Access_Handler * ah;
336 struct MHD_Response * retVal; 80
337 int i; 81 if ( (daemon == NULL) ||
338 82 (uri_prefix == NULL) ||
339 83 (dh == NULL) )
340 if(crc == NULL) { 84 return MHD_NO;
341 /* CG: printf! */ 85 ah = daemon->handlers;
342 fprintf(stderr, "A ContentReaderCallback must be provided to MHD_create_response_from_callback!\n"); 86 while (ah != NULL) {
343 return NULL; 87 if (0 == strcmp(uri_prefix,
344 } 88 ah->uri_prefix))
345 89 return MHD_NO;
346 retVal = (struct MHD_Response *) malloc(sizeof(struct MHD_Response)); 90 ah = ah->next;
347 if(retVal == NULL) { 91 }
348 /* CG: printf, useless check, useless cast */ 92 ah = malloc(sizeof(MHD_AccessHandlerCallback));
349 fprintf(stderr, "Error allocating memory!\n"); 93 ah->next = daemon->handlers;
350 return NULL; 94 ah->uri_prefix = strdup(uri_prefix);
351 } 95 ah->dh = dh;
352 96 ah->dh_cls = dh_cls;
353 retVal->size = size; 97 daemon->handlers = ah;
354 98 return MHD_YES;
355 retVal->crc = crc;
356 retVal->crc_cls = crc_cls;
357
358 retVal->crfc = crfc;
359
360 retVal->firstFreeHeader = 0;
361
362 retVal->responseCode = 0;
363
364 retVal->headersSent = 0;
365
366 retVal->bytesSentSoFar = 0;
367
368 retVal->freeWhenFinished = 0;
369 retVal->currentSession = NULL;
370
371 if(pthread_mutex_init(&retVal->mutex, NULL) != 0) {
372 fprintf(stderr, "Error initializing mutex!\n");
373 free(retVal);
374 return NULL;
375 }
376 /* CG: use memset? linked list!!? */
377 for(i = 0; i < MHD_MAX_HEADERS; i++) {
378 retVal->headers[i] = NULL;
379 }
380
381 retVal->data = NULL;
382 retVal->must_free = 0;
383
384 return retVal;
385} 99}
386 100
387/**
388 * Create a response object. The response object can be extended with
389 * header information and then be used any number of times.
390 *
391 * @param size size of the data portion of the response
392 * @param data the data itself
393 * @param must_free libmicrohttpd should free data when done
394 * @param must_copy libmicrohttpd must make a copy of data
395 * right away, the data maybe released anytime after
396 * this call returns
397 * @return NULL on error (i.e. invalid arguments, out of memory)
398 */
399struct MHD_Response *
400MHD_create_response_from_data(size_t size,
401 void * data,
402 int must_free,
403 int must_copy) {
404
405 struct MHD_Response * retVal;
406 int i;
407
408
409 if(data == NULL) {
410 fprintf(stderr, "data must be provided to MHD_Create_response_from_data!\n");
411 return NULL;
412 }
413
414 retVal = (struct MHD_Response *) malloc(sizeof(struct MHD_Response));
415 if(retVal == NULL) {
416 fprintf(stderr, "Error allocating memory!\n");
417 return NULL;
418 }
419
420 retVal->size = size;
421
422 retVal->crc = NULL;
423 retVal->crc_cls = NULL;
424 retVal->crfc = NULL;
425
426 retVal->responseCode = 0;
427
428 retVal->firstFreeHeader = 0;
429 retVal->freeWhenFinished = 0;
430 retVal->currentSession = NULL;
431
432 retVal->headersSent = 0;
433
434 retVal->bytesSentSoFar = 0;
435
436 for(i = 0; i < MHD_MAX_HEADERS; i++) {
437 retVal->headers[i] = NULL;
438 }
439
440 if(pthread_mutex_init(&retVal->mutex, NULL) != 0) {
441 fprintf(stderr, "Error initializing mutex!\n");
442 free(retVal);
443 return NULL;
444 }
445
446 if(must_copy) {
447 retVal->data = malloc(size);
448 if(retVal->data == NULL) {
449 fprintf(stderr, "Error allocating memory!\n");
450 free(retVal);
451 return NULL;
452 }
453 memcpy(retVal->data, data, size);
454 retVal->must_free = 1;
455 } else {
456 retVal->data = data;
457 retVal->must_free = must_free;
458 }
459
460 return retVal;
461}
462 101
463/** 102/**
464 * Delete a header line from the response. 103 * Unregister an access handler for the URIs beginning with
104 * uri_prefix.
465 * 105 *
466 * @return MHD_NO on error (no such header known) 106 * @param uri_prefix
467 */ 107 * @return MHD_NO if a handler for this exact prefix
468int 108 * is not known for this daemon
469MHD_del_response_header(struct MHD_Response * response,
470 const char * header,
471 const char * content) {
472 int i;
473
474 if(header == NULL || content == NULL) {
475 return MHD_NO;
476 }
477
478 for(i = 0; i < MHD_MAX_HEADERS; i++) {
479 if(response->headers[i] != NULL &&
480 strncmp(header, response->headers[i]->header, strlen(header)) == 0 &&
481 strncmp(content, response->headers[i]->headerContent, strlen(header)) == 0) {
482 free(response->headers[i]->header);
483 free(response->headers[i]->headerContent);
484 free(response->headers[i]);
485 response->headers[i] = NULL;
486 return MHD_YES;
487 }
488 }
489 return MHD_NO;
490}
491
492/**
493 * Destroy a response object and associated resources. Note that
494 * libmicrohttpd may keep some of the resources around if the response
495 * is still in the queue for some clients, so the memory may not
496 * necessarily be freed immediatley.
497 */
498void
499MHD_destroy_response(struct MHD_Response * response) {
500 int i;
501
502 if(response == NULL) {
503 return;
504 }
505
506 pthread_mutex_lock(&response->mutex);
507
508 if(response->currentSession != NULL) {
509 response->freeWhenFinished = 1;
510 pthread_mutex_unlock(&response->mutex);
511 return;
512 }
513
514 if(response->must_free && response->data != NULL) {
515 free(response->data);
516 }
517
518 if(response->crfc != NULL && response->crc_cls != NULL) {
519 response->crfc(response->crc_cls);
520 }
521
522 for(i = 0; i < MHD_MAX_HEADERS; i++) {
523 if(response->headers[i] == NULL)
524 continue;
525
526 free(response->headers[i]->header);
527 free(response->headers[i]->headerContent);
528 free(response->headers[i]);
529 }
530
531 pthread_mutex_unlock(&response->mutex);
532 pthread_mutex_destroy(&response->mutex);
533
534 free(response);
535}
536
537/**
538 * Thi function is similar to destroy_response except
539 * that it was created to destroy the session object.
540 */ 109 */
541void 110int
542MHD_destroy_session(struct MHD_Session * session) { 111MHD_unregister_handler(struct MHD_Daemon * daemon,
543 int i; 112 const char * uri_prefix,
544 113 MHD_AccessHandlerCallback dh,
545 for(i = 0; i < MHD_MAX_HEADERS; i++) { 114 void * dh_cls) {
546 if(session->headers[i] != NULL) { 115 struct MHD_Access_Handler * prev;
547 free(session->headers[i]); 116 struct MHD_Access_Handler * pos;
548 } 117
549 } 118 if ( (daemon == NULL) ||
550 119 (uri_prefix == NULL) ||
551 for(i = 0; i < MHD_MAX_RESPONSE; i++) { 120 (dh == NULL) )
552 if(session->currentResponses[i] != NULL) { 121 return MHD_NO;
553 pthread_mutex_lock(&session->currentResponses[i]->mutex); 122 pos = daemon->handlers;
554 session->currentResponses[i]->currentSession = NULL; 123 prev = NULL;
555 pthread_mutex_unlock(&session->currentResponses[i]->mutex); 124 while (pos != NULL) {
556 } 125 if ( (dh == ah->dh) &&
557 } 126 (dh_cls == ah->dh_cls) &&
558 127 (0 == strcmp(uri_prefix,
559 close(session->socket_fd); 128 ah->uri_prefix)) ) {
560 free(session); 129 if (prev == NULL)
130 daemon->handlers = pos->next;
131 else
132 prev->next = pos->next;
133 free(pos);
134 return MHD_YES;
135 }
136 prev = pos;
137 pos = pos->next;
138 }
139 return MHD_NO;
561} 140}
562 141
563/** 142/**
@@ -573,576 +152,233 @@ MHD_get_fdset(struct MHD_Daemon * daemon,
573 fd_set * write_fd_set, 152 fd_set * write_fd_set,
574 fd_set * except_fd_set, 153 fd_set * except_fd_set,
575 int * max_fd) { 154 int * max_fd) {
576 155 struct MHD_Session * pos;
577 int i; 156
578 157 if ( (daemon == NULL) ||
579 if(daemon == NULL || read_fd_set == NULL || write_fd_set == NULL || except_fd_set == NULL || max_fd == NULL) { 158 (read_fd_set == NULL) ||
580 return MHD_NO; 159 (write_fd_set == NULL) ||
581 } 160 (except_fd_set == NULL) ||
582 161 (max_fd == NULL) ||
583 if((daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0) { 162 ( (daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0) )
584 return MHD_NO; 163 return MHD_NO;
585 } 164 FD_SET(daemon->socket_fd,
586 165 &daemon->read_fd_set);
587 FD_ZERO(read_fd_set); 166 if ( (*max_fd) < daemon->socket_fd)
588 FD_ZERO(write_fd_set); 167 *max_fd = daemon->socket_fd;
589 FD_ZERO(except_fd_set); 168 pos = daemon->session;
590 169 while (pos != NULL) {
591 FD_SET(daemon->socket_fd, &daemon->read_fd_set); 170 if (MHD_YES != MHD_session_get_fdset(pos,
592 for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { 171 read_fd_set,
593 if(daemon->connections[i] != NULL) { 172 write_fd_set,
594 FD_SET(daemon->connections[i]->socket_fd, read_fd_set); 173 except_fd_set,
595 FD_SET(daemon->connections[i]->socket_fd, write_fd_set); 174 max_fd))
596 } 175 return MHD_NO;
597 } 176 pos = pos->next;
598 177 }
599 *max_fd = daemon->max_fd; 178 return MHD_YES;
600
601 return MHD_YES;
602}
603
604/**
605 * Get all of the headers added to a response.
606 *
607 * @param iterator callback to call on each header;
608 * maybe NULL (then just count headers)
609 * @param iterator_cls extra argument to iterator
610 * @return number of entries iterated over
611 */
612int
613MHD_get_response_headers(struct MHD_Response * response,
614 MHD_KeyValueIterator * iterator,
615 void * iterator_cls) {
616 int i, numHeaders;
617
618 if(response == NULL) {
619 return -1;
620 }
621
622 numHeaders = 0;
623 for(i = 0; i < MHD_MAX_HEADERS; i++) {
624 if(response->headers[i] != NULL) {
625 if(iterator != NULL) {
626 (*iterator)(iterator_cls, response->headers[i]->kind, response->headers[i]->header, response->headers[i]->headerContent);
627 }
628 numHeaders++;
629 }
630 }
631 return numHeaders;
632} 179}
633 180
634/**
635 * Get all of the headers from the request.
636 *
637 * @param iterator callback to call on each header;
638 * maybe NULL (then just count headers)
639 * @param iterator_cls extra argument to iterator
640 * @return number of entries iterated over
641 */
642int
643MHD_get_session_values(struct MHD_Session * session,
644 enum MHD_ValueKind kind,
645 MHD_KeyValueIterator * iterator,
646 void * iterator_cls) {
647 int i, numHeaders;
648
649 if(session == NULL) {
650 return -1;
651 }
652 numHeaders = 0;
653 for(i = 0; i < MHD_MAX_HEADERS; i++) {
654 if(session->headers[i] != NULL && session->headers[i]->kind == kind) {
655 if(iterator != NULL) {
656 (*iterator)(iterator_cls, session->headers[i]->kind, session->headers[i]->header, session->headers[i]->headerContent);
657 }
658 numHeaders++;
659 }
660 }
661 return numHeaders;
662}
663 181
664/** 182/**
665 * This function is intented to be called in the case of 183 * Main function of the thread that handles an individual
666 * multithreaded connections. A thread will be spawned calling this 184 * connection.
667 * function with a particular connection, and the thread will poll the connection
668 * (this should be improved) until there is something to do
669 */ 185 */
670void * 186static void *
671MHD_handle_connection(void * data) { 187MHD_handle_connection(void * data) {
672 struct MHD_Session * con; 188 struct MHD_Session * con = data;
673 int num_ready; 189 int num_ready;
674 struct timeval timeout; 190 fd_set rs;
675 fd_set read; 191 fd_set ws;
676 fd_set write; 192 fd_set es;
677 193 int max;
678 con = data; 194
679 195 if (con == NULL)
680 if(con == NULL) 196 abort();
681 return NULL; 197 while (! con->daemon->shutdown) {
682 198 FD_ZERO(&rs);
683 do { 199 FD_ZERO(&ws);
684 200 FD_ZERO(&es);
685 FD_ZERO(&read); 201 max = 0;
686 FD_ZERO(&write); 202 MHD_session_get_fdset(con,
687 203 &rs,
688 FD_SET(con->socket_fd, &read); 204 &ws,
689 FD_SET(con->socket_fd, &write); 205 &es,
690 206 &max);
691 timeout.tv_sec = 0; 207 num_ready = select(max + 1,
692 timeout.tv_usec = 0; 208 &rs,
693 209 &ws,
694 num_ready = select(con->socket_fd + 1, 210 &es,
695 &read, &write, NULL, &timeout); 211 NULL);
696 212 if (num_ready <= 0) {
697 if(num_ready > 0) { 213 if (errno == EINTR)
698 if(FD_ISSET(con->socket_fd, &read)) { 214 continue;
699 if(MHD_handle_read(con->id, con->daemon) == MHD_NO) { 215 break;
700 pthread_detach(pthread_self()); 216 }
701 return NULL; 217 if ( ( (FD_ISSET(con->socket_fd, &rs)) &&
702 } 218 (MHD_YES != MHD_session_handle_read(con)) ) ||
703 } 219 ( (FD_ISSET(con->socket_fd, &ws)) &&
704 if (FD_ISSET(con->socket_fd, &write)) { 220 (MHD_YES != MHD_session_handle_write(con)) ) )
705 if(MHD_handle_write(con->id, con->daemon) == MHD_NO) { 221 break;
706 pthread_detach(pthread_self()); 222 }
707 return NULL; 223 close(con->socket_fd);
708 } 224 con->socket_fd = -1;
709 } 225 return NULL;
710 }
711 } while (!con->daemon->shutdown);
712
713 return NULL;
714}
715
716/**
717 * This function is created to handle the except file descriptor
718 * set, but it is doubtfull that it will ever be used.
719 */
720void
721MHD_handle_except(int connection_id, struct MHD_Daemon * daemon) {
722 //It is unlikely that this function will ever need to be implemented.
723} 226}
724 227
725/**
726 * This function handles a particular connection when it has been
727 * determined that there is data to be read off a socket. All implementations
728 * (multithreaded, external select, internal select) call this function
729 * to handle reads.
730 */
731int
732MHD_handle_read(int connection_id, struct MHD_Daemon * daemon) {
733 int bytes_read,i;
734
735 if((daemon->options & MHD_USE_DEBUG) != 0) {
736 fprintf(stderr, "Enter MHD_handle_read\n");
737 }
738
739 if(daemon == NULL || daemon->connections[connection_id]==NULL) {
740 return MHD_NO;
741 }
742
743 if(daemon->connections[connection_id]->responsePending) {
744 return MHD_YES;
745 }
746
747 daemon->connections[connection_id]->firstFreeHeader = 0;
748 daemon->connections[connection_id]->requestType = NULL;
749
750 for(i = 0; i < MHD_MAX_HEADERS; i++) {
751 daemon->connections[connection_id]->headers[i] = NULL;
752 }
753
754
755
756 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);
757
758 memset(daemon->connections[connection_id]->inbuf + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos,
759 0, MHD_MAX_BUF_SIZE - daemon->connections[connection_id]->bufPos + (daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos));
760
761 bytes_read = recv(daemon->connections[connection_id]->socket_fd,
762 daemon->connections[connection_id]->inbuf + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos,
763 MHD_MAX_BUF_SIZE - (daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos), 0);
764
765 daemon->connections[connection_id]->bufPos = bytes_read + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos;
766
767 if(bytes_read == 0) {
768 MHD_destroy_session(daemon->connections[connection_id]);
769 daemon->connections[connection_id] = NULL;
770 return MHD_NO;
771 } else {
772 fprintf(stderr, "\"%s\"\n", daemon->connections[connection_id]->inbuf);
773 i = MHD_parse_message(daemon->connections[connection_id]);
774 if(i == -1) {
775 daemon->connections[connection_id]->messagePos = daemon->connections[connection_id]->bufPos;
776 return MHD_YES;
777 } else {
778 daemon->connections[connection_id]->messagePos = i;
779 fprintf(stderr, "Number of bytes in header: %i\n", daemon->connections[connection_id]->messagePos);
780 }
781
782 daemon->connections[connection_id]->responsePending = 1;
783
784 MHD_parse_URL(daemon->connections[connection_id]);
785
786 for(i = 0; i < MHD_MAX_HANDLERS; i++) {
787 if(daemon->handlers[i] == NULL)
788 continue;
789
790 //header 0 will hold the url of the request
791 if(strstr(daemon->connections[connection_id]->headers[0]->headerContent, daemon->handlers[i]->uri_prefix) != NULL){
792 return daemon->handlers[i]->dh(daemon->handlers[i]->dh_cls, daemon->connections[connection_id],
793 daemon->connections[connection_id]->documentName, daemon->connections[connection_id]->requestType, NULL, NULL);
794 }
795 }
796 return daemon->dh(daemon->dh_cls, daemon->connections[connection_id],
797 daemon->connections[connection_id]->documentName, daemon->connections[connection_id]->requestType, NULL, NULL);
798 }
799
800 return MHD_YES;
801}
802 228
803/** 229/**
804 * This function was created to handle writes to sockets when it has been 230 * Accept an incoming connection and create the MHD_Session object for
805 * determined that the socket can be written to. If there is no data 231 * it. This function also enforces policy by way of checking with the
806 * to be written, however, the function call does nothing. All implementations 232 * accept policy callback.
807 * (multithreaded, external select, internal select) call this function
808 */ 233 */
809int 234static int
810MHD_handle_write(int connection_id, struct MHD_Daemon * daemon) { 235MHD_accept_connection(struct MHD_Daemon * daemon) {
811 struct MHD_Session * session; 236 struct MHD_Session * session;
812 237 struct sockaddr addr;
813 struct MHD_Response * response; 238 socklen_t addrlen;
814 239 int s;
815 int i; 240
816 241 addrlen = sizeof(struct sockaddr);
817 char * buffer[2048]; 242 memset(&addr,
818 243 0,
819 char * responseMessage; 244 sizeof(struct sockaddr));
820 int numBytesInMessage; 245 if ( (0 != (s = accept(daemon->socket_fd,
821 246 &addr,
822 if((daemon->options & MHD_USE_DEBUG) != 0) { 247 &addrlen))) ||
823 fprintf(stderr, "Enter MHD_handle_write\n"); 248 (addrlen <= 0) ) {
824 } 249 DLOG(daemon,
825 250 "Error accepting connection: %s\n",
826 251 strerror(errno));
827 session = daemon->connections[connection_id]; 252 return MHD_NO;
828 253 }
829 response = session->currentResponses[session->currentResponse]; 254 if (MHD_NO == daemon->apc(mhd->apc_cls,
830 255 &addr,
831 numBytesInMessage = 25; 256 addrlen)) {
832 257 close(s);
833 responseMessage = malloc(25); 258 return MHD_YES;
834 if(responseMessage == NULL) { 259 }
835 if(daemon->options & MHD_USE_DEBUG) 260 session = malloc(sizeof(struct MHD_Session));
836 fprintf(stderr, "Error allocating memory!\n"); 261 memset(session,
837 return MHD_NO; 262 0,
838 } 263 sizeof(struct MHD_Session));
839 264 session->addr = malloc(addrlen);
840 if(response == NULL) 265 memcpy(session->addr,
841 return MHD_NO; 266 &addr,
842 267 addrlen);
843 pthread_mutex_lock(&response->mutex); 268 session->addr_len = addrlen;
844 269 session->socket_fd = s;
845 if(!response->headersSent) { 270 session->daemon = daemon;
846 sprintf(responseMessage, "HTTP/1.1 %i Go to hell!\r\n", response->responseCode); 271 if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION) ) &&
847 fprintf(stderr, "%s\n", responseMessage); 272 (0 != pthread_create(&session->pid,
848 if(send(session->socket_fd, responseMessage, strlen(responseMessage), 0) != strlen(responseMessage)) { 273 NULL,
849 fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n"); 274 &MHD_handle_connection,
850 pthread_mutex_unlock(&response->mutex); 275 session)) ) {
851 return MHD_NO; 276 DLOG(daemon,
852 } 277 "Failed to create a thread: %s\n",
853 278 strerror(errno));
854 for(i = 0; i < MHD_MAX_HEADERS; i++) { 279 free(session->addr);
855 if(response->headers[i] == NULL) 280 close(s);
856 continue; 281 free(session);
857 282 return MHD_NO;
858 if(strlen(response->headers[i]->header) + strlen(response->headers[i]->headerContent) + 5 > numBytesInMessage) { 283 }
859 free(responseMessage); 284 session->next = daemon->connections;
860 responseMessage = malloc(strlen(response->headers[i]->header) + strlen(response->headers[i]->headerContent) + 5); 285 daemon->connections = session;
861 if(responseMessage == NULL) { 286 return MHD_YES;
862 if(daemon->options & MHD_USE_DEBUG)
863 fprintf(stderr, "Error allocating memory!\n");
864 pthread_mutex_unlock(&response->mutex);
865 return MHD_NO;
866 }
867 numBytesInMessage = strlen(response->headers[i]->header) + strlen(response->headers[i]->headerContent) + 5;
868 }
869 sprintf(responseMessage, "%s: %s\r\n", response->headers[i]->header, response->headers[i]->headerContent);
870 fprintf(stderr, "%s\n", responseMessage);
871 if(send(session->socket_fd, responseMessage, strlen(responseMessage), 0) != strlen(responseMessage)) {
872 fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n");
873 pthread_mutex_unlock(&response->mutex);
874 return MHD_NO;
875 }
876 }
877
878 response->headersSent = 1;
879 }
880
881 if(response->data != NULL) {
882 if(response->bytesSentSoFar == 0) {
883 if(numBytesInMessage < 32) {
884 free(responseMessage);
885 responseMessage = malloc(32);
886 if(responseMessage == NULL) {
887 if(daemon->options & MHD_USE_DEBUG)
888 fprintf(stderr, "Error allocating memory!\n");
889 pthread_mutex_unlock(&response->mutex);
890 return MHD_NO;
891 }
892 }
893 sprintf(responseMessage, "Content-length: %llu\r\n\r\n", (unsigned long long)response->size);
894 fprintf(stderr, "%s\n", responseMessage);
895 if(send(session->socket_fd, responseMessage, strlen(responseMessage),0)!= strlen(responseMessage)) {
896 fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n");
897 pthread_mutex_unlock(&response->mutex);
898 return MHD_NO;
899 }
900 }
901
902 i = send(session->socket_fd, response->data+response->bytesSentSoFar, response->size-response->bytesSentSoFar,0);
903 response->bytesSentSoFar += i;
904
905 fprintf(stderr, "Sent %i bytes of data\nTotal to send is %llu bytes\n", i, (unsigned long long)response->size);
906
907 if(response->bytesSentSoFar == response->size) {
908 session->currentResponses[session->currentResponse] = NULL;
909 session->currentResponse = (session->currentResponse + 1) % MHD_MAX_RESPONSE;
910 response->currentSession = NULL;
911
912 if(response->freeWhenFinished) {
913 pthread_mutex_unlock(&response->mutex);
914 MHD_destroy_response(response);
915 }
916 /*THIS NEEDS TO BE HANDLED ANOTHER WAY!!! TIMEOUT, ect..., as of now this is the only way to get test case to work
917 * since client never disconnects on their own!
918 */
919 if(session->currentResponses[session->currentResponse] == NULL) {
920 MHD_destroy_session(session);
921 daemon->connections[connection_id] = NULL;
922 return MHD_NO;
923 }
924 }
925 } else {
926 if(response->crc == NULL) {
927 pthread_mutex_unlock(&response->mutex);
928 return MHD_NO;
929 }
930
931 if(response->bytesSentSoFar == 0) {
932 if(send(session->socket_fd, "\r\n", response->size,0) != 2) {
933 fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n");
934 pthread_mutex_unlock(&response->mutex);
935 return MHD_NO;
936 }
937 }
938 memset(buffer, 0, 2048);
939
940 i = response->crc(response->crc_cls, response->bytesSentSoFar, (char *)buffer, 2048);
941
942 if(i == -1) {
943 pthread_mutex_unlock(&response->mutex);
944
945 session->currentResponses[session->currentResponse] = NULL;
946 session->currentResponse = (session->currentResponse + 1) % MHD_MAX_RESPONSE;
947 response->currentSession = NULL;
948
949 if(response->freeWhenFinished) {
950 pthread_mutex_unlock(&response->mutex);
951 MHD_destroy_response(response);
952 }
953 /*THIS NEEDS TO BE HANDLED ANOTHER WAY!!! TIMEOUT, ect..., as of now this is the only way to get test case to work
954 * since client never disconnects on their own!
955 */
956 if(session->currentResponses[session->currentResponse] == NULL) {
957 MHD_destroy_session(session);
958 daemon->connections[connection_id] = NULL;
959 return MHD_NO;
960 }
961
962 } else {
963 i = send(session->socket_fd, buffer, i,0);
964 response->bytesSentSoFar += i;
965 }
966 }
967 pthread_mutex_unlock(&response->mutex);
968 return MHD_YES;
969}
970
971
972/**
973 * Get a particular header value. If multiple
974 * values match the kind, return any one of them.
975 *
976 * @param key the header to look for
977 * @return NULL if no such item was found
978 */
979const char *
980MHD_lookup_session_value(struct MHD_Session * session,
981 enum MHD_ValueKind kind,
982 const char * key) {
983 int i;
984
985 for(i = 0; i < MHD_MAX_HEADERS; i++) {
986 if(session->headers[i] != NULL &&
987 session->headers[i]->kind == kind &&
988 strncmp(session->headers[i]->header, key, strlen(session->headers[i]->header)) == 0) {
989 return (const char *)session->headers[i]->headerContent;
990 }
991 }
992
993 return NULL;
994} 287}
995 288
996 289
997/** 290/**
998 * This function is designed to parse the input buffer of a given session. 291 * Free resources associated with all closed sessions.
999 * It is assumed that the data being parsed originates at buffer location 292 * (destroy responses, free buffers, etc.). A session
1000 * 0 (a valid assumption since the buffer is shifted after each message) 293 * is known to be closed if the socket_fd is -1.
1001 */ 294 */
1002int 295static void
1003MHD_parse_message(struct MHD_Session * session) { 296MHD_cleanup_sessions(struct MHD_Daemon * daemon) {
1004 const char * crlfcrlf = "\r\n\r\n"; 297 struct MHD_Session * pos;
1005 const char * crlf = "\r\n"; 298 struct MHD_Session * prev;
1006 299
1007 char * saveptr; 300 pos = daemon->connections;
1008 char * saveptr1; 301 prev = NULL;
1009 302 while (pos != NULL) {
1010 struct MHD_HTTP_Header * newHeader; 303 if (pos->socket_fd == -1) {
1011 char * curTok; 304 if (prev == NULL)
1012 char * curTok1; 305 daemon->connections = pos->next;
1013 306 else
1014 int numBytes; 307 prev->next = pos->next;
1015 308 free(pos->addr);
1016 curTok = strstr(session->inbuf, crlfcrlf); 309 /* FIXME: more to free here! */
1017 310 free(pos);
1018 if(curTok == NULL) { 311 }
1019 return -1; 312 prev = pos;
1020 } 313 pos = pos->next;
1021 314 }
1022 memset(curTok+2, 0, 2);
1023
1024 numBytes = strlen(session->inbuf) + 2;
1025
1026 curTok = strtok_r(session->inbuf, crlf, &saveptr);
1027
1028 session->requestType = strtok_r(curTok, " ", &saveptr1);
1029
1030 newHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header));
1031 if(newHeader == NULL) {
1032 if(session->daemon->options & MHD_USE_DEBUG)
1033 fprintf(stderr, "Error allocating memory!\n");
1034 return -1;
1035 }
1036 newHeader->kind = MHD_GET_ARGUMENT_KIND;
1037 newHeader->header = session->requestType;
1038 newHeader->headerContent = strtok_r(NULL, " ", &saveptr1);
1039
1040 session->headers[session->firstFreeHeader++] = newHeader;
1041
1042 curTok = strtok_r(NULL, crlf, &saveptr);
1043 while(curTok != NULL && session->firstFreeHeader < MHD_MAX_HEADERS) {
1044 curTok1 = strtok_r(curTok, ":", &saveptr1);
1045 newHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header));
1046 if(newHeader == NULL) {
1047 if(session->daemon->options & MHD_USE_DEBUG)
1048 fprintf(stderr, "Error allocating memory!\n");
1049 return -1;
1050 }
1051 newHeader->header = curTok1;
1052 newHeader->headerContent = curTok + strlen(curTok1) + 2;
1053 //For now, everything is a get!
1054 newHeader->kind = MHD_GET_ARGUMENT_KIND;
1055 session->headers[session->firstFreeHeader++] = newHeader;
1056 curTok = strtok_r(NULL, crlf, &saveptr);
1057 }
1058
1059 return numBytes;
1060} 315}
1061 316
1062/**
1063 * This function needs to do a lot more (i.e. break up get arguments)
1064 * but for now just seperates the prefix of the url from the document
1065 * portion.
1066 */
1067void
1068MHD_parse_URL(struct MHD_Session * session) {
1069 char * working;
1070 int pos,i;
1071
1072 working = session->headers[0]->headerContent;
1073
1074 pos = 0;
1075 for(i = 0; i < strlen(working); i++) {
1076 if(working[i] == '/')
1077 pos = i+1;
1078 }
1079 if(pos >= strlen(working))
1080 pos = 0;
1081
1082 session->documentName = session->headers[0]->headerContent+pos;
1083}
1084 317
1085/** 318/**
1086 * Queue a response to be transmitted to the client (as soon as 319 * Main select call.
1087 * possible). 320 *
1088 * 321 * @return MHD_NO on serious errors, MHD_YES on success
1089 * @param session the session identifying the client
1090 * @param status_code HTTP status code (i.e. 200 for OK)
1091 * @param response response to transmit
1092 * @return MHD_NO on error (i.e. reply already sent),
1093 * MHD_YES on success or if message has been queued
1094 */ 322 */
1095int 323static int
1096MHD_queue_response(struct MHD_Session * session, 324MHD_select(struct MHD_Daemon * daemon) {
1097 unsigned int status_code, 325 struct MHD_Session * pos;
1098 struct MHD_Response * response) { 326 int num_ready;
1099 327 fd_set rs;
1100 //As of now this function can only support a fixed amount of queued responses, and will 328 fd_set ws;
1101 //return MHD_NO if that queue is full 329 fd_set es;
1102 330 int max;
1103 int index; 331
1104 332 if(daemon == NULL)
1105 if(session == NULL || response == NULL) { 333 abort();
1106 return MHD_NO; 334 FD_ZERO(&rs);
1107 } 335 FD_ZERO(&ws);
1108 336 FD_ZERO(&es);
1109 pthread_mutex_lock(&response->mutex); 337 max = 0;
1110 338
1111 if(response->currentSession != NULL) { 339 if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) {
1112 return MHD_NO; 340 /* single-threaded, go over everything */
1113 } 341 if (MHD_NO == MHD_get_fdset(daemon,
1114 342 &rs,
1115 if(session->currentResponses[session->currentResponse] == NULL) { 343 &ws,
1116 index = session->currentResponse; 344 &es,
1117 } else if(session->currentResponses[session->currentResponse + 1 % MHD_MAX_RESPONSE] == NULL) { 345 &max))
1118 index = session->currentResponse + 1 % MHD_MAX_RESPONSE; 346 return MHD_NO;
1119 } else { 347 } else {
1120 pthread_mutex_unlock(&response->mutex); 348 /* accept only, have one thread per connection */
1121 return MHD_NO; 349 max = daemon->socket_fd;
1122 } 350 FD_SET(daemon->socket_fd, &rs);
1123 351 }
1124 response->responseCode = status_code; 352 num_ready = select(max + 1,
1125 session->currentResponses[index] = response; 353 &rs,
1126 response->currentSession = session; 354 &ws,
1127 session->responsePending = 0; 355 &es,
1128 356 NULL);
1129 pthread_mutex_unlock(&response->mutex); 357 if (num_ready < 0) {
1130 358 if (errno == EINTR)
1131 return MHD_YES; 359 return MHD_YES;
360 DLOG(daemon,
361 "Select failed: %s\n",
362 strerror(errno));
363 return MHD_NO;
364 }
365 if (FD_ISSET(daemon->socket_fd,
366 &rs))
367 MHD_accept_connection(daemon);
368 if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) {
369 /* do not have a thread per connection, process all connections now */
370 pos = daemon->connections;
371 while (pos != NULL) {
372 if (FD_ISSET(pos->socket_fd, &rs))
373 MHD_session_handle_read(pos);
374 if (FD_ISSET(pos->socket_fd, &ws))
375 MHD_session_handle_write(pos);
376 pos = pos->next;
377 }
378 }
379 return MHD_YES;
1132} 380}
1133 381
1134/**
1135 * @return -1 if no data uploaded; otherwise number of bytes
1136 * read into buf; 0 for end of transmission
1137 * Specification not complete at this time.
1138 */
1139int
1140MHD_read_file_upload(struct MHD_Session * session,
1141 void * buf,
1142 size_t len) {
1143 //This function will not be implemented until the specification is completed.
1144 return -1;
1145}
1146 382
1147/** 383/**
1148 * Run webserver operations (without blocking unless 384 * Run webserver operations (without blocking unless
@@ -1156,190 +392,34 @@ MHD_read_file_upload(struct MHD_Session * session,
1156 */ 392 */
1157int 393int
1158MHD_run(struct MHD_Daemon * daemon) { 394MHD_run(struct MHD_Daemon * daemon) {
1159 395 if ( (daemon->shutdown != 0) ||
1160 if((daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0) { 396 (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
1161 daemon->shutdown = 0; 397 (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) )
1162 if(pthread_create(&daemon->pid, NULL, (void *) &MHD_spawn_connections, (void *)daemon) == 0) { 398 return MHD_NO;
1163 return MHD_YES; 399 MHD_select(daemon);
1164 } else { 400 MHD_cleanup_sessions(daemon);
1165 return MHD_NO; 401 return MHD_YES;
1166 }
1167 } else if((daemon->options & MHD_USE_SELECT_INTERNALLY) != 0) {
1168 daemon->shutdown = 0;
1169 if(pthread_create(&daemon->pid, NULL, (void *) &MHD_select, (void *)daemon) == 0) {
1170 return MHD_YES;
1171 } else {
1172 return MHD_NO;
1173 }
1174 } else {
1175 daemon->shutdown = 1;
1176 return (MHD_select((void *)daemon) == NULL);
1177 }
1178} 402}
1179 403
1180 404
1181/** 405/**
1182 * Register an access handler for all URIs beginning with uri_prefix. 406 * Thread that runs the select loop until the daemon
1183 * 407 * is explicitly shut down.
1184 * @param uri_prefix
1185 * @return MRI_NO if a handler for this exact prefix
1186 * already exists
1187 */ 408 */
1188int 409static void *
1189MHD_register_handler(struct MHD_Daemon * daemon, 410MHD_select_thread(void * cls) {
1190 const char * uri_prefix, 411 struct MHD_Daemon * daemon = cls;
1191 MHD_AccessHandlerCallback dh, 412 while (daemon->shutdown == 0) {
1192 void * dh_cls) { 413 MHD_select(daemon);
1193 int i; 414 MHD_cleanup_sessions(daemon);
1194 415 }
1195 //This function will also return MHD_NO if the maximum number of supported handlers is exceeded 416 return NULL;
1196
1197 if(daemon == NULL || uri_prefix == NULL || dh == NULL) {
1198 return MHD_NO;
1199 }
1200
1201 if(daemon->firstFreeHandler >= MHD_MAX_HANDLERS) {
1202 return MHD_NO;
1203 }
1204
1205 daemon->handlers[daemon->firstFreeHandler] = malloc(sizeof(struct MHD_Access_Handler));
1206
1207 if(daemon->handlers[daemon->firstFreeHandler] == NULL) {
1208 if((daemon->options & MHD_USE_DEBUG) != 0)
1209 fprintf(stderr, "Error allocating memory!\n");
1210 return MHD_NO;
1211 }
1212
1213 daemon->handlers[daemon->firstFreeHandler]->uri_prefix = malloc(strlen(uri_prefix)+1);
1214 if(daemon->handlers[daemon->firstFreeHandler]->uri_prefix == NULL) {
1215 if((daemon->options & MHD_USE_DEBUG) != 0) {
1216 free(daemon->handlers[daemon->firstFreeHandler]);
1217 fprintf(stderr, "Error allocating memory!\n");
1218 }
1219 return MHD_NO;
1220 }
1221 sprintf(daemon->handlers[daemon->firstFreeHandler]->uri_prefix, "%s", uri_prefix);
1222
1223 daemon->handlers[daemon->firstFreeHandler]->dh = dh;
1224 daemon->handlers[daemon->firstFreeHandler]->dh_cls = dh_cls;
1225
1226 daemon->firstFreeHandler = MHD_MAX_HANDLERS;
1227 for(i = 0; i < MHD_MAX_HANDLERS; i++) {
1228 if(daemon->handlers[i] == NULL) {
1229 daemon->firstFreeHandler = i;
1230 break;
1231 }
1232 }
1233
1234 return MHD_YES;
1235
1236} 417}
1237 418
1238/**
1239 * This function is the entry point for either internal or external select.
1240 * The only differences between the two forms of running is whether the call is
1241 * made from a new thread or the main thread, and whether the initial value
1242 * of shutdown is 0 or 1 (1 for loop, 0 for one time pass)
1243 */
1244void *
1245MHD_select(void * data) {
1246 struct MHD_Daemon * daemon;
1247 int i, num_ready;
1248 struct timeval timeout;
1249
1250 daemon = data;
1251 if(daemon == NULL) {
1252 return NULL;
1253 }
1254 do {
1255 FD_ZERO(&daemon->read_fd_set);
1256 FD_ZERO(&daemon->write_fd_set);
1257 FD_ZERO(&daemon->except_fd_set);
1258
1259 FD_SET(daemon->socket_fd, &daemon->read_fd_set);
1260 for(i = 0; i < MHD_MAX_CONNECTIONS; i++) {
1261 if(daemon->connections[i] != NULL) {
1262 FD_SET(daemon->connections[i]->socket_fd, &daemon->read_fd_set);
1263 FD_SET(daemon->connections[i]->socket_fd, &daemon->write_fd_set);
1264 }
1265 }
1266
1267 timeout.tv_sec = 0;
1268 timeout.tv_usec = 0;
1269
1270 num_ready = select(daemon->max_fd + 1,
1271 &(daemon->read_fd_set), &(daemon->write_fd_set), &(daemon->except_fd_set), &timeout);
1272
1273 if(num_ready > 0) {
1274 for(i = 0; i < MHD_MAX_CONNECTIONS; i++) {
1275 if(daemon->connections[i] != NULL) {
1276 if(FD_ISSET(daemon->connections[i]->socket_fd, &(daemon->read_fd_set))) {
1277 if(MHD_handle_read(i, daemon) == MHD_NO)
1278 continue;
1279 }
1280 if (FD_ISSET(daemon->connections[i]->socket_fd, &(daemon->write_fd_set))) {
1281 if(MHD_handle_write(i, daemon) == MHD_NO)
1282 continue;
1283 }
1284 if (FD_ISSET(daemon->connections[i]->socket_fd, &(daemon->except_fd_set))) {
1285 MHD_handle_except(i, daemon);
1286 }
1287 }
1288 }
1289 if(FD_ISSET(daemon->socket_fd, &(daemon->read_fd_set))) {
1290 if(MHD_create_connection(daemon) == -1) {
1291 continue;
1292 }
1293 }
1294 }
1295 } while (!daemon->shutdown);
1296
1297 return NULL;
1298}
1299
1300/**
1301 * This function was created for the case of multithreaded connections.
1302 * A thread will spawned to sit on this function, and in turn spawns more
1303 * threads, one per connection.
1304 */
1305void *
1306MHD_spawn_connections(void * data) {
1307 struct MHD_Daemon * daemon;
1308 int con, num_ready;
1309 struct timeval timeout;
1310 fd_set read;
1311
1312 daemon = data;
1313 if(daemon == NULL) {
1314 return NULL;
1315 }
1316
1317 do {
1318 FD_ZERO(&read);
1319 FD_SET(daemon->socket_fd, &read);
1320
1321 timeout.tv_sec = 0;
1322 timeout.tv_usec = 0;
1323
1324 num_ready = select(daemon->socket_fd + 1,&read, NULL, NULL, &timeout);
1325
1326 if(num_ready > 0) {
1327 con = MHD_create_connection(daemon);
1328 if(con == -1)
1329 continue;
1330
1331 if(pthread_create(&daemon->connections[con]->pid, NULL, (void *) &MHD_handle_connection, (void *)daemon->connections[con]) != 0) {
1332 if((daemon->options & MHD_USE_DEBUG) != 0)
1333 fprintf(stderr, "Error creating connection handler!.\n");
1334 }
1335 }
1336 } while (!daemon->shutdown);
1337
1338 return NULL;
1339}
1340 419
1341/** 420/**
1342 * Start a webserver on the given port. 421 * Start a webserver on the given port.
422 *
1343 * @param port port to bind to 423 * @param port port to bind to
1344 * @param apc callback to call to check which clients 424 * @param apc callback to call to check which clients
1345 * will be allowed to connect 425 * will be allowed to connect
@@ -1355,128 +435,73 @@ MHD_start_daemon(unsigned int options,
1355 void * apc_cls, 435 void * apc_cls,
1356 MHD_AccessHandlerCallback dh, 436 MHD_AccessHandlerCallback dh,
1357 void * dh_cls) { 437 void * dh_cls) {
1358 438 struct MHD_Daemon * retVal;
1359 439 int socket_fd;
1360 struct MHD_Daemon * retVal = NULL; 440 int opt;
1361 int socket_fd, opt, res, i; 441 int res;
1362 struct sockaddr_in servaddr; 442 struct sockaddr_in servaddr;
1363 struct hostent *hostptr; 443
1364 char hostid[32]; 444 if ((options & MHD_USE_SSL) != 0)
1365 445 return NULL;
1366 if((options & MHD_USE_SSL) != 0) { 446 if ((options & MHD_USE_IPv6) != 0)
1367 if((options & MHD_USE_DEBUG) != 0) 447 return NULL;
1368 fprintf(stderr, "SSL at this time is unsupported.\n"); 448 if ((options & MHD_USE_IPv4) == 0)
1369 return NULL; 449 return NULL;
1370 } 450 if ( (port == 0) ||
1371 if((options & MHD_USE_IPv6) != 0) { 451 (dh == NULL) )
1372 if((options & MHD_USE_DEBUG) != 0) 452 return NULL;
1373 fprintf(stderr, "IP version 6 is not supported at this time.\n"); 453 socket_fd = socket(AF_INET, SOCK_STREAM, 0);
1374 return NULL; 454 if (socket_fd < 0) {
1375 } 455 /* FIXME: log error */
1376 456 return NULL;
1377 if((options & MHD_USE_IPv4) != 0) { 457 }
1378 if((options & MHD_USE_DEBUG) != 0) 458 memset(&servaddr,
1379 fprintf(stderr, "Enter MHD_start_daemon. Starting Daemon on port %i\n", port); 459 0,
1380 460 sizeof(struct sockaddr_in));
1381 if(port < 1) { 461 servaddr.sin_family = AF_INET;
1382 if((options & MHD_USE_DEBUG) != 0) 462 servaddr.sin_port = htons(port);
1383 fprintf(stderr, "Invalid port: %i!\n", port); 463 if (bind(socket_fd,
1384 return NULL; 464 (struct sockaddr *)&servaddr,
1385 } 465 sizeof(struct sockaddr_in)) < 0) {
1386 466 /* FIXME: log error */
1387 if(dh == NULL) { 467 close(socket_fd);
1388 if((options & MHD_USE_DEBUG) != 0) 468 return NULL;
1389 fprintf(stderr, "A default access handler must be provided\n"); 469 }
1390 return NULL; 470 if (listen(socket_fd, 20) < 0) {
1391 } 471 /* FIXME: log error */
1392 472 return NULL;
1393 retVal = (struct MHD_Daemon *)malloc(sizeof(struct MHD_Daemon)); 473 }
1394 if(retVal == NULL) { 474 opt = fcntl(socket_fd, F_GETFL, 0);
1395 if((options & MHD_USE_DEBUG) != 0) 475 res = fcntl(socket_fd, F_SETFL, opt | O_NONBLOCK);
1396 fprintf(stderr, "Error allocating memory!\n"); 476 if (res < 0) {
1397 return NULL; 477 /* FIXME: log error */
1398 } 478 close(socket_fd);
1399 479 return NULL;
1400 retVal->options = options; 480 }
1401 retVal->port = port; 481
1402 retVal->apc = apc; 482 retVal = malloc(sizeof(struct MHD_Daemon));
1403 retVal->apc_cls = apc_cls; 483 memset(retVal,
1404 retVal->dh = dh; 484 0,
1405 retVal->dh_cls = dh_cls; 485 sizeof(struct MHD_Daemon));
1406 retVal->shutdown = 0; 486 retVal->options = options;
1407 retVal->pid = (pthread_t)-1; 487 retVal->port = port;
1408 488 retVal->apc = apc;
1409 retVal->firstFreeHandler = 0; 489 retVal->apc_cls = apc_cls;
1410 for(i = 0; i < MHD_MAX_HANDLERS; i++) { 490 retVal->dh = dh;
1411 retVal->handlers[i] = NULL; 491 retVal->dh_cls = dh_cls;
1412 } 492 retVal->socket_fd = socket_fd;
1413 493 if ( ( (0 != (options & MHD_USE_THREAD_PER_CONNECTION)) ||
1414 FD_ZERO(&retVal->read_fd_set); 494 (0 != (options & MHD_USE_SELECT_INTERNALLY)) ) &&
1415 FD_ZERO(&retVal->write_fd_set); 495 (0 != pthread_create(&daemon->pid,
1416 FD_ZERO(&retVal->except_fd_set); 496 NULL,
1417 497 &MHD_select_thread,
1418 for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { 498 daemon)) ) {
1419 retVal->connections[i] = NULL; 499 /* FIXME: log error */
1420 } 500 free(retVal);
1421 501 close(socket_fd);
1422 socket_fd = socket(AF_INET, SOCK_STREAM, 0); 502 return NULL;
1423 if(socket_fd < 0) { 503 }
1424 if((options & MHD_USE_DEBUG) != 0) 504 return retVal;
1425 perror("Error creating socket!");
1426 return NULL;
1427 }
1428
1429 memset((void *)&servaddr, 0, (size_t)sizeof(servaddr));
1430
1431 if (gethostname(hostid,32) < 0){
1432 if((options & MHD_USE_DEBUG) != 0)
1433 perror("server_tcp:gethostname");
1434 return NULL;
1435 }
1436
1437 if ((hostptr = gethostbyname(hostid)) == NULL){
1438 if((options & MHD_USE_DEBUG) != 0)
1439 fprintf(stderr, "invalid host name, %s\n",hostid);
1440 return NULL;
1441 }
1442
1443 servaddr.sin_family = AF_INET;
1444 memcpy((void *)&(servaddr.sin_addr), (void *)(hostptr->h_addr), hostptr->h_length);
1445 servaddr.sin_port = htons(port);
1446
1447
1448 if (bind(socket_fd, (struct sockaddr *)&servaddr, (socklen_t)sizeof(servaddr)) < 0) {
1449 if((options & MHD_USE_DEBUG) != 0)
1450 perror("server:bind");
1451 return NULL;
1452 }
1453
1454 if(listen(socket_fd, 20) < 0) {
1455 if((options & MHD_USE_DEBUG) != 0)
1456 perror("server:bind");
1457 return NULL;
1458 }
1459
1460 retVal->socket_fd = socket_fd;
1461 retVal->max_fd = socket_fd;
1462 FD_SET(socket_fd, &retVal->read_fd_set);
1463
1464 opt = fcntl(socket_fd, F_GETFL, 0);
1465 res = fcntl(socket_fd, F_SETFL, opt | O_NONBLOCK);
1466 if(res < 0) {
1467 if((options & MHD_USE_DEBUG) != 0)
1468 perror("Error disabling block on socket!");
1469 return NULL;
1470 }
1471
1472 return retVal;
1473 }
1474
1475 if((options & MHD_USE_DEBUG) != 0)
1476 fprintf(stderr, "No options given to start_daemon!\n");
1477
1478 return NULL;
1479
1480} 505}
1481 506
1482/** 507/**
@@ -1484,76 +509,26 @@ MHD_start_daemon(unsigned int options,
1484 */ 509 */
1485void 510void
1486MHD_stop_daemon(struct MHD_Daemon * daemon) { 511MHD_stop_daemon(struct MHD_Daemon * daemon) {
1487 int i, j; 512 void * unused;
1488 513
1489 if(daemon == NULL) { 514 if (daemon == NULL)
1490 return; 515 return;
1491 } 516 daemon->shutdown = 1;
1492 517 close(daemon->socket_fd);
1493 if((daemon->options & MHD_USE_DEBUG) != 0) 518 daemon->socket_fd = -1;
1494 fprintf(stderr, "Enter MHD_stop_daemon. Stopping daemon on port %i\n", daemon->port); 519 if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
1495 520 (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) )
1496 daemon->shutdown = 1; 521 pthread_join(daemon->pid, &unused);
1497 522
1498 if(daemon->pid != (pthread_t)-1) { 523 while (daemon->connections != NULL) {
1499 pthread_join(daemon->pid, NULL); 524 if (-1 != daemon->connections->socket_fd) {
1500 } 525 close(daemon->connections->socket_fd);
1501 526 daemon->connections->socket_fd = -1;
1502 for(i = 0; i < MHD_MAX_CONNECTIONS; i++) { 527 }
1503 if(daemon->connections[i] != NULL) { 528 pthread_join(daemon->connections->pid, &unused);
1504 if(daemon->connections[i]->pid != (pthread_t)-1) { 529
1505 pthread_join(daemon->connections[i]->pid, NULL); 530 MHD_cleanup_sessions(daemon);
1506 } 531 }
1507 532 free(daemon);
1508 for(j = 0; j < MHD_MAX_RESPONSE; j++) {
1509 if(daemon->connections[i]->currentResponses[j] != NULL) {
1510 MHD_destroy_response(daemon->connections[i]->currentResponses[j]);
1511 }
1512 }
1513 MHD_destroy_session(daemon->connections[i]);
1514 }
1515 }
1516
1517 for(i = 0; i < MHD_MAX_HANDLERS; i++) {
1518 if(daemon->handlers[i] != NULL) {
1519 free(daemon->handlers[i]->uri_prefix);
1520 free(daemon->handlers[i]);
1521 }
1522 }
1523
1524 close(daemon->socket_fd);
1525
1526 free(daemon);
1527}
1528
1529
1530/**
1531 * Unregister an access handler for the URIs beginning with
1532 * uri_prefix.
1533 *
1534 * @param uri_prefix
1535 * @return MHD_NO if a handler for this exact prefix
1536 * is not known for this daemon
1537 */
1538int
1539MHD_unregister_handler(struct MHD_Daemon * daemon,
1540 const char * uri_prefix,
1541 MHD_AccessHandlerCallback dh,
1542 void * dh_cls) {
1543 int i;
1544
1545 for(i = 0; i < MHD_MAX_HANDLERS; i++) {
1546 if(daemon->handlers[i] != NULL) {
1547 if(strncmp(daemon->handlers[i]->uri_prefix, uri_prefix, strlen(daemon->handlers[i]->uri_prefix)) == 0) {
1548 if(daemon->handlers[i]->dh == dh && daemon->handlers[i]->dh_cls == dh_cls) {
1549 free(daemon->handlers[i]->uri_prefix);
1550 free(daemon->handlers[i]);
1551 return MHD_YES;
1552 }
1553 }
1554 }
1555 }
1556
1557 return MHD_NO;
1558} 533}
1559 534
diff --git a/src/daemon/internal.h b/src/daemon/internal.h
new file mode 100644
index 00000000..e09acf3b
--- /dev/null
+++ b/src/daemon/internal.h
@@ -0,0 +1,111 @@
1/*
2 This file is part of libmicrohttpd
3 (C) 2007 Daniel Pittman
4
5 libmicrohttpd is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 libmicrohttpd is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with libmicrohttpd; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file internal.h
23 * @brief internal shared structures
24 * @author Daniel Pittman
25 * @author Christian Grothoff
26 * @version 0.1.0
27 */
28
29#ifndef INTERNAL_H
30#define INTERNAL_H
31
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <netdb.h>
36#include <string.h>
37#include <unistd.h>
38#include <stdarg>
39#include <fcntl.h>
40#include <pthread.h>
41#include <netinet/in.h>
42
43#include "microhttpd.h"
44#include "config.h"
45
46
47/**
48 * Header or cookie in HTTP request or response.
49 */
50struct MHD_HTTP_Header {
51 struct MHD_HTTP_Header * next;
52
53 char * header;
54
55 char * value;
56
57 enum MHD_ValueKind kind;
58};
59
60
61struct MHD_Access_Handler {
62 struct MHD_Access_Handler * next;
63
64 char * uri_prefix;
65
66 MHD_AccessHandlerCallback dh;
67
68 void * dh_cls;
69};
70
71
72struct MHD_Daemon {
73
74 struct MHD_Access_Handler * handlers;
75
76 MHD_AccessHandlerCallback default_handler;
77
78 struct MHD_Session * connections;
79
80 void * dh_cls;
81
82 MHD_AcceptPolicyCallback apc;
83
84 void * apc_cls;
85
86 /**
87 * PID of the select thread (if we have internal select)
88 */
89 pthread_t pid;
90
91 /**
92 * Listen socket.
93 */
94 int socket_fd;
95
96 /**
97 * Are we shutting down?
98 */
99 int shutdown;
100
101 /**
102 * Daemon's options.
103 */
104 enum MHD_OPTION options;
105
106 unsigned short port;
107
108};
109
110
111#endif
diff --git a/src/daemon/response.c b/src/daemon/response.c
new file mode 100644
index 00000000..a26b003b
--- /dev/null
+++ b/src/daemon/response.c
@@ -0,0 +1,333 @@
1/*
2 This file is part of libmicrohttpd
3 (C) 2007 Daniel Pittman
4
5 libmicrohttpd is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 libmicrohttpd is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with libmicrohttpd; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file response.c
23 * @brief Methods for managing response objects
24 * @author Daniel Pittman
25 * @author Christian Grothoff
26 * @version 0.1.0
27 */
28
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <netdb.h>
33#include <string.h>
34#include <unistd.h>
35#include <stdarg>
36#include <fcntl.h>
37#include <pthread.h>
38#include <netinet/in.h>
39
40#include "microhttpd.h"
41#include "response.h"
42#include "internal.h"
43#include "config.h"
44
45
46/**
47 * Representation of a response.
48 */
49struct MHD_Response {
50
51 /**
52 * Headers to send for the response. Initially
53 * the linked list is created in inverse order;
54 * the order should be inverted before sending!
55 */
56 struct MHD_HTTP_Header * first_header;
57
58 /**
59 * Buffer pointing to data that we are supposed
60 * to send as a response.
61 */
62 void * data;
63
64 /**
65 * Closure to give to the content reader
66 * free callback.
67 */
68 void * crc_cls;
69
70 /**
71 * How do we get more data? NULL if we are
72 * given all of the data up front.
73 */
74 MHD_ContentReaderCallback crc;
75
76 /**
77 * NULL if data must not be freed, otherwise
78 * either user-specified callback or "&free".
79 */
80 MHD_ContentReaderFreeCallback crfc;
81
82 /**
83 * Mutex to synchronize access to data/size and
84 * reference counts.
85 */
86 pthread_mutex_t mutex;
87
88 /**
89 * Reference count for this response. Free
90 * once the counter hits zero.
91 */
92 unsigned int reference_count;
93
94 /**
95 * Set to -1 if size is not known.
96 */
97 size_t total_size;
98
99 /**
100 * Size of data.
101 */
102 size_t data_size;
103
104 /**
105 * At what offset in the stream is the
106 * beginning of data located?
107 */
108 size_t data_start;
109
110};
111
112
113
114/**
115 * Add a header line to the response.
116 *
117 * @return MHD_NO on error (i.e. invalid header or content format).
118 */
119int
120MHD_add_response_header(struct MHD_Response * response,
121 const char * header,
122 const char * content) {
123 struct MHD_HTTP_Header * hdr;
124
125 if ( (response == NULL) ||
126 (header == NULL) ||
127 (content == NULL) ||
128 (strlen(header) == 0) ||
129 (strlen(content) == 0) ||
130 (NULL != strstr(header, "\t")) ||
131 (NULL != strstr(header, "\r")) ||
132 (NULL != strstr(header, "\n")) ||
133 (NULL != strstr(content, "\t")) ||
134 (NULL != strstr(content, "\r")) ||
135 (NULL != strstr(content, "\n")) )
136 return MHD_NO;
137 hdr = malloc(sizeof(MHD_HTTP_Header));
138 hdr->header = STRDUP(header);
139 hdr->value = STRDUP(content);
140 hdr->kind = MHD_HEADER_KIND;
141 hdr->next = response->first_header;
142 response->first_header = hdr;
143 return MHD_YES;
144}
145
146/**
147 * Delete a header line from the response.
148 *
149 * @return MHD_NO on error (no such header known)
150 */
151int
152MHD_del_response_header(struct MHD_Response * response,
153 const char * header,
154 const char * content) {
155 struct MHD_HTTP_Header * pos;
156 struct MHD_HTTP_Header * prev;
157
158 if ( (header == NULL) ||
159 (content == NULL) )
160 return MHD_NO;
161 prev = NULL;
162 pos = response->first_header;
163 while (pos != NULL) {
164 if ( (0 == strcmp(header, pos->header)) &&
165 (0 == strcmp(content, pos->value)) ) {
166 free(pos->header);
167 free(pos->value);
168 if (prev == NULL)
169 response->first_header = pos->next;
170 else
171 prev->next = pos->next;
172 free(pos);
173 return MHD_YES;
174 }
175 prev = pos;
176 pos = pos->next;
177 }
178 return MHD_NO;
179}
180
181/**
182 * Get all of the headers added to a response.
183 *
184 * @param iterator callback to call on each header;
185 * maybe NULL (then just count headers)
186 * @param iterator_cls extra argument to iterator
187 * @return number of entries iterated over
188 */
189int
190MHD_get_response_headers(struct MHD_Response * response,
191 MHD_KeyValueIterator * iterator,
192 void * iterator_cls) {
193 struct MHD_HTTP_Header * pos;
194 int numHeaders = 0;
195 pos = response->first_header;
196 while (pos != NULL) {
197 numHeaders++;
198 if ( (iterator != NULL) &&
199 (MHD_YES != iterator(iterator_cls,
200 pos->kind,
201 pos->header,
202 pos->value)) )
203 break;
204 pos = pos->next;
205 }
206 return numHeaders;
207}
208
209
210/**
211 * Create a response object. The response object can be extended with
212 * header information and then be used any number of times.
213 *
214 * @param size size of the data portion of the response, -1 for unknown
215 * @param crc callback to use to obtain response data
216 * @param crc_cls extra argument to crc
217 * @param crfc callback to call to free crc_cls resources
218 * @return NULL on error (i.e. invalid arguments, out of memory)
219 */
220struct MHD_Response *
221MHD_create_response_from_callback(size_t size,
222 MHD_ContentReaderCallback crc,
223 void * crc_cls,
224 MHD_ContentReaderFreeCallback crfc) {
225 struct MHD_Response * retVal;
226
227 if (crc == NULL)
228 return NULL;
229 retVal = malloc(sizeof(struct MHD_Response));
230 memset(retVal,
231 0,
232 sizeof(struct MHD_Response));
233 if (pthread_mutex_init(&retVal->mutex, NULL) != 0) {
234 free(retVal);
235 return NULL;
236 }
237 retVal->crc = crc;
238 retVal->crfc = crfc;
239 retVal->crc_cls = crc_cls;
240 retVal->reference_count = 1;
241 retVal->total_size = size;
242 return retVal;
243}
244
245/**
246 * Create a response object. The response object can be extended with
247 * header information and then be used any number of times.
248 *
249 * @param size size of the data portion of the response
250 * @param data the data itself
251 * @param must_free libmicrohttpd should free data when done
252 * @param must_copy libmicrohttpd must make a copy of data
253 * right away, the data maybe released anytime after
254 * this call returns
255 * @return NULL on error (i.e. invalid arguments, out of memory)
256 */
257struct MHD_Response *
258MHD_create_response_from_data(size_t size,
259 void * data,
260 int must_free,
261 int must_copy) {
262 struct MHD_Response * retVal;
263 void * tmp;
264
265 if ( (data == NULL) &&
266 (size > 0) )
267 return NULL;
268 retVal = malloc(sizeof(struct MHD_Response));
269 memset(retVal,
270 0,
271 sizeof(struct MHD_Response));
272 if (pthread_mutex_init(&retVal->mutex, NULL) != 0) {
273 free(retVal);
274 return NULL;
275 }
276 if ( (must_copy) &&
277 (size > 0) ) {
278 tmp = malloc(size);
279 memcpy(tmp,
280 data,
281 size);
282 must_free = 1;
283 data = tmp;
284 }
285 retVal->crc = NULL;
286 retVal->crfc = must_free ? &free : NULL;
287 retVal->crc_cls = must_free ? data : NULL;
288 retVal->reference_count = 1;
289 retVal->total_size = size;
290 retVal->data = data;
291 retVal->data_size = size;
292 return retVal;
293}
294
295/**
296 * Destroy a response object and associated resources. Note that
297 * libmicrohttpd may keep some of the resources around if the response
298 * is still in the queue for some clients, so the memory may not
299 * necessarily be freed immediatley.
300 */
301void
302MHD_destroy_response(struct MHD_Response * response) {
303 struct MHD_HTTP_Header * pos;
304
305 if (response == NULL)
306 return;
307 pthread_mutex_lock(&response->mutex);
308 if (0 != --response->reference_count) {
309 pthread_mutex_unlock(&response->mutex);
310 return;
311 }
312 pthread_mutex_unlock(&response->mutex);
313 pthread_mutex_destroy(&response->mutex);
314 while (response->first_header != NULL) {
315 pos = response->first_header;
316 response->first_header = pos->next;
317 free(pos->header);
318 free(pos->value);
319 free(pos);
320 }
321 free(response);
322}
323
324
325void
326MHD_increment_response_rc(struct MHD_Response * response) {
327 pthread_mutex_lock(&response->mutex);
328 response->reference_count++;
329 pthread_mutex_unlock(&response->mutex);
330}
331
332
333/* end of response.c */
diff --git a/src/daemon/response.h b/src/daemon/response.h
new file mode 100644
index 00000000..0f0b3745
--- /dev/null
+++ b/src/daemon/response.h
@@ -0,0 +1,39 @@
1/*
2 This file is part of libmicrohttpd
3 (C) 2007 Daniel Pittman
4
5 libmicrohttpd is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 libmicrohttpd is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with libmicrohttpd; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file response.h
23 * @brief Methods for managing response objects
24 * @author Daniel Pittman
25 * @author Christian Grothoff
26 * @version 0.1.0
27 */
28
29#ifndef RESPONSE_H
30#define RESPONSE_H
31
32/**
33 * Increment response RC. Should this be part of the
34 * public API?
35 */
36void MHD_increment_response_rc(struct MHD_Response * response);
37
38
39#endif
diff --git a/src/daemon/session.c b/src/daemon/session.c
new file mode 100644
index 00000000..9d17f12f
--- /dev/null
+++ b/src/daemon/session.c
@@ -0,0 +1,515 @@
1/*
2 This file is part of libmicrohttpd
3 (C) 2007 Daniel Pittman
4
5 libmicrohttpd is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 libmicrohttpd is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with libmicrohttpd; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file session.c
23 * @brief Methods for managing sessions
24 * @author Daniel Pittman
25 * @author Christian Grothoff
26 * @version 0.1.0
27 */
28
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <netdb.h>
33#include <string.h>
34#include <unistd.h>
35#include <stdarg>
36#include <fcntl.h>
37#include <pthread.h>
38#include <netinet/in.h>
39
40#include "microhttpd.h"
41#include "session.h"
42#include "response.h"
43#include "internal.h"
44#include "config.h"
45
46
47/**
48 * Get all of the headers from the request.
49 *
50 * @param iterator callback to call on each header;
51 * maybe NULL (then just count headers)
52 * @param iterator_cls extra argument to iterator
53 * @return number of entries iterated over
54 */
55int
56MHD_get_session_values(struct MHD_Session * session,
57 enum MHD_ValueKind kind,
58 MHD_KeyValueIterator * iterator,
59 void * iterator_cls) {
60 int ret;
61 struct MHD_HTTP_Header * pos;
62
63 if (session == NULL)
64 return -1;
65 ret = 0;
66 pos = session->headers_received;
67 while (pos != NULL) {
68 if (0 != (pos->kind & kind)) {
69 ret++;
70 if ( (iterator != NULL) &&
71 (MHD_YES != iterator(iterator_cls,
72 kind,
73 pos->header,
74 pos->value)) )
75 return ret;
76 }
77 pos = pos->next;
78 }
79 return ret;
80}
81
82
83/**
84 * Get a particular header value. If multiple
85 * values match the kind, return any one of them.
86 *
87 * @param key the header to look for
88 * @return NULL if no such item was found
89 */
90const char *
91MHD_lookup_session_value(struct MHD_Session * session,
92 enum MHD_ValueKind kind,
93 const char * key) {
94 struct MHD_HTTP_Header * pos;
95
96 if (session == NULL)
97 return NULL;
98 ret = 0;
99 pos = session->headers_received;
100 while (pos != NULL) {
101 if ( (0 != (pos->kind & kind)) &&
102 (0 == strcmp(key,
103 pos->header)) )
104 return pos->value;
105 pos = pos->next;
106 }
107 return NULL;
108}
109
110/**
111 * Queue a response to be transmitted to the client (as soon as
112 * possible).
113 *
114 * @param session the session identifying the client
115 * @param status_code HTTP status code (i.e. 200 for OK)
116 * @param response response to transmit
117 * @return MHD_NO on error (i.e. reply already sent),
118 * MHD_YES on success or if message has been queued
119 */
120int
121MHD_queue_response(struct MHD_Session * session,
122 unsigned int status_code,
123 struct MHD_Response * response) {
124 if ( (session == NULL) ||
125 (response == NULL) ||
126 (session->response != NULL) )
127 return MHD_NO;
128 MHD_increment_response_rc(response);
129 session->response = response;
130 session->responseCode = status_code;
131 return MHD_YES;
132}
133
134
135/**
136 * Obtain the select sets for this session
137 *
138 * @return MHD_YES on success
139 */
140int
141MHD_session_get_fdset(struct MHD_Session * session,
142 fd_set * read_fd_set,
143 fd_set * write_fd_set,
144 fd_set * except_fd_set,
145 int * max_fd) {
146 FD_SET(session->socket_fd, read_fd_set);
147 FD_SET(session->socket_fd, write_fd_set);
148 if (session->socket_fd > *max_fd)
149 *max_fd = session->socket_fd;
150 return MHD_YES;
151}
152
153
154
155/* FIXME: implement/fix code below this line! */
156
157
158
159/**
160 * This function is designed to parse the input buffer of a given session.
161 * It is assumed that the data being parsed originates at buffer location
162 * 0 (a valid assumption since the buffer is shifted after each message)
163 */
164static int
165MHD_parse_message(struct MHD_Session * session) {
166 const char * crlfcrlf = "\r\n\r\n";
167 const char * crlf = "\r\n";
168
169 char * saveptr;
170 char * saveptr1;
171
172 struct MHD_HTTP_Header * newHeader;
173 char * curTok;
174 char * curTok1;
175
176 int numBytes;
177
178 curTok = strstr(session->inbuf, crlfcrlf);
179
180 if(curTok == NULL) {
181 return -1;
182 }
183
184 memset(curTok+2, 0, 2);
185
186 numBytes = strlen(session->inbuf) + 2;
187
188 curTok = strtok_r(session->inbuf, crlf, &saveptr);
189
190 session->requestType = strtok_r(curTok, " ", &saveptr1);
191
192 newHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header));
193 if(newHeader == NULL) {
194 if(session->daemon->options & MHD_USE_DEBUG)
195 fprintf(stderr, "Error allocating memory!\n");
196 return -1;
197 }
198 newHeader->kind = MHD_GET_ARGUMENT_KIND;
199 newHeader->header = session->requestType;
200 newHeader->value = strtok_r(NULL, " ", &saveptr1);
201
202 session->headers[session->firstFreeHeader++] = newHeader;
203
204 curTok = strtok_r(NULL, crlf, &saveptr);
205 while(curTok != NULL && session->firstFreeHeader < MHD_MAX_HEADERS) {
206 curTok1 = strtok_r(curTok, ":", &saveptr1);
207 newHeader = (struct MHD_HTTP_Header *)malloc(sizeof(struct MHD_HTTP_Header));
208 if(newHeader == NULL) {
209 if(session->daemon->options & MHD_USE_DEBUG)
210 fprintf(stderr, "Error allocating memory!\n");
211 return -1;
212 }
213 newHeader->header = curTok1;
214 newHeader->value = curTok + strlen(curTok1) + 2;
215 //For now, everything is a get!
216 newHeader->kind = MHD_GET_ARGUMENT_KIND;
217 session->headers[session->firstFreeHeader++] = newHeader;
218 curTok = strtok_r(NULL, crlf, &saveptr);
219 }
220
221 return numBytes;
222}
223
224
225/**
226 * This function needs to do a lot more (i.e. break up get arguments)
227 * but for now just seperates the prefix of the url from the document
228 * portion.
229 */
230static void
231MHD_parse_URL(struct MHD_Session * session) {
232 char * working;
233 int pos,i;
234
235 working = session->headers[0]->value;
236
237 pos = 0;
238 for(i = 0; i < strlen(working); i++) {
239 if(working[i] == '/')
240 pos = i+1;
241 }
242 if(pos >= strlen(working))
243 pos = 0;
244
245 session->documentName = session->headers[0]->value+pos;
246}
247
248/**
249 * This function handles a particular connection when it has been
250 * determined that there is data to be read off a socket. All implementations
251 * (multithreaded, external select, internal select) call this function
252 * to handle reads.
253 */
254static int
255MHD_session_handle_read(struct MHD_Session * session) {
256 int bytes_read,i;
257
258 if((daemon->options & MHD_USE_DEBUG) != 0) {
259 fprintf(stderr, "Enter MHD_handle_read\n");
260 }
261
262 if(daemon == NULL || daemon->connections[connection_id]==NULL) {
263 return MHD_NO;
264 }
265
266 if(daemon->connections[connection_id]->responsePending) {
267 return MHD_YES;
268 }
269
270 daemon->connections[connection_id]->firstFreeHeader = 0;
271 daemon->connections[connection_id]->requestType = NULL;
272
273 for(i = 0; i < MHD_MAX_HEADERS; i++) {
274 daemon->connections[connection_id]->headers[i] = NULL;
275 }
276
277
278
279 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);
280
281 memset(daemon->connections[connection_id]->inbuf + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos,
282 0, MHD_MAX_BUF_SIZE - daemon->connections[connection_id]->bufPos + (daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos));
283
284 bytes_read = recv(daemon->connections[connection_id]->socket_fd,
285 daemon->connections[connection_id]->inbuf + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos,
286 MHD_MAX_BUF_SIZE - (daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos), 0);
287
288 daemon->connections[connection_id]->bufPos = bytes_read + daemon->connections[connection_id]->bufPos - daemon->connections[connection_id]->messagePos;
289
290 if(bytes_read == 0) {
291 MHD_destroy_session(daemon->connections[connection_id]);
292 daemon->connections[connection_id] = NULL;
293 return MHD_NO;
294 } else {
295 fprintf(stderr, "\"%s\"\n", daemon->connections[connection_id]->inbuf);
296 i = MHD_parse_message(daemon->connections[connection_id]);
297 if(i == -1) {
298 daemon->connections[connection_id]->messagePos = daemon->connections[connection_id]->bufPos;
299 return MHD_YES;
300 } else {
301 daemon->connections[connection_id]->messagePos = i;
302 fprintf(stderr, "Number of bytes in header: %i\n", daemon->connections[connection_id]->messagePos);
303 }
304
305 daemon->connections[connection_id]->responsePending = 1;
306
307 MHD_parse_URL(daemon->connections[connection_id]);
308
309 for(i = 0; i < MHD_MAX_HANDLERS; i++) {
310 if(daemon->handlers[i] == NULL)
311 continue;
312
313 //header 0 will hold the url of the request
314 if(strstr(daemon->connections[connection_id]->headers[0]->value, daemon->handlers[i]->uri_prefix) != NULL){
315 return daemon->handlers[i]->dh(daemon->handlers[i]->dh_cls, daemon->connections[connection_id],
316 daemon->connections[connection_id]->documentName, daemon->connections[connection_id]->requestType, NULL, NULL);
317 }
318 }
319 return daemon->dh(daemon->dh_cls, daemon->connections[connection_id],
320 daemon->connections[connection_id]->documentName, daemon->connections[connection_id]->requestType, NULL, NULL);
321 }
322
323 return MHD_YES;
324}
325
326
327/**
328 * This function was created to handle writes to sockets when it has been
329 * determined that the socket can be written to. If there is no data
330 * to be written, however, the function call does nothing. All implementations
331 * (multithreaded, external select, internal select) call this function
332 */
333int
334MHD_session_handle_write(struct MHD_Session * session) {
335 struct MHD_Session * session;
336
337 struct MHD_Response * response;
338
339 int i;
340
341 char * buffer[2048];
342
343 char * responseMessage;
344 int numBytesInMessage;
345
346 if((daemon->options & MHD_USE_DEBUG) != 0) {
347 fprintf(stderr, "Enter MHD_handle_write\n");
348 }
349
350
351 session = daemon->connections[connection_id];
352
353 response = session->currentResponses[session->currentResponse];
354
355 numBytesInMessage = 25;
356
357 responseMessage = malloc(25);
358 if(responseMessage == NULL) {
359 if(daemon->options & MHD_USE_DEBUG)
360 fprintf(stderr, "Error allocating memory!\n");
361 return MHD_NO;
362 }
363
364 if(response == NULL)
365 return MHD_NO;
366
367 pthread_mutex_lock(&response->mutex);
368
369 if(!response->headersSent) {
370 sprintf(responseMessage, "HTTP/1.1 %i Go to hell!\r\n", response->responseCode);
371 fprintf(stderr, "%s\n", responseMessage);
372 if(send(session->socket_fd, responseMessage, strlen(responseMessage), 0) != strlen(responseMessage)) {
373 fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n");
374 pthread_mutex_unlock(&response->mutex);
375 return MHD_NO;
376 }
377
378 for(i = 0; i < MHD_MAX_HEADERS; i++) {
379 if(response->headers[i] == NULL)
380 continue;
381
382 if(strlen(response->headers[i]->header) + strlen(response->headers[i]->value) + 5 > numBytesInMessage) {
383 free(responseMessage);
384 responseMessage = malloc(strlen(response->headers[i]->header) + strlen(response->headers[i]->value) + 5);
385 if(responseMessage == NULL) {
386 if(daemon->options & MHD_USE_DEBUG)
387 fprintf(stderr, "Error allocating memory!\n");
388 pthread_mutex_unlock(&response->mutex);
389 return MHD_NO;
390 }
391 numBytesInMessage = strlen(response->headers[i]->header) + strlen(response->headers[i]->value) + 5;
392 }
393 sprintf(responseMessage, "%s: %s\r\n", response->headers[i]->header, response->headers[i]->value);
394 fprintf(stderr, "%s\n", responseMessage);
395 if(send(session->socket_fd, responseMessage, strlen(responseMessage), 0) != strlen(responseMessage)) {
396 fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n");
397 pthread_mutex_unlock(&response->mutex);
398 return MHD_NO;
399 }
400 }
401
402 response->headersSent = 1;
403 }
404
405 if(response->data != NULL) {
406 if(response->bytesSentSoFar == 0) {
407 if(numBytesInMessage < 32) {
408 free(responseMessage);
409 responseMessage = malloc(32);
410 if(responseMessage == NULL) {
411 if(daemon->options & MHD_USE_DEBUG)
412 fprintf(stderr, "Error allocating memory!\n");
413 pthread_mutex_unlock(&response->mutex);
414 return MHD_NO;
415 }
416 }
417 sprintf(responseMessage, "Content-length: %llu\r\n\r\n", (unsigned long long)response->size);
418 fprintf(stderr, "%s\n", responseMessage);
419 if(send(session->socket_fd, responseMessage, strlen(responseMessage),0)!= strlen(responseMessage)) {
420 fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n");
421 pthread_mutex_unlock(&response->mutex);
422 return MHD_NO;
423 }
424 }
425
426 i = send(session->socket_fd, response->data+response->bytesSentSoFar, response->size-response->bytesSentSoFar,0);
427 response->bytesSentSoFar += i;
428
429 fprintf(stderr, "Sent %i bytes of data\nTotal to send is %llu bytes\n", i, (unsigned long long)response->size);
430
431 if(response->bytesSentSoFar == response->size) {
432 session->currentResponses[session->currentResponse] = NULL;
433 session->currentResponse = (session->currentResponse + 1) % MHD_MAX_RESPONSE;
434 response->currentSession = NULL;
435
436 if(response->freeWhenFinished) {
437 pthread_mutex_unlock(&response->mutex);
438 MHD_destroy_response(response);
439 }
440 /*THIS NEEDS TO BE HANDLED ANOTHER WAY!!! TIMEOUT, ect..., as of now this is the only way to get test case to work
441 * since client never disconnects on their own!
442 */
443 if(session->currentResponses[session->currentResponse] == NULL) {
444 MHD_destroy_session(session);
445 daemon->connections[connection_id] = NULL;
446 return MHD_NO;
447 }
448 }
449 } else {
450 if(response->crc == NULL) {
451 pthread_mutex_unlock(&response->mutex);
452 return MHD_NO;
453 }
454
455 if(response->bytesSentSoFar == 0) {
456 if(send(session->socket_fd, "\r\n", response->size,0) != 2) {
457 fprintf(stderr, "Error! could not send an entire header in one call to send! unable to handle this case as of this time.\n");
458 pthread_mutex_unlock(&response->mutex);
459 return MHD_NO;
460 }
461 }
462 memset(buffer, 0, 2048);
463
464 i = response->crc(response->crc_cls, response->bytesSentSoFar, (char *)buffer, 2048);
465
466 if(i == -1) {
467 pthread_mutex_unlock(&response->mutex);
468
469 session->currentResponses[session->currentResponse] = NULL;
470 session->currentResponse = (session->currentResponse + 1) % MHD_MAX_RESPONSE;
471 response->currentSession = NULL;
472
473 if(response->freeWhenFinished) {
474 pthread_mutex_unlock(&response->mutex);
475 MHD_destroy_response(response);
476 }
477 /*THIS NEEDS TO BE HANDLED ANOTHER WAY!!! TIMEOUT, ect..., as of now this is the only way to get test case to work
478 * since client never disconnects on their own!
479 */
480 if(session->currentResponses[session->currentResponse] == NULL) {
481 MHD_destroy_session(session);
482 daemon->connections[connection_id] = NULL;
483 return MHD_NO;
484 }
485
486 } else {
487 i = send(session->socket_fd, buffer, i,0);
488 response->bytesSentSoFar += i;
489 }
490 }
491 pthread_mutex_unlock(&response->mutex);
492 return MHD_YES;
493}
494
495
496
497
498
499
500
501
502
503/**
504 * @return -1 if no data uploaded; otherwise number of bytes
505 * read into buf; 0 for end of transmission
506 * Specification not complete at this time.
507 */
508int
509MHD_read_file_upload(struct MHD_Session * session,
510 void * buf,
511 size_t len) {
512 return -1; /* FIXME: not implemented */
513}
514
515
diff --git a/src/daemon/session.h b/src/daemon/session.h
new file mode 100644
index 00000000..5e8be40c
--- /dev/null
+++ b/src/daemon/session.h
@@ -0,0 +1,154 @@
1/*
2 This file is part of libmicrohttpd
3 (C) 2007 Daniel Pittman
4
5 libmicrohttpd is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 libmicrohttpd is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with libmicrohttpd; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file session.h
23 * @brief Methods for managing sessions
24 * @author Daniel Pittman
25 * @author Christian Grothoff
26 * @version 0.1.0
27 */
28
29#ifndef SESSION_H
30#define SESSION_H
31
32
33struct MHD_Session {
34 struct MHD_Session * next;
35
36 struct MHD_Daemon * daemon;
37
38 struct MHD_HTTP_Header * headers_received;
39
40 struct MHD_Response * response;
41
42 char * requestType;
43
44 /**
45 * Buffer for reading requests.
46 */
47 void * read_buffer;
48
49 /**
50 * Buffer for writing response.
51 */
52 void * write_buffer;
53
54 /**
55 * Foreign address (of length addr_len).
56 */
57 struct sockaddr_in * addr;
58
59 /**
60 * Thread for this session (if we are using
61 * one thread per connection).
62 */
63 pthread_t pid;
64
65 size_t read_buffer_size;
66
67 size_t readLoc;
68
69 size_t write_buffer_size;
70
71 size_t writeLoc;
72
73 /**
74 * Current write position in the actual response
75 * (excluding headers, content only; should be 0
76 * while sending headers).
77 */
78 size_t messagePos;
79
80 /**
81 * Length of the foreign address.
82 */
83 socklen_t addr_len;
84
85 /**
86 * Socket for this connection. Set to -1 if
87 * this connection has died (daemon should clean
88 * up in that case).
89 */
90 int socket_fd;
91
92 /**
93 * Have we finished receiving all of the headers yet?
94 * Set to 1 once we are done processing all of the
95 * headers. Note that due to pipelining, it is
96 * possible that the NEXT request is already
97 * (partially) waiting in the read buffer.
98 */
99 int headersReceived;
100
101 /**
102 * Have we finished receiving the data from a
103 * potential file-upload?
104 */
105 int bodyReceived;
106
107 /**
108 * Have we finished sending all of the headers yet?
109 */
110 int headersSent;
111
112 /**
113 * HTTP response code. Only valid if response object
114 * is already set.
115 */
116 unsigned int responseCode;
117
118};
119
120
121/**
122 * Obtain the select sets for this session.
123 *
124 * @return MHD_YES on success
125 */
126int
127MHD_session_get_fdset(struct MHD_Session * session,
128 fd_set * read_fd_set,
129 fd_set * write_fd_set,
130 fd_set * except_fd_set,
131 int * max_fd);
132
133
134/**
135 * This function handles a particular connection when it has been
136 * determined that there is data to be read off a socket. All implementations
137 * (multithreaded, external select, internal select) call this function
138 * to handle reads.
139 */
140int
141MHD_session_handle_read(struct MHD_Session * session);
142
143
144/**
145 * This function was created to handle writes to sockets when it has been
146 * determined that the socket can be written to. If there is no data
147 * to be written, however, the function call does nothing. All implementations
148 * (multithreaded, external select, internal select) call this function
149 */
150int
151MHD_session_handle_write(struct MHD_Session * session);
152
153
154#endif