/[osn-commons]/trunk/freehttpd/request.c
ViewVC logotype

Annotation of /trunk/freehttpd/request.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 46 - (hide annotations)
Mon Aug 12 16:55:32 2024 UTC (7 months, 2 weeks ago) by rakinar2
File MIME type: text/x-c
File size: 7126 byte(s)
feat(freehttpd): better error handling
1 rakinar2 44 #include "request.h"
2     #include "freehttpd.h"
3     #include <ctype.h>
4     #include <stdio.h>
5     #include <stdlib.h>
6     #include <string.h>
7 rakinar2 45 #include <sys/socket.h>
8 rakinar2 44 #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 rakinar2 45 freehttpd_header_init (const char *name, const char *value, size_t name_length,
43     size_t value_length)
44 rakinar2 44 {
45     freehttpd_header_t *header = calloc (1, sizeof (freehttpd_header_t));
46    
47     if (header == NULL)
48     return NULL;
49    
50 rakinar2 46 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 rakinar2 44 if (name != NULL)
64 rakinar2 45 {
65 rakinar2 46 header.name = strdup (name);
66     header.name_length = name_length == 0 ? strlen (name) : name_length;
67 rakinar2 45 }
68 rakinar2 44
69     if (value != NULL)
70 rakinar2 45 {
71 rakinar2 46 header.value = strdup (value);
72     header.value_length
73 rakinar2 45 = value_length == 0 ? strlen (value) : value_length;
74     }
75 rakinar2 44
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 rakinar2 45 free (request->version - 5);
108 rakinar2 44
109 rakinar2 45 if (request->path != NULL)
110     free (request->path);
111    
112     if (request->query != NULL)
113     free (request->query);
114    
115 rakinar2 44 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 rakinar2 45 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 rakinar2 44 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 rakinar2 45 /* 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 rakinar2 44 {
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 rakinar2 45 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 rakinar2 44 *error = E_OK;
273     return request;
274     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26