tinywebd

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