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