1 |
#include "request.h" |
2 |
#include "freehttpd.h" |
3 |
#include <ctype.h> |
4 |
#include <stdio.h> |
5 |
#include <stdlib.h> |
6 |
#include <string.h> |
7 |
#include <sys/socket.h> |
8 |
#include <unistd.h> |
9 |
|
10 |
freehttpd_request_t * |
11 |
freehttpd_request_init (const char *method, const char *uri, |
12 |
const char *version) |
13 |
{ |
14 |
freehttpd_request_t *request = calloc (1, sizeof (freehttpd_request_t)); |
15 |
|
16 |
if (request == NULL) |
17 |
return NULL; |
18 |
|
19 |
if (method != NULL) |
20 |
{ |
21 |
request->method = strdup (method); |
22 |
request->method_length = strlen (method); |
23 |
} |
24 |
|
25 |
if (uri != NULL) |
26 |
{ |
27 |
request->uri = strdup (uri); |
28 |
request->uri_length = strlen (uri); |
29 |
} |
30 |
|
31 |
if (version != NULL) |
32 |
{ |
33 |
|
34 |
request->version = strdup (version); |
35 |
request->version_length = strlen (version); |
36 |
} |
37 |
|
38 |
return request; |
39 |
} |
40 |
|
41 |
freehttpd_header_t * |
42 |
freehttpd_header_init (const char *name, const char *value, size_t name_length, |
43 |
size_t value_length) |
44 |
{ |
45 |
freehttpd_header_t *header = calloc (1, sizeof (freehttpd_header_t)); |
46 |
|
47 |
if (header == NULL) |
48 |
return NULL; |
49 |
|
50 |
freehttpd_header_t on_stack |
51 |
= freehttpd_header_init_stack (name, value, name_length, value_length); |
52 |
memcpy (header, &on_stack, sizeof (freehttpd_header_t)); |
53 |
|
54 |
return header; |
55 |
} |
56 |
|
57 |
freehttpd_header_t |
58 |
freehttpd_header_init_stack (const char *name, const char *value, |
59 |
size_t name_length, size_t value_length) |
60 |
{ |
61 |
freehttpd_header_t header = { 0 }; |
62 |
|
63 |
if (name != NULL) |
64 |
{ |
65 |
header.name = strdup (name); |
66 |
header.name_length = name_length == 0 ? strlen (name) : name_length; |
67 |
} |
68 |
|
69 |
if (value != NULL) |
70 |
{ |
71 |
header.value = strdup (value); |
72 |
header.value_length |
73 |
= value_length == 0 ? strlen (value) : value_length; |
74 |
} |
75 |
|
76 |
return header; |
77 |
} |
78 |
|
79 |
void |
80 |
freehttpd_header_free (freehttpd_header_t *header) |
81 |
{ |
82 |
if (header == NULL) |
83 |
return; |
84 |
|
85 |
if (header->name != NULL) |
86 |
free (header->name); |
87 |
|
88 |
if (header->value != NULL) |
89 |
free (header->value); |
90 |
|
91 |
free (header); |
92 |
} |
93 |
|
94 |
void |
95 |
freehttpd_request_free (freehttpd_request_t *request) |
96 |
{ |
97 |
if (request == NULL) |
98 |
return; |
99 |
|
100 |
if (request->method != NULL) |
101 |
free (request->method); |
102 |
|
103 |
if (request->uri != NULL) |
104 |
free (request->uri); |
105 |
|
106 |
if (request->version != NULL) |
107 |
free (request->version - 5); |
108 |
|
109 |
if (request->path != NULL) |
110 |
free (request->path); |
111 |
|
112 |
if (request->query != NULL) |
113 |
free (request->query); |
114 |
|
115 |
if (request->headers != NULL) |
116 |
{ |
117 |
for (size_t i = 0; i < request->headers_count; i++) |
118 |
freehttpd_header_free (request->headers[i]); |
119 |
|
120 |
free (request->headers); |
121 |
} |
122 |
|
123 |
if (request->body != NULL) |
124 |
free (request->body); |
125 |
|
126 |
free (request); |
127 |
} |
128 |
|
129 |
static char * |
130 |
urldecode (const char *input, size_t *len) |
131 |
{ |
132 |
char *output = calloc (strlen (input) + 1, 1); |
133 |
|
134 |
if (output == NULL) |
135 |
return NULL; |
136 |
|
137 |
size_t i = 0, j = 0; |
138 |
|
139 |
for (; input[i] != 0; i++, j++) |
140 |
{ |
141 |
if (input[i] == '%') |
142 |
{ |
143 |
if (input[i + 1] == 0 || input[i + 2] == 0) |
144 |
{ |
145 |
free (output); |
146 |
return NULL; |
147 |
} |
148 |
|
149 |
char c1 = tolower (input[i + 1]); |
150 |
char c2 = tolower (input[i + 2]); |
151 |
int c1n = isdigit (c1) ? c1 - '0' : c1 - 'a' + 10; |
152 |
int c2n = isdigit (c2) ? c2 - '0' : c2 - 'a' + 10; |
153 |
int code = c1n * 16 + c2n; |
154 |
output[j] = (char) code; |
155 |
i += 2; |
156 |
} |
157 |
else |
158 |
output[j] = input[i]; |
159 |
} |
160 |
|
161 |
output[j] = 0; |
162 |
|
163 |
printf ("output: %s\n", output); |
164 |
|
165 |
if (len != NULL) |
166 |
*len = j; |
167 |
|
168 |
return output; |
169 |
} |
170 |
|
171 |
const char *const SUPPORTED_METHODS[] |
172 |
= { "GET", "POST", "PUT", "DELETE", "HEAD", |
173 |
"OPTIONS", "TRACE", "CONNECT", NULL }; |
174 |
|
175 |
freehttpd_request_t * |
176 |
freehttpd_request_parse (freehttpd_t *freehttpd, int sockfd, ecode_t *error) |
177 |
{ |
178 |
freehttpd_request_t *request = freehttpd_request_init (NULL, NULL, NULL); |
179 |
|
180 |
if (request == NULL) |
181 |
{ |
182 |
*error = E_LIBC_MALLOC; |
183 |
return NULL; |
184 |
} |
185 |
|
186 |
const freehttpd_config_t *config = freehttpd_get_config (freehttpd); |
187 |
char *buf[3] = { 0 }; |
188 |
char **buf_ptrs[] = { &request->method, &request->uri, &request->version }; |
189 |
size_t max_lengths[] = { config->max_method_len, config->max_uri_len, |
190 |
config->max_version_len }; |
191 |
size_t *length_ptrs[] = { &request->method_length, &request->uri_length, |
192 |
&request->version_length }; |
193 |
|
194 |
for (int i = 0; i < 3; i++) |
195 |
{ |
196 |
char c; |
197 |
size_t j = 0; |
198 |
|
199 |
/* TODO: instead of always calling read with size 1, we could |
200 |
optimize this by reading more bytes at once and then parsing |
201 |
the buffer. */ |
202 |
while (recv (sockfd, &c, 1, 0) == 1) |
203 |
{ |
204 |
if (isspace (c)) |
205 |
break; |
206 |
|
207 |
if (j >= max_lengths[i]) |
208 |
{ |
209 |
freehttpd_request_free (request); |
210 |
*error = E_MALFORMED_REQUEST; |
211 |
return NULL; |
212 |
} |
213 |
|
214 |
buf[i] = realloc (buf[i], j + 1); |
215 |
buf[i][j++] = c; |
216 |
} |
217 |
|
218 |
if (j == 0) |
219 |
{ |
220 |
freehttpd_request_free (request); |
221 |
*error = E_MALFORMED_REQUEST; |
222 |
return NULL; |
223 |
} |
224 |
|
225 |
buf[i] = realloc (buf[i], j + 1); |
226 |
buf[i][j] = 0; |
227 |
*length_ptrs[i] = j; |
228 |
*buf_ptrs[i] = buf[i]; |
229 |
} |
230 |
|
231 |
for (size_t i = 0; SUPPORTED_METHODS[i] != NULL; i++) |
232 |
{ |
233 |
if (strcmp (buf[0], SUPPORTED_METHODS[i]) == 0) |
234 |
{ |
235 |
request->method = buf[0]; |
236 |
break; |
237 |
} |
238 |
} |
239 |
|
240 |
if (request->method == NULL || request->uri == NULL |
241 |
|| request->version == NULL) |
242 |
{ |
243 |
freehttpd_request_free (request); |
244 |
*error = E_MALFORMED_REQUEST; |
245 |
return NULL; |
246 |
} |
247 |
|
248 |
if (memcmp (request->version, "HTTP/", 5) != 0) |
249 |
{ |
250 |
freehttpd_request_free (request); |
251 |
*error = E_MALFORMED_REQUEST; |
252 |
return NULL; |
253 |
} |
254 |
|
255 |
request->version += 5; |
256 |
request->version_length -= 5; |
257 |
|
258 |
char *path = strchr (request->uri, '?'); |
259 |
|
260 |
if (path != NULL) |
261 |
{ |
262 |
path = strndup (request->uri, path - request->uri); |
263 |
request->query = strdup (path + 1); |
264 |
request->query_length |
265 |
= request->uri_length - request->path_length - 1; |
266 |
} |
267 |
else |
268 |
path = strdup (request->uri); |
269 |
|
270 |
request->path = urldecode (path, &request->path_length); |
271 |
free (path); |
272 |
*error = E_OK; |
273 |
return request; |
274 |
} |