diff options
Diffstat (limited to 'src/daemon/session.c')
-rw-r--r-- | src/daemon/session.c | 515 |
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 | */ | ||
55 | int | ||
56 | MHD_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 | */ | ||
90 | const char * | ||
91 | MHD_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 | */ | ||
120 | int | ||
121 | MHD_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 | */ | ||
140 | int | ||
141 | MHD_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 | */ | ||
164 | static int | ||
165 | MHD_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 | */ | ||
230 | static void | ||
231 | MHD_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 | */ | ||
254 | static int | ||
255 | MHD_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 | */ | ||
333 | int | ||
334 | MHD_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 | */ | ||
508 | int | ||
509 | MHD_read_file_upload(struct MHD_Session * session, | ||
510 | void * buf, | ||
511 | size_t len) { | ||
512 | return -1; /* FIXME: not implemented */ | ||
513 | } | ||
514 | |||
515 | |||