aboutsummaryrefslogtreecommitdiff
path: root/src/daemon/session.c
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 /src/daemon/session.c
parentc09343fb06be96edac01bbe48d8a269d898c91fd (diff)
downloadlibmicrohttpd-641853be5073b9b2dc5c60fa414271ccbec0c0aa.tar.gz
libmicrohttpd-641853be5073b9b2dc5c60fa414271ccbec0c0aa.zip
cleaning up the code big time -- incomplete
Diffstat (limited to 'src/daemon/session.c')
-rw-r--r--src/daemon/session.c515
1 files changed, 515 insertions, 0 deletions
diff --git a/src/daemon/session.c b/src/daemon/session.c
new file mode 100644
index 00000000..9d17f12f
--- /dev/null
+++ b/src/daemon/session.c
@@ -0,0 +1,515 @@
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