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