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