tinywebd

view libtinyweb/src/http.c @ 10:0dd50a23f3dd

separated all the tinyweb functionality out as a library
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 18 Apr 2015 22:47:57 +0300
parents src/http.c@5ec50ca0d071
children 86f703031228
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdarg.h>
5 #include <ctype.h>
6 #include <alloca.h>
7 #include "http.h"
8 #include "logger.h"
11 static const char *http_method_str[] = {
12 "<unknown>",
13 "OPTIONS",
14 "GET",
15 "HEAD",
16 "POST",
17 "PUT",
18 "DELETE",
19 "TRACE",
20 "CONNECT",
21 0
22 };
25 /* HTTP 1xx message strings */
26 static const char *http_msg1xx[] = {
27 "Continue", /* 100 */
28 "Switching Protocols" /* 101 */
29 };
31 /* HTTP 2xx message strings */
32 static const char *http_msg2xx[] = {
33 "OK", /* 200 */
34 "Created", /* 201 */
35 "Accepted", /* 202 */
36 "Non-Authoritative Information", /* 203 */
37 "No Content", /* 204 */
38 "Reset Content", /* 205 */
39 "Partial Content" /* 206 */
40 };
42 /* HTTP 3xx message strings */
43 static const char *http_msg3xx[] = {
44 "Multiple Choices", /* 300 */
45 "Moved Permanently", /* 301 */
46 "Found", /* 302 */
47 "See Other", /* 303 */
48 "Not Modified", /* 304 */
49 "Use Proxy", /* 305 */
50 "<unknown>", /* 306 is undefined? */
51 "Temporary Redirect" /* 307 */
52 };
54 /* HTTP 4xx error strings */
55 static const char *http_msg4xx[] = {
56 "Bad Request", /* 400 */
57 "Unauthorized", /* 401 */
58 "What the Fuck?", /* 402 */
59 "Forbidden", /* 403 */
60 "Not Found", /* 404 */
61 "Method Not Allowed", /* 405 */
62 "Not Acceptable", /* 406 */
63 "Proxy Authentication Required", /* 407 */
64 "Request Time-out", /* 408 */
65 "Conflict", /* 409 */
66 "Gone", /* 410 */
67 "Length Required", /* 411 */
68 "Precondition Failed", /* 412 */
69 "Request Entity Too Large", /* 413 */
70 "Request-URI Too Large", /* 414 */
71 "Unsupported Media Type", /* 415 */
72 "Request range not satisfiable", /* 416 */
73 "Expectation Failed" /* 417 */
74 };
76 /* HTTP 5xx error strings */
77 static const char *http_msg5xx[] = {
78 "Internal Server Error", /* 500 */
79 "Not Implemented", /* 501 */
80 "Bad Gateway", /* 502 */
81 "Service Unavailable", /* 503 */
82 "Gateway Time-out", /* 504 */
83 "HTTP Version not supported" /* 505 */
84 };
87 static enum http_method parse_method(const char *s);
90 int http_parse_request(struct http_req_header *hdr, const char *buf, int bufsz)
91 {
92 int i, nlines = 0;
93 char *rqline = 0;
94 char *method, *uri, *version, *ptr;
95 const char *startln, *endln;
97 memset(hdr, 0, sizeof *hdr);
99 for(i=1; i<bufsz; i++) {
100 if(buf[i] == '\n' && buf[i - 1] == '\r') {
101 if(!rqline) {
102 rqline = alloca(i);
103 memcpy(rqline, buf, i - 1);
104 rqline[i - 1] = 0;
105 }
106 ++nlines;
108 if(i > 4 && buf[i - 2] == '\n' && buf[i - 3] == '\r') {
109 hdr->body_offset = i + 1;
110 break;
111 }
112 }
113 }
115 if(!rqline) {
116 return HTTP_HDR_PARTIAL;
117 }
119 ptr = rqline;
120 while(*ptr && isspace(*ptr)) ++ptr;
121 method = ptr;
123 /* parse the request line */
124 while(*ptr && !isspace(*ptr)) ++ptr;
125 while(*ptr && isspace(*ptr)) *ptr++ = 0;
127 uri = ptr;
128 while(*ptr && !isspace(*ptr)) ++ptr;
129 while(*ptr && isspace(*ptr)) *ptr++ = 0;
131 version = ptr;
132 while(*ptr && !isspace(*ptr)) ++ptr;
133 while(*ptr && isspace(*ptr)) *ptr++ = 0;
135 hdr->method = parse_method(method);
136 hdr->uri = strdup(uri);
137 if(sscanf(version, "HTTP/%d.%d", &hdr->ver_major, &hdr->ver_minor) != 2) {
138 fprintf(stderr, "warning: failed to parse HTTP version \"%s\"\n", version);
139 hdr->ver_major = 1;
140 hdr->ver_minor = 1;
141 }
143 if(!(hdr->hdrfields = malloc(nlines * sizeof *hdr->hdrfields))) {
144 perror("failed to allocate memory for the header fields");
145 return HTTP_HDR_NOMEM;
146 }
147 hdr->num_hdrfields = 0;
149 startln = buf;
150 endln = buf;
151 for(i=1; i<hdr->body_offset - 2; i++) {
152 if(buf[i] == '\n' && buf[i - 1] == '\r') {
153 int linesz;
155 endln = buf + i - 1;
156 linesz = endln - startln;
158 if(startln > buf) { /* skip first line */
159 int idx = hdr->num_hdrfields++;
160 hdr->hdrfields[idx] = malloc(linesz + 1);
161 memcpy(hdr->hdrfields[idx], startln, linesz);
162 hdr->hdrfields[idx][linesz] = 0;
163 }
164 startln = endln = buf + i + 1;
165 }
166 }
168 return HTTP_HDR_OK;
169 }
171 void http_log_request(struct http_req_header *hdr)
172 {
173 int i;
175 logmsg("HTTP request header\n");
176 logmsg(" method: %s\n", http_method_str[hdr->method]);
177 logmsg(" uri: %s\n", hdr->uri);
178 logmsg(" version: %d.%d\n", hdr->ver_major, hdr->ver_minor);
179 logmsg(" fields (%d):\n", hdr->num_hdrfields);
181 for(i=0; i<hdr->num_hdrfields; i++) {
182 logmsg(" %s\n", hdr->hdrfields[i]);
183 }
184 logmsg("\n");
185 }
187 void http_destroy_request(struct http_req_header *hdr)
188 {
189 int i;
191 if(hdr->hdrfields) {
192 for(i=0; i<hdr->num_hdrfields; i++) {
193 free(hdr->hdrfields[i]);
194 }
195 free(hdr->hdrfields);
196 }
197 free(hdr->uri);
198 }
200 int http_init_resp(struct http_resp_header *resp)
201 {
202 memset(resp, 0, sizeof *resp);
203 resp->status = 200;
204 resp->ver_major = resp->ver_minor = 1;
205 resp->fields = 0;
206 resp->num_fields = 0;
208 return 0;
209 }
211 int http_add_resp_field(struct http_resp_header *resp, const char *fmt, ...)
212 {
213 int sz;
214 va_list ap;
215 char *field, **newarr, tmp;
217 va_start(ap, fmt);
218 sz = vsnprintf(&tmp, 0, fmt, ap);
219 va_end(ap);
221 if(sz <= 0) sz = 1023;
222 if(!(field = malloc(sz + 1))) {
223 return -1;
224 }
225 va_start(ap, fmt);
226 vsnprintf(field, sz + 1, fmt, ap);
227 va_end(ap);
229 if(!(newarr = realloc(resp->fields, (resp->num_fields + 1) * sizeof *resp->fields))) {
230 free(field);
231 return -1;
232 }
233 resp->fields = newarr;
235 resp->fields[resp->num_fields++] = field;
236 return 0;
237 }
239 void http_destroy_resp(struct http_resp_header *resp)
240 {
241 int i;
242 if(resp->fields) {
243 for(i=0; i<resp->num_fields; i++) {
244 free(resp->fields[i]);
245 }
246 free(resp->fields);
247 resp->fields = 0;
248 }
249 resp->num_fields = 0;
250 }
252 int http_serialize_resp(struct http_resp_header *resp, char *buf)
253 {
254 int i, stsize, size, *fsize;
255 char *ptr, tmp;
257 stsize = snprintf(&tmp, 0, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor,
258 resp->status, http_strmsg(resp->status));
260 fsize = alloca(resp->num_fields * sizeof *fsize);
262 size = stsize;
263 for(i=0; i<resp->num_fields; i++) {
264 int len = strlen(resp->fields[i]) + 2;
265 fsize[i] = len;
266 size += len;
267 }
268 size += 2; /* CRLF and null */
270 if(buf) {
271 sprintf(buf, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor,
272 resp->status, http_strmsg(resp->status));
274 ptr = buf + stsize;
275 for(i=0; i<resp->num_fields; i++) {
276 sprintf(ptr, "%s\r\n", resp->fields[i]);
277 ptr += fsize[i];
278 }
279 *ptr++ = '\r';
280 *ptr++ = '\n';
281 *ptr++ = 0;
282 }
284 return size;
285 }
287 const char *http_strmsg(int code)
288 {
289 static const char **msgxxx[] = {
290 0, http_msg1xx, http_msg2xx, http_msg3xx, http_msg4xx, http_msg5xx
291 };
292 static int msgcount[] = {
293 0,
294 sizeof http_msg1xx / sizeof *http_msg1xx,
295 sizeof http_msg2xx / sizeof *http_msg2xx,
296 sizeof http_msg3xx / sizeof *http_msg3xx,
297 sizeof http_msg4xx / sizeof *http_msg4xx,
298 sizeof http_msg5xx / sizeof *http_msg5xx
299 };
301 int type = code / 100;
302 int idx = code % 100;
304 if(type < 1 || type >= sizeof msgxxx / sizeof *msgxxx) {
305 return "Invalid HTTP Status";
306 }
308 if(idx < 0 || idx >= msgcount[type]) {
309 return "Unknown HTTP Status";
310 }
312 return msgxxx[type][idx];
313 }
315 static enum http_method parse_method(const char *s)
316 {
317 int i;
318 for(i=0; http_method_str[i]; i++) {
319 if(strcmp(s, http_method_str[i]) == 0) {
320 return (enum http_method)i;
321 }
322 }
323 return HTTP_UNKNOWN;
324 }