first http server working
[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 /* print header values */
63
64 void print_header_values (header_t *header)
65 {
66 printf ("Header values\n");
67 if (header->allow) printf ("allow = '%s'\n", header->allow);
68 if (header->authorization) printf ("authorization = '%s'\n", header->authorization);
69 if (header->content_encoding) printf ("content_encoding = '%s'\n", header->content_encoding);
70 if (header->content_length) printf ("content_length = '%s'\n", header->content_length);
71 if (header->content_type) printf ("content_type = '%s'\n", header->content_type);
72 if (header->date) printf ("date = '%s'\n", header->date);
73 if (header->expires) printf ("expires = '%s'\n", header->expires);
74 if (header->from) printf ("from = '%s'\n", header->from);
75 if (header->if_modified_since) printf ("if_modified_since = '%s'\n", header->if_modified_since);
76 if (header->last_modified) printf ("last_modified = '%s'\n", header->last_modified);
77 if (header->location) printf ("location = '%s'\n", header->location);
78 if (header->pragma) printf ("pragma = '%s'\n", header->pragma);
79 if (header->referer) printf ("referer = '%s'\n", header->referer);
80 if (header->server) printf ("server = '%s'\n", header->server);
81 if (header->user_agent) printf ("user_agent = '%s'\n", header->user_agent);
82 if (header->www_authenticate) printf ("www_authenticate = '%s'\n", header->www_authenticate);
83 }
84
85 /* find sequence*/
86
87 char *find_sequence (char *data, int len, char *seq, char **pdata)
88 {
89
90 int size = strlen (seq);
91
92 int i;
93 for (i = 0; i < len - size + 1; i++) {
94 if (strncmp (data + i, seq, size) == 0) {
95 data[i] = 0;
96 if (pdata != NULL) {
97 *pdata = data + i + size;
98 }
99 return data;
100 }
101 }
102
103 return NULL;
104 }
105
106 /* response entity */
107
108 int add_line (char **buffer, char *str)
109 {
110 VERBOSE (DEBUG, PRINT ("add line: %s", str));
111 int len = ((*buffer) ? strlen (*buffer) : 0) + strlen (str) + 1;
112 VERBOSE (DEBUG, PRINT ("len: %d\n", len));
113 if (*buffer) {
114 *buffer = (char *)realloc (*buffer, len);
115 } else {
116 *buffer = (char *)calloc (len, 1);
117 }
118 strcat (*buffer, str);
119 return len;
120 }
121
122 int add_status_line (char **buffer, code_t code)
123 {
124 char tmp[BUFFER_SIZE] = {0};
125
126 /* Status */
127 sprintf (tmp, "%s %s\r\n", HTTP_VERSION, codes[code]);
128 add_line (buffer, tmp);
129
130 return strlen (*buffer);
131 }
132
133 int add_general_header (char **buffer)
134 {
135 char tmp[BUFFER_SIZE] = {0};
136
137 /* Date */
138 time_t ts = time (NULL);
139 sprintf (tmp, "Date: %s\r\n", ctime (&ts));
140 add_line (buffer, tmp);
141
142 /* Pragma */
143
144 return strlen (*buffer);
145 }
146
147 int add_response_header (char **buffer, char *uri)
148 {
149 char tmp[BUFFER_SIZE] = {0};
150
151 /* Location */
152 sprintf (tmp, "Location: %s\r\n", uri);
153 add_line (buffer, tmp);
154
155 /* Server */
156 sprintf (tmp, "Server: %s\r\n", SERVER_NAME);
157 add_line (buffer, tmp);
158
159 /* WWW-Authentificate */
160
161 return strlen (*buffer);
162 }
163
164 int add_entity (char **buffer, char *entity, int size, char *type, char *encoding)
165 {
166 char tmp[BUFFER_SIZE] = {0};
167 int len = strlen (*buffer);
168
169 /* Allow */
170 /* Expires */
171 /* Last-Modified */
172
173 if (entity != NULL) {
174
175 /* Content-Encoding */
176 if (encoding != NULL) {
177 sprintf (tmp, "Content-Encoding: %s\r\n", encoding);
178 add_line (buffer, tmp);
179 }
180
181 /* Content-Length */
182 sprintf (tmp, "Content-Length: %d\r\n", size);
183 add_line (buffer, tmp);
184
185 /* Content-Type */
186 sprintf (tmp, "Content-Type: %s\r\n", type);
187 add_line (buffer, tmp);
188
189 add_line (buffer, "\r\n");
190
191 /* Entity */
192 len = strlen (*buffer);
193 *buffer = realloc (*buffer, len + size);
194 memcpy (*buffer + len, entity, size);
195 len += size;
196 }
197
198 return len;
199 }
200
201 /* error 400 */
202
203 int error_400 (char **buffer)
204 {
205 add_status_line (buffer, c400);
206 add_general_header (buffer);
207 add_response_header (buffer, NULL);
208
209 char *response = "<html><head><title>Error 400</title></head><body><p>Bad Request</p></body></html>";
210 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
211 }
212
213 /* error 404 */
214
215 int error_404 (char **buffer, char *uri)
216 {
217 add_status_line (buffer, c404);
218 add_general_header (buffer);
219 add_response_header (buffer, uri);
220
221 char *response = "<html><head><title>Error 404</title></head><body><p>File not found</p></body></html>";
222 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
223 }
224
225 /* response html */
226
227 int response_html (char **buffer, char *location, char *response)
228 {
229 int len = 0;
230 VERBOSE (DEBUG, PRINT ("add_status_line %d\n", len));
231 len = add_status_line (buffer, c200);
232 VERBOSE (DEBUG, PRINT ("add_general_header %d\n", len));
233 len = add_general_header (buffer);
234 VERBOSE (DEBUG, PRINT ("add_response_header %d\n", len));
235 len = add_response_header (buffer, location);
236
237 VERBOSE (DEBUG, PRINT ("add_entity %d\n", len));
238 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
239 }
240
241 /* trim string */
242
243 char *trim (char *str)
244 {
245 if (str != NULL) {
246 while ((*str == ' ') || (*str == '\t')) {
247 str++;
248 }
249 }
250 return str;
251 }
252
253 /* main HTTP processing */
254
255 int processing (char *data, int len, char *root, char **pdata)
256 {
257 VERBOSE (DEBUG, PRINT ("Start processing\n"));
258
259 /* check method */
260 char *line = find_sequence (data, len, "\r\n", &data);
261 if (line == NULL) {
262 VERBOSE (WARNING, PRINT ("Unknown received data\n"));
263 if (pdata != NULL) {
264 *pdata = NULL;
265 }
266 return 0;
267 }
268 VERBOSE (DEBUG, PRINT ("Command line: '%s'\n", line));
269
270 char *method = strtok (line, " ");
271 char *uri = strtok (NULL, " ");
272 char *version = strtok (NULL, " ");
273 method_t type = not_supported_e;
274 if (strcmp (method, "GET") == 0) {
275 type = get_e;
276 } else if (strcmp ("HEAD", method) == 0) {
277 type = head_e;
278 } else if (strcmp ("POST", method) == 0) {
279 type = post_e;
280 } else {
281 VERBOSE (WARNING, PRINT ("Unkown method: %s\n", method));
282 return error_400 (pdata);
283 }
284 VERBOSE (INFO, PRINT ("%s %s (%s)\n", (type == get_e) ? "Get" : (type == head_e) ? "Head" : "Post", uri, version));
285
286 /* analyse uri */
287 char *filename = (char *)calloc (strlen (root) + strlen (uri) + 2, 1);
288 //sprintf (filename, "%s%s%s", root, ((root[strlen (root) - 1] != '/') && (uri[0] != '/')) ? "/" : "", uri);
289 sprintf (filename, "%s/%s", root, uri);
290
291 /* check header */
292 header_t header = {0};
293 while (strcmp (line = find_sequence (data, len, "\r\n", &data), "") != 0) {
294 VERBOSE (DEBUG, PRINT ("Header line: '%s'\n", line));
295 char *field = strtok (line, ":");
296 char *value = trim (strtok (NULL, "\r"));
297 VERBOSE (DEBUG, PRINT ("Field: %s\nValue: %s\n", field, value));
298 if (*line == 0) {
299 break;
300 }
301
302 VERBOSE (DEBUG, PRINT ("Analyse field\n"));
303 if (strcmp (field, "Allow") == 0) {
304 header.allow = value;
305 } else if (strcmp (field, "Authorization") == 0) {
306 header.authorization = value;
307 } else if (strcmp (field, "Content-Encoding") == 0) {
308 header.content_encoding = value;
309 } else if (strcmp (field, "Content-Length") == 0) {
310 header.content_length = value;
311 } else if (strcmp (field, "Content-Type") == 0) {
312 header.content_type = value;
313 } else if (strcmp (field, "Date") == 0) {
314 header.date = value;
315 } else if (strcmp (field, "Expires") == 0) {
316 header.expires = value;
317 } else if (strcmp (field, "From") == 0) {
318 header.from = value;
319 } else if (strcmp (field, "If-Modified-Since") == 0) {
320 header.if_modified_since = value;
321 } else if (strcmp (field, "Last-Modified") == 0) {
322 header.last_modified = value;
323 } else if (strcmp (field, "Location") == 0) {
324 header.location = value;
325 } else if (strcmp (field, "Pragma") == 0) {
326 header.pragma = value;
327 } else if (strcmp (field, "Referer") == 0) {
328 header.referer = value;
329 } else if (strcmp (field, "Server") == 0) {
330 header.server = value;
331 } else if (strcmp (field, "User-Agent") == 0) {
332 header.user_agent = value;
333 } else if (strcmp (field, "WWW-Authenticate") == 0) {
334 header.www_authenticate = value;
335 } else {
336 VERBOSE (WARNING, PRINT ("Unknown header field: '%s'\n", field));
337 }
338 }
339 VERBOSE (DEBUG, print_header_values (&header));
340
341 /* response */
342 char *buffer = NULL;
343 switch (type) {
344 case get_e:
345 VERBOSE (DEBUG, PRINT ("Read file %s\n", filename));
346 len = readfile (&buffer, filename);
347 if (len == 0) {
348 len = error_404 (pdata, "http://localhost/");
349 } else {
350 len = response_html (pdata, "http://localhost/", buffer);
351 free (buffer);
352 }
353 break;
354 case head_e:
355 break;
356 case post_e:
357 VERBOSE (DEBUG, PRINT ("Write file %s\n", filename));
358 break;
359 case not_supported_e:
360 break;
361 }
362
363 /* cleaning */
364 VERBOSE (DEBUG, PRINT ("Cleaning\n"));
365 if (filename) {
366 VERBOSE (DEBUG, PRINT ("Cleaning filename\n"));
367 free (filename);
368 }
369
370 return len;
371 }
372
373 /* vim: set ts=4 sw=4 et: */