diff options
author | Christian Grothoff <christian@grothoff.org> | 2007-06-12 06:48:11 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2007-06-12 06:48:11 +0000 |
commit | 641853be5073b9b2dc5c60fa414271ccbec0c0aa (patch) | |
tree | 7da8c1810f45ea5cd07923808ed158662c1aadc6 | |
parent | c09343fb06be96edac01bbe48d8a269d898c91fd (diff) | |
download | libmicrohttpd-641853be5073b9b2dc5c60fa414271ccbec0c0aa.tar.gz libmicrohttpd-641853be5073b9b2dc5c60fa414271ccbec0c0aa.zip |
cleaning up the code big time -- incomplete
-rw-r--r-- | INSTALL | 69 | ||||
-rw-r--r-- | src/daemon/Makefile.am | 5 | ||||
-rw-r--r-- | src/daemon/daemon.c | 1817 | ||||
-rw-r--r-- | src/daemon/internal.h | 111 | ||||
-rw-r--r-- | src/daemon/response.c | 333 | ||||
-rw-r--r-- | src/daemon/response.h | 39 | ||||
-rw-r--r-- | src/daemon/session.c | 515 | ||||
-rw-r--r-- | src/daemon/session.h | 154 |
8 files changed, 1583 insertions, 1460 deletions
@@ -1,16 +1,13 @@ | |||
1 | Installation Instructions | 1 | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software |
2 | ************************* | 2 | Foundation, Inc. |
3 | 3 | ||
4 | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free | 4 | This file is free documentation; the Free Software Foundation gives |
5 | Software Foundation, Inc. | ||
6 | |||
7 | This file is free documentation; the Free Software Foundation gives | ||
8 | unlimited permission to copy, distribute and modify it. | 5 | unlimited permission to copy, distribute and modify it. |
9 | 6 | ||
10 | Basic Installation | 7 | Basic Installation |
11 | ================== | 8 | ================== |
12 | 9 | ||
13 | These 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 |
16 | various system-dependent variables used during compilation. It uses | 13 | various system-dependent variables used during compilation. It uses |
@@ -70,9 +67,9 @@ The simplest way to compile this package is: | |||
70 | Compilers and Options | 67 | Compilers and Options |
71 | ===================== | 68 | ===================== |
72 | 69 | ||
73 | Some 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 | 71 | the `configure' script does not know about. Run `./configure --help' |
75 | details on some of the pertinent environment variables. | 72 | for 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 |
78 | by setting variables in the command line or in the environment. Here | 75 | by setting variables in the command line or in the environment. Here |
@@ -85,7 +82,7 @@ is an example: | |||
85 | Compiling For Multiple Architectures | 82 | Compiling For Multiple Architectures |
86 | ==================================== | 83 | ==================================== |
87 | 84 | ||
88 | You 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 |
89 | same time, by placing the object files for each architecture in their | 86 | same time, by placing the object files for each architecture in their |
90 | own directory. To do this, you must use a version of `make' that | 87 | own directory. To do this, you must use a version of `make' that |
91 | supports the `VPATH' variable, such as GNU `make'. `cd' to the | 88 | supports the `VPATH' variable, such as GNU `make'. `cd' to the |
@@ -102,19 +99,19 @@ for another architecture. | |||
102 | Installation Names | 99 | Installation Names |
103 | ================== | 100 | ================== |
104 | 101 | ||
105 | By 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 |
107 | can specify an installation prefix other than `/usr/local' by giving | 104 | installation prefix other than `/usr/local' by giving `configure' the |
108 | `configure' the option `--prefix=PREFIX'. | 105 | option `--prefix=PATH'. |
109 | 106 | ||
110 | You can specify separate installation prefixes for | 107 | You can specify separate installation prefixes for |
111 | architecture-specific files and architecture-independent files. If you | 108 | architecture-specific files and architecture-independent files. If you |
112 | pass the option `--exec-prefix=PREFIX' to `configure', the package uses | 109 | give `configure' the option `--exec-prefix=PATH', the package will use |
113 | PREFIX as the prefix for installing programs and libraries. | 110 | PATH as the prefix for installing programs and libraries. |
114 | Documentation and other data files still use the regular prefix. | 111 | Documentation 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 |
117 | options like `--bindir=DIR' to specify different values for particular | 114 | options like `--bindir=PATH' to specify different values for particular |
118 | kinds of files. Run `configure --help' for a list of the directories | 115 | kinds of files. Run `configure --help' for a list of the directories |
119 | you can set and what kinds of files go in them. | 116 | you can set and what kinds of files go in them. |
120 | 117 | ||
@@ -125,7 +122,7 @@ option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. | |||
125 | Optional Features | 122 | Optional Features |
126 | ================= | 123 | ================= |
127 | 124 | ||
128 | Some 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. |
130 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE | 127 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE |
131 | is something like `gnu-as' or `x' (for the X Window System). The | 128 | is 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 | |||
140 | Specifying the System Type | 137 | Specifying the System Type |
141 | ========================== | 138 | ========================== |
142 | 139 | ||
143 | There may be some features `configure' cannot figure out automatically, | 140 | There may be some features `configure' cannot figure out |
144 | but needs to determine by the type of machine the package will run on. | 141 | automatically, but needs to determine by the type of machine the package |
145 | Usually, assuming the package is built to be run on the _same_ | 142 | will run on. Usually, assuming the package is built to be run on the |
146 | architectures, `configure' can figure that out, but if it prints a | 143 | _same_ architectures, `configure' can figure that out, but if it prints |
147 | message saying it cannot guess the machine type, give it the | 144 | a 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 |
149 | type, such as `sun4', or a canonical name which has the form: | 146 | type, 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: | |||
159 | need to know the machine type. | 156 | need 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 |
162 | use the option `--target=TYPE' to select the type of system they will | 159 | use the `--target=TYPE' option to select the type of system they will |
163 | produce code for. | 160 | produce 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'. | |||
170 | Sharing Defaults | 167 | Sharing Defaults |
171 | ================ | 168 | ================ |
172 | 169 | ||
173 | If 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, |
174 | can create a site shell script called `config.site' that gives default | 171 | you can create a site shell script called `config.site' that gives |
175 | values for variables like `CC', `cache_file', and `prefix'. | 172 | default 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. | |||
181 | Defining Variables | 178 | Defining Variables |
182 | ================== | 179 | ================== |
183 | 180 | ||
184 | Variables 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 |
185 | environment passed to `configure'. However, some packages may run | 182 | environment passed to `configure'. However, some packages may run |
186 | configure again during the build, and the customized values of these | 183 | configure again during the build, and the customized values of these |
187 | variables may be lost. In order to avoid this problem, you should set | 184 | variables 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 | ||
192 | causes the specified `gcc' to be used as the C compiler (unless it is | 189 | will cause the specified gcc to be used as the C compiler (unless it is |
193 | overridden in the site shell script). Here is a another example: | 190 | overridden in the site shell script). |
194 | |||
195 | /bin/bash ./configure CONFIG_SHELL=/bin/bash | ||
196 | |||
197 | Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent | ||
198 | configuration-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 |
196 | operates. | ||
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 |
12 | libmicrohttpd_la_SOURCES = \ | 12 | libmicrohttpd_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 |
16 | if HAVE_CURL | 19 | if 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 | |||
48 | int MHD_handle_read(int, struct MHD_Daemon *); | ||
49 | int MHD_handle_write(int, struct MHD_Daemon *); | ||
50 | void * MHD_spawn_connections(void * data); | ||
51 | void * MHD_select(void * data); | ||
52 | int MHD_parse_message(struct MHD_Session * session); | ||
53 | void MHD_parse_URL(struct MHD_Session * session); | ||
54 | |||
55 | struct 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 | |||
80 | struct 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 | |||
107 | struct 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 | |||
132 | struct MHD_Access_Handler { | ||
133 | char * uri_prefix; | ||
134 | MHD_AccessHandlerCallback dh; | ||
135 | void * dh_cls; | ||
136 | }; | ||
137 | 48 | ||
138 | struct 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 | */ |
149 | int | 54 | static void DLOG(const struct MHD_Daemon * daemon, |
150 | MHD_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 | */ | ||
239 | int | ||
240 | MHD_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 | */ |
330 | struct MHD_Response * | 74 | int |
331 | MHD_create_response_from_callback(size_t size, | 75 | MHD_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 | */ | ||
399 | struct MHD_Response * | ||
400 | MHD_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 |
468 | int | 108 | * is not known for this daemon |
469 | MHD_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 | */ | ||
498 | void | ||
499 | MHD_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 | */ |
541 | void | 110 | int |
542 | MHD_destroy_session(struct MHD_Session * session) { | 111 | MHD_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 | */ | ||
612 | int | ||
613 | MHD_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 | */ | ||
642 | int | ||
643 | MHD_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 | */ |
670 | void * | 186 | static void * |
671 | MHD_handle_connection(void * data) { | 187 | MHD_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 | */ | ||
720 | void | ||
721 | MHD_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 | */ | ||
731 | int | ||
732 | MHD_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 | */ |
809 | int | 234 | static int |
810 | MHD_handle_write(int connection_id, struct MHD_Daemon * daemon) { | 235 | MHD_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 | */ | ||
979 | const char * | ||
980 | MHD_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 | */ |
1002 | int | 295 | static void |
1003 | MHD_parse_message(struct MHD_Session * session) { | 296 | MHD_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 | */ | ||
1067 | void | ||
1068 | MHD_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 | */ |
1095 | int | 323 | static int |
1096 | MHD_queue_response(struct MHD_Session * session, | 324 | MHD_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 | */ | ||
1139 | int | ||
1140 | MHD_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 | */ |
1157 | int | 393 | int |
1158 | MHD_run(struct MHD_Daemon * daemon) { | 394 | MHD_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 | */ |
1188 | int | 409 | static void * |
1189 | MHD_register_handler(struct MHD_Daemon * daemon, | 410 | MHD_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 | */ | ||
1244 | void * | ||
1245 | MHD_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 | */ | ||
1305 | void * | ||
1306 | MHD_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 | */ |
1485 | void | 510 | void |
1486 | MHD_stop_daemon(struct MHD_Daemon * daemon) { | 511 | MHD_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 | */ | ||
1538 | int | ||
1539 | MHD_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 | */ | ||
50 | struct 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 | |||
61 | struct 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 | |||
72 | struct 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 | */ | ||
49 | struct 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 | */ | ||
119 | int | ||
120 | MHD_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 | */ | ||
151 | int | ||
152 | MHD_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 | */ | ||
189 | int | ||
190 | MHD_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 | */ | ||
220 | struct MHD_Response * | ||
221 | MHD_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 | */ | ||
257 | struct MHD_Response * | ||
258 | MHD_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 | */ | ||
301 | void | ||
302 | MHD_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 | |||
325 | void | ||
326 | MHD_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 | */ | ||
36 | void 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 | */ | ||
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 | |||
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 | |||
33 | struct 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 | */ | ||
126 | int | ||
127 | MHD_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 | */ | ||
140 | int | ||
141 | MHD_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 | */ | ||
150 | int | ||
151 | MHD_session_handle_write(struct MHD_Session * session); | ||
152 | |||
153 | |||
154 | #endif | ||