tinywebd

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