tinywebd

annotate libtinyweb/src/http.c @ 12:86f703031228

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