manage content-encoding header
[webserver.git] / http.c
1 #include <malloc.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <time.h>
5
6 #include "debug.h"
7 #include "file.h"
8
9 #include "http.h"
10
11 #define BUFFER_SIZE 128
12
13 #define HTTP_VERSION "HTTP/1.0"
14 #define SERVER_NAME "Webserver/0.0.1"
15
16 char *codes[15] = {
17 "200 OK",
18 "201 Created",
19 "202 Accepted",
20 "204 No Content",
21 "301 Moved Permanently",
22 "302 Moved Temporarily",
23 "304 Not Modified",
24 "400 Bad Request",
25 "401 Unauthorized",
26 "403 Forbidden",
27 "404 Not Found",
28 "500 Internal Server Error",
29 "501 Not Implemented",
30 "502 Bad Gateway",
31 "503 Service Unavailable"};
32
33 typedef enum {
34 c200 = 0, c201, c202, c204,
35 c301, c302, c304, c400,
36 c401, c403, c404, c500,
37 c501, c502, c503
38 } code_t;
39
40 typedef enum {
41 not_supported_e = 0, get_e, head_e, post_e
42 } method_t;
43
44 typedef enum {
45 encoding_plain_e = 0, encoding_gzip_e, encoding_compress_e
46 } encoding_t;
47
48 typedef struct {
49 char *allow;
50 char *authorization;
51 encoding_t content_encoding;
52 char *content_length;
53 char *content_type;
54 char *date;
55 char *expires;
56 char *from;
57 char *if_modified_since;
58 char *last_modified;
59 char *location;
60 char *pragma;
61 char *referer;
62 char *server;
63 char *user_agent;
64 char *www_authenticate;
65 } header_t;
66
67 typedef struct {
68 char *ext;
69 char *type;
70 char *charset;
71 } mime_t;
72
73 #define NB_MIMES 8
74
75 mime_t mimes[NB_MIMES] = {
76 {"js", "application/javascript", "iso-8859-1"},
77 {"css", "text/css", "iso-8859-1"},
78 {"htm", "text/html", "iso-8859-1"},
79 {"html", "text/html", "iso-8859-1"},
80 {"png", "image/png", NULL},
81 {"jpeg", "image/jpeg", NULL},
82 {"jpg", "image/jpeg", NULL},
83 {"txt", "text/plain", "iso-8859-1"}
84 };
85
86 /* find mime type */
87
88 mime_t *find_mime_type (char *filename)
89 {
90 /* find extention */
91 char *ext = filename + strlen (filename);
92 while (--ext > filename) {
93 if (ext[-1] == '.') {
94 break;
95 }
96 }
97 if (ext == filename) {
98 return NULL;
99 }
100
101 /* find mime */
102 int i;
103 for (i = 0; i < NB_MIMES; i++) {
104 if (strcmp (ext, (mimes + i)->ext) == 0) {
105 return mimes + i;
106 }
107 }
108
109 return NULL;
110 }
111
112 /* print header values */
113
114 void print_header_values (header_t *header)
115 {
116 printf ("Header values\n");
117 if (header->allow) printf ("allow = '%s'\n", header->allow);
118 if (header->authorization) printf ("authorization = '%s'\n", header->authorization);
119 if (header->content_encoding) {
120 printf ("content_encoding = ");
121 switch (header->content_encoding) {
122 case encoding_plain_e: printf ("plain\n"); break;
123 case encoding_gzip_e: printf ("gzip\n"); break;
124 case encoding_compress_e: printf ("compress\n"); break;
125 }
126 }
127 if (header->content_length) printf ("content_length = '%s'\n", header->content_length);
128 if (header->content_type) printf ("content_type = '%s'\n", header->content_type);
129 if (header->date) printf ("date = '%s'\n", header->date);
130 if (header->expires) printf ("expires = '%s'\n", header->expires);
131 if (header->from) printf ("from = '%s'\n", header->from);
132 if (header->if_modified_since) printf ("if_modified_since = '%s'\n", header->if_modified_since);
133 if (header->last_modified) printf ("last_modified = '%s'\n", header->last_modified);
134 if (header->location) printf ("location = '%s'\n", header->location);
135 if (header->pragma) printf ("pragma = '%s'\n", header->pragma);
136 if (header->referer) printf ("referer = '%s'\n", header->referer);
137 if (header->server) printf ("server = '%s'\n", header->server);
138 if (header->user_agent) printf ("user_agent = '%s'\n", header->user_agent);
139 if (header->www_authenticate) printf ("www_authenticate = '%s'\n", header->www_authenticate);
140 }
141
142 /* find sequence*/
143
144 char *find_sequence (char *data, int len, char *seq, char **pdata)
145 {
146
147 int size = strlen (seq);
148
149 int i;
150 for (i = 0; i < len - size + 1; i++) {
151 if (strncmp (data + i, seq, size) == 0) {
152 data[i] = 0;
153 if (pdata != NULL) {
154 *pdata = data + i + size;
155 }
156 return data;
157 }
158 }
159
160 return NULL;
161 }
162
163 /* response entity */
164
165 int add_line (char **buffer, char *str)
166 {
167 VERBOSE (DEBUG, PRINT ("add line: %s\n", str));
168 int len = ((*buffer) ? strlen (*buffer) : 0) + strlen (str) + 3;
169 VERBOSE (DEBUG, PRINT ("len: %d\n", len));
170 if (*buffer) {
171 *buffer = (char *)realloc (*buffer, len);
172 } else {
173 *buffer = (char *)calloc (len, 1);
174 }
175 strcat (*buffer, str);
176 strcat (*buffer, "\r\n");
177 return len;
178 }
179
180 int add_status_line (char **buffer, code_t code)
181 {
182 char tmp[BUFFER_SIZE] = {0};
183
184 /* Status */
185 sprintf (tmp, "%s %s", HTTP_VERSION, codes[code]);
186 add_line (buffer, tmp);
187
188 return strlen (*buffer);
189 }
190
191 int add_general_header (char **buffer)
192 {
193 char tmp[BUFFER_SIZE] = {0};
194
195 /* Date */
196 time_t ts = time (NULL);
197 sprintf (tmp, "Date: %s", ctime (&ts));
198 tmp[strlen (tmp) - 1] = 0; // remove last \n
199 add_line (buffer, tmp);
200
201 /* Pragma */
202
203 return strlen (*buffer);
204 }
205
206 int add_response_header (char **buffer, char *uri)
207 {
208 char tmp[BUFFER_SIZE] = {0};
209
210 /* Location */
211 sprintf (tmp, "Location: %s", uri);
212 add_line (buffer, tmp);
213
214 /* Server */
215 sprintf (tmp, "Server: %s", SERVER_NAME);
216 add_line (buffer, tmp);
217
218 /* WWW-Authentificate */
219
220 return strlen (*buffer);
221 }
222
223 int add_entity (char **buffer, char *entity, int size, char *type, char *encoding)
224 {
225 char tmp[BUFFER_SIZE] = {0};
226 int len = strlen (*buffer);
227
228 /* Allow */
229 /* Expires */
230 /* Last-Modified */
231
232 if (entity != NULL) {
233
234 /* Content-Encoding */
235 if (encoding != NULL) {
236 sprintf (tmp, "Content-Encoding: %s", encoding);
237 add_line (buffer, tmp);
238 }
239
240 /* Content-Length */
241 sprintf (tmp, "Content-Length: %d", size);
242 add_line (buffer, tmp);
243
244 /* Content-Type */
245 sprintf (tmp, "Content-Type: %s", type);
246 add_line (buffer, tmp);
247
248 add_line (buffer, "");
249
250 /* Entity */
251 len = strlen (*buffer);
252 *buffer = realloc (*buffer, len + size);
253 memcpy (*buffer + len, entity, size);
254 len += size;
255 }
256
257 return len;
258 }
259
260 /* error 400 */
261
262 int error_400 (char **buffer)
263 {
264 add_status_line (buffer, c400);
265 add_general_header (buffer);
266 add_response_header (buffer, NULL);
267
268 char *response = "<html><head><title>Error 400</title></head><body><p>Bad Request</p></body></html>";
269 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
270 }
271
272 /* error 404 */
273
274 int error_404 (char **buffer, char *uri)
275 {
276 add_status_line (buffer, c404);
277 add_general_header (buffer);
278 add_response_header (buffer, uri);
279
280 char *response = "<html><head><title>Error 404</title></head><body><p>File not found</p></body></html>";
281 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
282 }
283
284 /* generic response */
285
286 int generic_response (char **buffer, char *location, char *response, int size)
287 {
288 int len = 0;
289 VERBOSE (DEBUG, PRINT ("add_status_line %d\n", len));
290 len = add_status_line (buffer, c200);
291 VERBOSE (DEBUG, PRINT ("add_general_header %d\n", len));
292 len = add_general_header (buffer);
293 VERBOSE (DEBUG, PRINT ("add_response_header %d\n", len));
294 len = add_response_header (buffer, location);
295 mime_t *mime = find_mime_type (location);
296
297 VERBOSE (DEBUG, PRINT ("add_entity %d\n", len));
298 return add_entity (buffer, response, size, mime->type, mime->charset);
299 }
300
301 /* trim string */
302
303 char *trim (char *str)
304 {
305 if (str != NULL) {
306 while ((*str == ' ') || (*str == '\t')) {
307 str++;
308 }
309 }
310 return str;
311 }
312
313 /* main HTTP processing */
314
315 int processing (char *data, int len, conf_t *conf, char **pdata)
316 {
317 char *saved_data = data;
318 char location[BUFFER_SIZE] = {0};
319 VERBOSE (DEBUG, PRINT ("Start processing\n"));
320
321 /* check method */
322 char *line = find_sequence (data, len + data - saved_data, "\r\n", &data);
323 if (line == NULL) {
324 VERBOSE (WARNING, PRINT ("Unknown received data\n"));
325 if (pdata != NULL) {
326 *pdata = NULL;
327 }
328 return 0;
329 }
330 VERBOSE (DEBUG, PRINT ("Command line: '%s'\n", line));
331
332 char *method = strtok (line, " ");
333 char *uri = strtok (NULL, " ");
334 char *version = strtok (NULL, " ");
335 method_t type = not_supported_e;
336 if (strcmp (method, "GET") == 0) {
337 type = get_e;
338 } else if (strcmp ("HEAD", method) == 0) {
339 type = head_e;
340 } else if (strcmp ("POST", method) == 0) {
341 type = post_e;
342 } else {
343 VERBOSE (WARNING, PRINT ("Unkown method: %s\n", method));
344 return error_400 (pdata);
345 }
346 VERBOSE (INFO, PRINT ("%s %s (%s)\n", (type == get_e) ? "Get" : (type == head_e) ? "Head" : "Post", uri, version));
347
348 /* analyse uri */
349 char *filename = strtok (uri, "&");
350 char *variables = strtok (NULL, "\n");
351 char *path = (char *) calloc (strlen (conf->root) + strlen (filename) + 2, 1);
352 //sprintf (filename, "%s%s%s", conf->root, ((conf->root[strlen (conf->root) - 1] != '/') && (uri[0] != '/')) ? "/" : "", uri);
353 sprintf (path, "%s/%s", conf->root, filename);
354
355 /* check header */
356 header_t header = {0};
357 while (strcmp (line = find_sequence (data, len + data - saved_data, "\r\n", &data), "") != 0) {
358 VERBOSE (DEBUG, PRINT ("Header line: '%s'\n", line));
359 char *field = strtok (line, ":");
360 char *value = trim (strtok (NULL, "\r"));
361 VERBOSE (DEBUG, PRINT ("Field: %s\nValue: %s\n", field, value));
362 if (*line == 0) {
363 break;
364 }
365
366 VERBOSE (DEBUG, PRINT ("Analyse field\n"));
367 if (strcmp (field, "Allow") == 0) { header.allow = value; }
368 else if (strcmp (field, "Authorization") == 0) { header.authorization = value; }
369 else if (strcmp (field, "Content-Encoding") == 0) {
370 if (strcmp (value, "x-gzip") == 0) {
371 header.content_encoding = encoding_gzip_e;
372 } else if (strcmp (value, "x-compress") == 0) {
373 header.content_encoding = encoding_compress_e;
374 } else {
375 VERBOSE (WARNING, PRINT ("Unknown content encoding: %s\n", value));
376 }
377 } else if (strcmp (field, "Content-Length") == 0) { header.content_length = value; }
378 else if (strcmp (field, "Content-Type") == 0) { header.content_type = value; }
379 else if (strcmp (field, "Date") == 0) { header.date = value; }
380 else if (strcmp (field, "Expires") == 0) { header.expires = value; }
381 else if (strcmp (field, "From") == 0) { header.from = value; }
382 else if (strcmp (field, "If-Modified-Since") == 0) { header.if_modified_since = value; }
383 else if (strcmp (field, "Last-Modified") == 0) { header.last_modified = value; }
384 else if (strcmp (field, "Location") == 0) { header.location = value; }
385 else if (strcmp (field, "Pragma") == 0) { header.pragma = value; }
386 else if (strcmp (field, "Referer") == 0) { header.referer = value; }
387 else if (strcmp (field, "Server") == 0) { header.server = value; }
388 else if (strcmp (field, "User-Agent") == 0) { header.user_agent = value; }
389 else if (strcmp (field, "WWW-Authenticate") == 0) { header.www_authenticate = value; }
390 else { VERBOSE (WARNING, PRINT ("Unknown header field: '%s'\n", field)); }
391 }
392 VERBOSE (DEBUG, print_header_values (&header));
393
394 /* body */
395 char *body = data;
396 len -= saved_data - data;
397 if (len != (header.content_length ? atoi (header.content_length) : 0)) {
398 VERBOSE (WARNING, PRINT ("Incoherent size (%d <> %s)\n", len, header.content_length));
399 }
400 if (len > 0) {
401 switch (header.content_encoding) {
402 case encoding_plain_e: break;
403 case encoding_gzip_e: VERBOSE (WARNING, PRINT ("Gzip encoding not supported yet\n")); break;
404 case encoding_compress_e: VERBOSE (WARNING, PRINT ("Compress encoding not supported yet\n")); break;
405 }
406 }
407
408 /* response */
409 char *buffer = NULL;
410 FILE *fid = NULL;
411 switch (type) {
412 case get_e:
413 case post_e: /* fall through */
414 VERBOSE (DEBUG, PRINT ("Read file %s\n", path));
415 len = readfile (&buffer, path);
416 if (len < 0) {
417 sprintf (location, "http://%s/", conf->servername);
418 len = error_404 (pdata, location);
419 } else {
420 sprintf (location, "http://%s%s", conf->servername, path);
421 len = generic_response (pdata, location, buffer, len);
422 free (buffer);
423 }
424 break;
425 case head_e:
426 VERBOSE (DEBUG, PRINT ("Test file %s\n", path));
427 fid = fopen (path, "rb");
428 if (fid == NULL) {
429 sprintf (location, "http://%s/", conf->servername);
430 len = error_404 (pdata, location);
431 } else {
432 fclose (fid);
433 sprintf (location, "http://%s%s", conf->servername, path);
434 len = generic_response (pdata, location, NULL, 0);
435 }
436 break;
437 case not_supported_e:
438 break;
439 }
440
441 /* cleaning */
442 VERBOSE (DEBUG, PRINT ("Cleaning\n"));
443 if (path) {
444 VERBOSE (DEBUG, PRINT ("Cleaning path\n"));
445 free (path);
446 }
447
448 return len;
449 }
450
451 /* vim: set ts=4 sw=4 et: */