tinywebd

view src/http.c @ 5:def49a046566

serialization of responses
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 17 Apr 2015 01:57:00 +0300
parents 9e054c002489
children 5ec50ca0d071
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"
10 const char *http_method_str[] = {
11 "<unknown>",
12 "OPTIONS",
13 "GET",
14 "HEAD",
15 "POST",
16 "PUT",
17 "DELETE",
18 "TRACE",
19 "CONNECT",
20 0
21 };
24 /* HTTP 1xx message strings */
25 const char *http_msg1xx[] = {
26 "Continue", /* 100 */
27 "Switching Protocols" /* 101 */
28 };
30 /* HTTP 2xx message strings */
31 const char *http_msg2xx[] = {
32 "OK", /* 200 */
33 "Created", /* 201 */
34 "Accepted", /* 202 */
35 "Non-Authoritative Information", /* 203 */
36 "No Content", /* 204 */
37 "Reset Content", /* 205 */
38 "Partial Content" /* 206 */
39 };
41 /* HTTP 3xx message strings */
42 const char *http_msg3xx[] = {
43 "Multiple Choices", /* 300 */
44 "Moved Permanently", /* 301 */
45 "Found", /* 302 */
46 "See Other", /* 303 */
47 "Not Modified", /* 304 */
48 "Use Proxy", /* 305 */
49 "<unknown>", /* 306 is undefined? */
50 "Temporary Redirect" /* 307 */
51 };
53 /* HTTP 4xx error strings */
54 const char *http_msg4xx[] = {
55 "Bad Request", /* 400 */
56 "Unauthorized", /* 401 */
57 "What the Fuck?", /* 402 */
58 "Forbidden", /* 403 */
59 "Not Found", /* 404 */
60 "Method Not Allowed", /* 405 */
61 "Not Acceptable", /* 406 */
62 "Proxy Authentication Required", /* 407 */
63 "Request Time-out", /* 408 */
64 "Conflict", /* 409 */
65 "Gone", /* 410 */
66 "Length Required", /* 411 */
67 "Precondition Failed", /* 412 */
68 "Request Entity Too Large", /* 413 */
69 "Request-URI Too Large", /* 414 */
70 "Unsupported Media Type", /* 415 */
71 "Request range not satisfiable", /* 416 */
72 "Expectation Failed" /* 417 */
73 };
75 /* HTTP 5xx error strings */
76 const char *http_msg5xx[] = {
77 "Internal Server Error", /* 500 */
78 "Not Implemented", /* 501 */
79 "Bad Gateway", /* 502 */
80 "Service Unavailable", /* 503 */
81 "Gateway Time-out", /* 504 */
82 "HTTP Version not supported" /* 505 */
83 };
86 static enum http_method parse_method(const char *s);
89 int http_parse_header(struct http_req_header *hdr, const char *buf, int bufsz)
90 {
91 int i, nlines = 0;
92 char *rqline = 0;
93 char *method, *uri, *version, *ptr;
94 const char *startln, *endln;
96 memset(hdr, 0, sizeof *hdr);
98 for(i=1; i<bufsz; i++) {
99 if(buf[i] == '\n' && buf[i - 1] == '\r') {
100 if(!rqline) {
101 rqline = alloca(i);
102 memcpy(rqline, buf, i - 1);
103 rqline[i - 1] = 0;
104 }
105 ++nlines;
107 if(i > 4 && buf[i - 2] == '\n' && buf[i - 3] == '\r') {
108 hdr->body_offset = i + 1;
109 break;
110 }
111 }
112 }
114 if(!rqline) {
115 return HTTP_HDR_PARTIAL;
116 }
118 ptr = rqline;
119 while(*ptr && isspace(*ptr)) ++ptr;
120 method = ptr;
122 /* parse the request line */
123 while(*ptr && !isspace(*ptr)) ++ptr;
124 while(*ptr && isspace(*ptr)) *ptr++ = 0;
126 uri = ptr;
127 while(*ptr && !isspace(*ptr)) ++ptr;
128 while(*ptr && isspace(*ptr)) *ptr++ = 0;
130 version = ptr;
131 while(*ptr && !isspace(*ptr)) ++ptr;
132 while(*ptr && isspace(*ptr)) *ptr++ = 0;
134 hdr->method = parse_method(method);
135 hdr->uri = strdup(uri);
136 if(sscanf(version, "HTTP/%d.%d", &hdr->ver_major, &hdr->ver_minor) != 2) {
137 fprintf(stderr, "warning: failed to parse HTTP version \"%s\"\n", version);
138 hdr->ver_major = 1;
139 hdr->ver_minor = 1;
140 }
142 if(!(hdr->hdrfields = malloc(nlines * sizeof *hdr->hdrfields))) {
143 perror("failed to allocate memory for the header fields");
144 return HTTP_HDR_NOMEM;
145 }
146 hdr->num_hdrfields = 0;
148 startln = buf;
149 endln = buf;
150 for(i=1; i<hdr->body_offset - 2; i++) {
151 if(buf[i] == '\n' && buf[i - 1] == '\r') {
152 int linesz;
154 endln = buf + i - 1;
155 linesz = endln - startln;
157 if(startln > buf) { /* skip first line */
158 int idx = hdr->num_hdrfields++;
159 hdr->hdrfields[idx] = malloc(linesz + 1);
160 memcpy(hdr->hdrfields[idx], startln, linesz);
161 hdr->hdrfields[idx][linesz] = 0;
162 }
163 startln = endln = buf + i + 1;
164 }
165 }
167 return HTTP_HDR_OK;
168 }
170 void http_print_header(struct http_req_header *hdr)
171 {
172 int i;
174 printf("HTTP request header\n");
175 printf(" method: %s\n", http_method_str[hdr->method]);
176 printf(" uri: %s\n", hdr->uri);
177 printf(" version: %d.%d\n", hdr->ver_major, hdr->ver_minor);
178 printf(" fields (%d):\n", hdr->num_hdrfields);
180 for(i=0; i<hdr->num_hdrfields; i++) {
181 printf(" %s\n", hdr->hdrfields[i]);
182 }
183 putchar('\n');
184 }
186 void http_destroy_header(struct http_req_header *hdr)
187 {
188 int i;
190 if(hdr->hdrfields) {
191 for(i=0; i<hdr->num_hdrfields; i++) {
192 free(hdr->hdrfields[i]);
193 }
194 free(hdr->hdrfields);
195 }
196 free(hdr->uri);
197 }
199 int http_init_resp(struct http_resp_header *resp)
200 {
201 memset(resp, 0, sizeof *resp);
202 resp->status = 200;
203 resp->ver_major = resp->ver_minor = 1;
204 resp->fields = 0;
205 resp->num_fields = 0;
207 return 0;
208 }
210 int http_add_resp_field(struct http_resp_header *resp, const char *fmt, ...)
211 {
212 int sz;
213 va_list ap;
214 char *field, *newarr, tmp;
216 va_start(ap, fmt);
217 sz = vsnprintf(&tmp, 0, fmt, ap);
218 va_end(ap);
220 if(sz <= 0) sz = 1023;
221 if(!(field = malloc(sz + 1))) {
222 return -1;
223 }
224 va_start(ap, fmt);
225 vsnprintf(field, sz + 1, fmt, ap);
226 va_end(ap);
228 if(!(newarr = realloc(resp->fields, (resp->num_fields + 1) * sizeof *resp->fields))) {
229 free(field);
230 return -1;
231 }
232 resp->fields[resp->num_fields++] = newarr;
233 return 0;
234 }
236 void http_destroy_resp(struct http_resp_header *resp)
237 {
238 int i;
239 if(resp->fields) {
240 for(i=0; i<resp->num_fields; i++) {
241 free(resp->fields[i]);
242 }
243 free(resp->fields);
244 resp->fields = 0;
245 }
246 resp->num_fields = 0;
247 }
249 int http_serialize_resp(struct http_resp_header *resp, char *buf)
250 {
251 int i, stsize, size, *fsize;
252 char *ptr, tmp;
254 stsize = snprintf(&tmp, 0, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor,
255 resp->status, http_strmsg(resp->status));
257 fsize = alloca(resp->num_fields * sizeof *fsize);
259 size = stsize;
260 for(i=0; i<resp->num_fields; i++) {
261 int len = strlen(resp->fields[i]) + 2;
262 fsize[i] = len;
263 size += len;
264 }
265 size += 2; /* CRLF and null */
267 if(buf) {
268 sprintf(buf, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor,
269 resp->status, http_strmsg(resp->status));
271 ptr = buf + stsize;
272 for(i=0; i<resp->num_fields; i++) {
273 sprintf(ptr, "%s\r\n", resp->fields[i]);
274 ptr += fsize[i];
275 }
276 *ptr++ = '\r';
277 *ptr++ = '\n';
278 *ptr++ = 0;
279 }
281 return size;
282 }
284 const char *http_strmsg(int code)
285 {
286 static const char **msgxxx[] = {
287 0, http_msg1xx, http_msg2xx, http_msg3xx, http_msg4xx, http_msg5xx
288 };
289 static int msgcount[] = {
290 0,
291 sizeof http_msg1xx / sizeof *http_msg1xx,
292 sizeof http_msg2xx / sizeof *http_msg2xx,
293 sizeof http_msg3xx / sizeof *http_msg3xx,
294 sizeof http_msg4xx / sizeof *http_msg4xx,
295 sizeof http_msg5xx / sizeof *http_msg5xx
296 };
298 int type = code / 100;
299 int idx = code % 100;
301 if(type < 1 || type >= sizeof msgxxx / sizeof *msgxxx) {
302 return "Invalid HTTP Status";
303 }
305 if(idx < 0 || idx >= msgcount[type]) {
306 return "Unknown HTTP Status";
307 }
309 return msgxxx[type][idx];
310 }
312 static enum http_method parse_method(const char *s)
313 {
314 int i;
315 for(i=0; http_method_str[i]; i++) {
316 if(strcmp(s, http_method_str[i]) == 0) {
317 return (enum http_method)i;
318 }
319 }
320 return HTTP_UNKNOWN;
321 }