manage content-encoding
[webserver.git] / http.c
CommitLineData
d0b0d52b 1#include <malloc.h>
0e6fd2e9 2#include <stdlib.h>
d0b0d52b 3#include <string.h>
8a1d9e4a 4#include <time.h>
d0b0d52b
ML
5
6#include "debug.h"
4baf6839 7#include "file.h"
d0b0d52b
ML
8
9#include "http.h"
10
8a1d9e4a
LM
11#define BUFFER_SIZE 128
12
bb0468a5
LM
13#define HTTP_VERSION "HTTP/1.0"
14#define SERVER_NAME "Webserver/0.0.1"
15
8a1d9e4a 16char *codes[15] = {
bb0468a5
LM
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
33typedef 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
40typedef enum {
41 not_supported_e = 0, get_e, head_e, post_e
42} method_t;
43
7fc5f3c6
LM
44typedef enum {
45 encoding_plain_e = 0, encoding_gzip_e, encoding_compress_e
46} encoding_t;
47
bb0468a5
LM
48typedef struct {
49 char *allow;
50 char *authorization;
7fc5f3c6 51 encoding_t content_encoding;
bb0468a5
LM
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
2bec4aa7
LM
67typedef struct {
68 char *ext;
69 char *type;
70 char *charset;
71} mime_t;
72
73#define NB_MIMES 8
74
75mime_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
88mime_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
bb0468a5
LM
112/* print header values */
113
114void print_header_values (header_t *header)
115{
cae06547
LM
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);
7fc5f3c6
LM
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 }
cae06547
LM
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);
bb0468a5
LM
140}
141
142/* find sequence*/
143
144char *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
8a1d9e4a 165int add_line (char **buffer, char *str)
bb0468a5 166{
2bec4aa7
LM
167 VERBOSE (DEBUG, PRINT ("add line: %s\n", str));
168 int len = ((*buffer) ? strlen (*buffer) : 0) + strlen (str) + 3;
50c7ef81
LM
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 }
8a1d9e4a 175 strcat (*buffer, str);
2bec4aa7 176 strcat (*buffer, "\r\n");
d0b0d52b
ML
177 return len;
178}
179
8a1d9e4a 180int add_status_line (char **buffer, code_t code)
bb0468a5 181{
8a1d9e4a
LM
182 char tmp[BUFFER_SIZE] = {0};
183
184 /* Status */
2bec4aa7 185 sprintf (tmp, "%s %s", HTTP_VERSION, codes[code]);
8a1d9e4a
LM
186 add_line (buffer, tmp);
187
188 return strlen (*buffer);
bb0468a5
LM
189}
190
8a1d9e4a 191int add_general_header (char **buffer)
bb0468a5 192{
8a1d9e4a
LM
193 char tmp[BUFFER_SIZE] = {0};
194
195 /* Date */
196 time_t ts = time (NULL);
2bec4aa7
LM
197 sprintf (tmp, "Date: %s", ctime (&ts));
198 tmp[strlen (tmp) - 1] = 0; // remove last \n
8a1d9e4a
LM
199 add_line (buffer, tmp);
200
201 /* Pragma */
202
203 return strlen (*buffer);
204}
205
206int add_response_header (char **buffer, char *uri)
207{
208 char tmp[BUFFER_SIZE] = {0};
209
210 /* Location */
2bec4aa7 211 sprintf (tmp, "Location: %s", uri);
8a1d9e4a
LM
212 add_line (buffer, tmp);
213
214 /* Server */
2bec4aa7 215 sprintf (tmp, "Server: %s", SERVER_NAME);
8a1d9e4a
LM
216 add_line (buffer, tmp);
217
218 /* WWW-Authentificate */
219
220 return strlen (*buffer);
bb0468a5
LM
221}
222
cae06547 223int add_entity (char **buffer, char *entity, int size, char *type, char *encoding)
bb0468a5 224{
8a1d9e4a
LM
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) {
0e6fd2e9 236 sprintf (tmp, "Content-Encoding: %s", encoding);
8a1d9e4a
LM
237 add_line (buffer, tmp);
238 }
239
240 /* Content-Length */
2bec4aa7 241 sprintf (tmp, "Content-Length: %d", size);
8a1d9e4a
LM
242 add_line (buffer, tmp);
243
244 /* Content-Type */
2bec4aa7 245 sprintf (tmp, "Content-Type: %s", type);
8a1d9e4a
LM
246 add_line (buffer, tmp);
247
2bec4aa7 248 add_line (buffer, "");
8a1d9e4a
LM
249
250 /* Entity */
251 len = strlen (*buffer);
252 *buffer = realloc (*buffer, len + size);
253 memcpy (*buffer + len, entity, size);
254 len += size;
255 }
256
bb0468a5
LM
257 return len;
258}
259
260/* error 400 */
261
262int error_400 (char **buffer)
263{
8a1d9e4a
LM
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>";
cae06547 269 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
8a1d9e4a
LM
270}
271
4baf6839
LM
272/* error 404 */
273
274int 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>";
cae06547 281 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
4baf6839
LM
282}
283
2bec4aa7 284/* generic response */
8a1d9e4a 285
2bec4aa7 286int generic_response (char **buffer, char *location, char *response, int size)
8a1d9e4a 287{
50c7ef81
LM
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);
2bec4aa7 295 mime_t *mime = find_mime_type (location);
50c7ef81
LM
296
297 VERBOSE (DEBUG, PRINT ("add_entity %d\n", len));
2bec4aa7 298 return add_entity (buffer, response, size, mime->type, mime->charset);
bb0468a5
LM
299}
300
301/* trim string */
302
303char *trim (char *str)
304{
305 if (str != NULL) {
306 while ((*str == ' ') || (*str == '\t')) {
307 str++;
308 }
309 }
310 return str;
311}
312
6ae861c7
LM
313/* creqte command */
314
315char *createcommand (char *format, char *name)
316{
317 char *command = (char *) calloc (strlen (format) + strlen (name) + 1, 1);
318 sprintf (command, format, name);
319 return command;
320}
321
bb0468a5
LM
322/* main HTTP processing */
323
2bec4aa7 324int processing (char *data, int len, conf_t *conf, char **pdata)
bb0468a5 325{
0e6fd2e9 326 char *saved_data = data;
2bec4aa7 327 char location[BUFFER_SIZE] = {0};
bb0468a5
LM
328 VERBOSE (DEBUG, PRINT ("Start processing\n"));
329
330 /* check method */
0e6fd2e9 331 char *line = find_sequence (data, len + data - saved_data, "\r\n", &data);
bb0468a5
LM
332 if (line == NULL) {
333 VERBOSE (WARNING, PRINT ("Unknown received data\n"));
334 if (pdata != NULL) {
335 *pdata = NULL;
336 }
337 return 0;
338 }
339 VERBOSE (DEBUG, PRINT ("Command line: '%s'\n", line));
340
341 char *method = strtok (line, " ");
342 char *uri = strtok (NULL, " ");
343 char *version = strtok (NULL, " ");
344 method_t type = not_supported_e;
345 if (strcmp (method, "GET") == 0) {
346 type = get_e;
347 } else if (strcmp ("HEAD", method) == 0) {
348 type = head_e;
349 } else if (strcmp ("POST", method) == 0) {
350 type = post_e;
351 } else {
352 VERBOSE (WARNING, PRINT ("Unkown method: %s\n", method));
bb0468a5
LM
353 return error_400 (pdata);
354 }
355 VERBOSE (INFO, PRINT ("%s %s (%s)\n", (type == get_e) ? "Get" : (type == head_e) ? "Head" : "Post", uri, version));
356
4baf6839 357 /* analyse uri */
0e6fd2e9
LM
358 char *filename = strtok (uri, "&");
359 char *variables = strtok (NULL, "\n");
360 char *path = (char *) calloc (strlen (conf->root) + strlen (filename) + 2, 1);
2bec4aa7 361 //sprintf (filename, "%s%s%s", conf->root, ((conf->root[strlen (conf->root) - 1] != '/') && (uri[0] != '/')) ? "/" : "", uri);
0e6fd2e9 362 sprintf (path, "%s/%s", conf->root, filename);
4baf6839 363
bb0468a5
LM
364 /* check header */
365 header_t header = {0};
0e6fd2e9 366 while (strcmp (line = find_sequence (data, len + data - saved_data, "\r\n", &data), "") != 0) {
bb0468a5
LM
367 VERBOSE (DEBUG, PRINT ("Header line: '%s'\n", line));
368 char *field = strtok (line, ":");
369 char *value = trim (strtok (NULL, "\r"));
370 VERBOSE (DEBUG, PRINT ("Field: %s\nValue: %s\n", field, value));
371 if (*line == 0) {
372 break;
373 }
6ae861c7 374
bb0468a5 375 VERBOSE (DEBUG, PRINT ("Analyse field\n"));
62584064
LM
376 if (strcmp (field, "Allow") == 0) { header.allow = value; }
377 else if (strcmp (field, "Authorization") == 0) { header.authorization = value; }
7fc5f3c6
LM
378 else if (strcmp (field, "Content-Encoding") == 0) {
379 if (strcmp (value, "x-gzip") == 0) {
380 header.content_encoding = encoding_gzip_e;
381 } else if (strcmp (value, "x-compress") == 0) {
382 header.content_encoding = encoding_compress_e;
383 } else {
384 VERBOSE (WARNING, PRINT ("Unknown content encoding: %s\n", value));
385 }
386 } else if (strcmp (field, "Content-Length") == 0) { header.content_length = value; }
62584064
LM
387 else if (strcmp (field, "Content-Type") == 0) { header.content_type = value; }
388 else if (strcmp (field, "Date") == 0) { header.date = value; }
389 else if (strcmp (field, "Expires") == 0) { header.expires = value; }
390 else if (strcmp (field, "From") == 0) { header.from = value; }
391 else if (strcmp (field, "If-Modified-Since") == 0) { header.if_modified_since = value; }
392 else if (strcmp (field, "Last-Modified") == 0) { header.last_modified = value; }
393 else if (strcmp (field, "Location") == 0) { header.location = value; }
394 else if (strcmp (field, "Pragma") == 0) { header.pragma = value; }
395 else if (strcmp (field, "Referer") == 0) { header.referer = value; }
396 else if (strcmp (field, "Server") == 0) { header.server = value; }
397 else if (strcmp (field, "User-Agent") == 0) { header.user_agent = value; }
398 else if (strcmp (field, "WWW-Authenticate") == 0) { header.www_authenticate = value; }
399 else { VERBOSE (WARNING, PRINT ("Unknown header field: '%s'\n", field)); }
bb0468a5
LM
400 }
401 VERBOSE (DEBUG, print_header_values (&header));
402
0e6fd2e9
LM
403 /* body */
404 char *body = data;
6ae861c7 405 char *newbody = NULL;
0e6fd2e9
LM
406 len -= saved_data - data;
407 if (len != (header.content_length ? atoi (header.content_length) : 0)) {
408 VERBOSE (WARNING, PRINT ("Incoherent size (%d <> %s)\n", len, header.content_length));
409 }
7fc5f3c6 410 if (len > 0) {
6ae861c7
LM
411 int i;
412 char *fcomp= NULL;
413 char *fdecomp = NULL;
414 char *command = NULL;
415
7fc5f3c6 416 switch (header.content_encoding) {
6ae861c7
LM
417 case encoding_plain_e:
418 break;
419
420 case encoding_gzip_e:
421 fcomp = tempname (conf->temp, ".gz");
422 writefile (fcomp, body, len);
423 command = createcommand ("gunzip %s", fcomp);
424 system (command);
425 fdecomp = strdup (fcomp);
426 for (i = strlen (fdecomp) - 1; i > 0; i--) {
427 if (fdecomp[i] == '.') {
428 fdecomp[i] = 0;
429 break;
430 }
431 }
432 len = readfile (&newbody, fdecomp);
433 break;
434
435 case encoding_compress_e:
436 fcomp = tempname (conf->temp, ".Z");
437 writefile (fcomp, body, len);
438 command = createcommand ("compress %s", fcomp);
439 system (command);
440 fdecomp = strdup (fcomp);
441 for (i = strlen (fdecomp) - 1; i > 0; i--) {
442 if (fdecomp[i] == '.') {
443 fdecomp[i] = 0;
444 break;
445 }
446 }
447 len = readfile (&newbody, fdecomp);
448 break;
449 }
450
451 if (fcomp) {
452 unlink (fcomp);
453 free (fcomp);
454 }
455 if (fdecomp) {
456 unlink (fdecomp);
457 free (fdecomp);
458 }
459 if (command) {
460 free (command);
7fc5f3c6
LM
461 }
462 }
0e6fd2e9 463
bb0468a5 464 /* response */
4baf6839 465 char *buffer = NULL;
62584064 466 FILE *fid = NULL;
8a1d9e4a
LM
467 switch (type) {
468 case get_e:
0e6fd2e9
LM
469 case post_e: /* fall through */
470 VERBOSE (DEBUG, PRINT ("Read file %s\n", path));
471 len = readfile (&buffer, path);
d256f755 472 if (len < 0) {
2bec4aa7
LM
473 sprintf (location, "http://%s/", conf->servername);
474 len = error_404 (pdata, location);
4baf6839 475 } else {
0e6fd2e9 476 sprintf (location, "http://%s%s", conf->servername, path);
2bec4aa7 477 len = generic_response (pdata, location, buffer, len);
4baf6839
LM
478 free (buffer);
479 }
8a1d9e4a
LM
480 break;
481 case head_e:
0e6fd2e9
LM
482 VERBOSE (DEBUG, PRINT ("Test file %s\n", path));
483 fid = fopen (path, "rb");
62584064
LM
484 if (fid == NULL) {
485 sprintf (location, "http://%s/", conf->servername);
486 len = error_404 (pdata, location);
487 } else {
488 fclose (fid);
0e6fd2e9 489 sprintf (location, "http://%s%s", conf->servername, path);
62584064
LM
490 len = generic_response (pdata, location, NULL, 0);
491 }
8a1d9e4a 492 break;
8a1d9e4a
LM
493 case not_supported_e:
494 break;
495 }
496
4baf6839 497 /* cleaning */
6add28e9 498 VERBOSE (DEBUG, PRINT ("Cleaning\n"));
0e6fd2e9 499 if (path) {
0e6fd2e9 500 free (path);
6add28e9 501 }
6ae861c7
LM
502 if (newbody) {
503 free (newbody);
504 }
4baf6839 505
8a1d9e4a 506 return len;
bb0468a5
LM
507}
508
d0b0d52b 509/* vim: set ts=4 sw=4 et: */