tinywebd

annotate src/http.c @ 4:9e054c002489

fixed the header parsing bugs
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 16 Apr 2015 17:34:15 +0300
parents 852a745503cf
children def49a046566
rev   line source
nuclear@3 1 #include <stdio.h>
nuclear@3 2 #include <stdlib.h>
nuclear@3 3 #include <string.h>
nuclear@3 4 #include <ctype.h>
nuclear@3 5 #include <alloca.h>
nuclear@1 6 #include "http.h"
nuclear@1 7
nuclear@3 8
nuclear@3 9 const char *http_method_str[] = {
nuclear@3 10 "<unknown>",
nuclear@3 11 "OPTIONS",
nuclear@3 12 "GET",
nuclear@3 13 "HEAD",
nuclear@3 14 "POST",
nuclear@3 15 "PUT",
nuclear@3 16 "DELETE",
nuclear@3 17 "TRACE",
nuclear@3 18 "CONNECT",
nuclear@3 19 0
nuclear@3 20 };
nuclear@3 21
nuclear@3 22
nuclear@3 23 /* HTTP 1xx message strings */
nuclear@3 24 const char *http_msg1xx[] = {
nuclear@3 25 "Continue", /* 100 */
nuclear@3 26 "Switching Protocols" /* 101 */
nuclear@3 27 };
nuclear@3 28
nuclear@3 29 /* HTTP 2xx message strings */
nuclear@3 30 const char *http_msg2xx[] = {
nuclear@3 31 "OK", /* 200 */
nuclear@3 32 "Created", /* 201 */
nuclear@3 33 "Accepted", /* 202 */
nuclear@3 34 "Non-Authoritative Information", /* 203 */
nuclear@3 35 "No Content", /* 204 */
nuclear@3 36 "Reset Content", /* 205 */
nuclear@3 37 "Partial Content" /* 206 */
nuclear@3 38 };
nuclear@3 39
nuclear@3 40 /* HTTP 3xx message strings */
nuclear@3 41 const char *http_msg3xx[] = {
nuclear@3 42 "Multiple Choices", /* 300 */
nuclear@3 43 "Moved Permanently", /* 301 */
nuclear@3 44 "Found", /* 302 */
nuclear@3 45 "See Other", /* 303 */
nuclear@3 46 "Not Modified", /* 304 */
nuclear@3 47 "Use Proxy", /* 305 */
nuclear@3 48 "<unknown>", /* 306 is undefined? */
nuclear@3 49 "Temporary Redirect" /* 307 */
nuclear@3 50 };
nuclear@3 51
nuclear@3 52 /* HTTP 4xx error strings */
nuclear@3 53 const char *http_msg4xx[] = {
nuclear@3 54 "Bad Request", /* 400 */
nuclear@3 55 "Unauthorized", /* 401 */
nuclear@3 56 "What the Fuck?", /* 402 */
nuclear@3 57 "Forbidden", /* 403 */
nuclear@3 58 "Not Found", /* 404 */
nuclear@3 59 "Method Not Allowed", /* 405 */
nuclear@3 60 "Not Acceptable", /* 406 */
nuclear@3 61 "Proxy Authentication Required", /* 407 */
nuclear@3 62 "Request Time-out", /* 408 */
nuclear@3 63 "Conflict", /* 409 */
nuclear@3 64 "Gone", /* 410 */
nuclear@3 65 "Length Required", /* 411 */
nuclear@3 66 "Precondition Failed", /* 412 */
nuclear@3 67 "Request Entity Too Large", /* 413 */
nuclear@3 68 "Request-URI Too Large", /* 414 */
nuclear@3 69 "Unsupported Media Type", /* 415 */
nuclear@3 70 "Request range not satisfiable", /* 416 */
nuclear@3 71 "Expectation Failed" /* 417 */
nuclear@3 72 };
nuclear@3 73
nuclear@3 74 /* HTTP 5xx error strings */
nuclear@3 75 const char *http_msg5xx[] = {
nuclear@3 76 "Internal Server Error", /* 500 */
nuclear@3 77 "Not Implemented", /* 501 */
nuclear@3 78 "Bad Gateway", /* 502 */
nuclear@3 79 "Service Unavailable", /* 503 */
nuclear@3 80 "Gateway Time-out", /* 504 */
nuclear@3 81 "HTTP Version not supported" /* 505 */
nuclear@3 82 };
nuclear@3 83
nuclear@3 84
nuclear@3 85 static enum http_method parse_method(const char *s);
nuclear@3 86
nuclear@3 87
nuclear@2 88 int http_parse_header(struct http_req_header *hdr, const char *buf, int bufsz)
nuclear@2 89 {
nuclear@2 90 int i, nlines = 0;
nuclear@2 91 char *rqline = 0;
nuclear@3 92 char *method, *uri, *version, *ptr;
nuclear@3 93 const char *startln, *endln;
nuclear@3 94
nuclear@3 95 memset(hdr, 0, sizeof *hdr);
nuclear@2 96
nuclear@2 97 for(i=1; i<bufsz; i++) {
nuclear@2 98 if(buf[i] == '\n' && buf[i - 1] == '\r') {
nuclear@2 99 if(!rqline) {
nuclear@2 100 rqline = alloca(i);
nuclear@2 101 memcpy(rqline, buf, i - 1);
nuclear@2 102 rqline[i - 1] = 0;
nuclear@2 103 }
nuclear@2 104 ++nlines;
nuclear@3 105
nuclear@3 106 if(i > 4 && buf[i - 2] == '\n' && buf[i - 3] == '\r') {
nuclear@3 107 hdr->body_offset = i + 1;
nuclear@3 108 break;
nuclear@3 109 }
nuclear@2 110 }
nuclear@2 111 }
nuclear@2 112
nuclear@3 113 if(!rqline) {
nuclear@3 114 return HTTP_HDR_PARTIAL;
nuclear@3 115 }
nuclear@2 116
nuclear@3 117 ptr = rqline;
nuclear@3 118 while(*ptr && isspace(*ptr)) ++ptr;
nuclear@3 119 method = ptr;
nuclear@2 120
nuclear@3 121 /* parse the request line */
nuclear@3 122 while(*ptr && !isspace(*ptr)) ++ptr;
nuclear@3 123 while(*ptr && isspace(*ptr)) *ptr++ = 0;
nuclear@3 124
nuclear@3 125 uri = ptr;
nuclear@3 126 while(*ptr && !isspace(*ptr)) ++ptr;
nuclear@3 127 while(*ptr && isspace(*ptr)) *ptr++ = 0;
nuclear@3 128
nuclear@3 129 version = ptr;
nuclear@3 130 while(*ptr && !isspace(*ptr)) ++ptr;
nuclear@3 131 while(*ptr && isspace(*ptr)) *ptr++ = 0;
nuclear@3 132
nuclear@3 133 hdr->method = parse_method(method);
nuclear@3 134 hdr->uri = strdup(uri);
nuclear@3 135 if(sscanf(version, "HTTP/%d.%d", &hdr->ver_major, &hdr->ver_minor) != 2) {
nuclear@3 136 fprintf(stderr, "warning: failed to parse HTTP version \"%s\"\n", version);
nuclear@3 137 hdr->ver_major = 1;
nuclear@3 138 hdr->ver_minor = 1;
nuclear@3 139 }
nuclear@3 140
nuclear@3 141 if(!(hdr->hdrfields = malloc(nlines * sizeof *hdr->hdrfields))) {
nuclear@3 142 perror("failed to allocate memory for the header fields");
nuclear@3 143 return HTTP_HDR_NOMEM;
nuclear@3 144 }
nuclear@3 145 hdr->num_hdrfields = 0;
nuclear@3 146
nuclear@3 147 startln = buf;
nuclear@3 148 endln = buf;
nuclear@4 149 for(i=1; i<hdr->body_offset - 2; i++) {
nuclear@3 150 if(buf[i] == '\n' && buf[i - 1] == '\r') {
nuclear@3 151 int linesz;
nuclear@4 152
nuclear@3 153 endln = buf + i - 1;
nuclear@4 154 linesz = endln - startln;
nuclear@3 155
nuclear@3 156 if(startln > buf) { /* skip first line */
nuclear@3 157 int idx = hdr->num_hdrfields++;
nuclear@3 158 hdr->hdrfields[idx] = malloc(linesz + 1);
nuclear@3 159 memcpy(hdr->hdrfields[idx], startln, linesz);
nuclear@3 160 hdr->hdrfields[idx][linesz] = 0;
nuclear@3 161 }
nuclear@3 162 startln = endln = buf + i + 1;
nuclear@3 163 }
nuclear@3 164 }
nuclear@3 165
nuclear@3 166 return HTTP_HDR_OK;
nuclear@3 167 }
nuclear@3 168
nuclear@3 169 void http_print_header(struct http_req_header *hdr)
nuclear@3 170 {
nuclear@3 171 int i;
nuclear@3 172
nuclear@3 173 printf("HTTP request header\n");
nuclear@3 174 printf(" method: %s\n", http_method_str[hdr->method]);
nuclear@3 175 printf(" uri: %s\n", hdr->uri);
nuclear@3 176 printf(" version: %d.%d\n", hdr->ver_major, hdr->ver_minor);
nuclear@3 177 printf(" fields (%d):\n", hdr->num_hdrfields);
nuclear@3 178
nuclear@3 179 for(i=0; i<hdr->num_hdrfields; i++) {
nuclear@3 180 printf(" %s\n", hdr->hdrfields[i]);
nuclear@3 181 }
nuclear@3 182 putchar('\n');
nuclear@3 183 }
nuclear@3 184
nuclear@3 185 void http_destroy_header(struct http_req_header *hdr)
nuclear@3 186 {
nuclear@3 187 int i;
nuclear@3 188
nuclear@3 189 if(hdr->hdrfields) {
nuclear@3 190 for(i=0; i<hdr->num_hdrfields; i++) {
nuclear@3 191 free(hdr->hdrfields[i]);
nuclear@3 192 }
nuclear@3 193 free(hdr->hdrfields);
nuclear@3 194 }
nuclear@3 195 free(hdr->uri);
nuclear@2 196 }
nuclear@2 197
nuclear@1 198 const char *http_strmsg(int code)
nuclear@1 199 {
nuclear@1 200 static const char **msgxxx[] = {
nuclear@1 201 0, http_msg1xx, http_msg2xx, http_msg3xx, http_msg4xx, http_msg5xx
nuclear@1 202 };
nuclear@1 203 static int msgcount[] = {
nuclear@1 204 0,
nuclear@1 205 sizeof http_msg1xx / sizeof *http_msg1xx,
nuclear@1 206 sizeof http_msg2xx / sizeof *http_msg2xx,
nuclear@1 207 sizeof http_msg3xx / sizeof *http_msg3xx,
nuclear@1 208 sizeof http_msg4xx / sizeof *http_msg4xx,
nuclear@1 209 sizeof http_msg5xx / sizeof *http_msg5xx
nuclear@1 210 };
nuclear@1 211
nuclear@1 212 int type = code / 100;
nuclear@1 213 int idx = code % 100;
nuclear@1 214
nuclear@1 215 if(type < 1 || type >= sizeof msgxxx / sizeof *msgxxx) {
nuclear@1 216 return "Invalid HTTP Status";
nuclear@1 217 }
nuclear@1 218
nuclear@1 219 if(idx < 0 || idx >= msgcount[type]) {
nuclear@1 220 return "Unknown HTTP Status";
nuclear@1 221 }
nuclear@1 222
nuclear@1 223 return msgxxx[type][idx];
nuclear@1 224 }
nuclear@3 225
nuclear@3 226 static enum http_method parse_method(const char *s)
nuclear@3 227 {
nuclear@3 228 int i;
nuclear@3 229 for(i=0; http_method_str[i]; i++) {
nuclear@3 230 if(strcmp(s, http_method_str[i]) == 0) {
nuclear@3 231 return (enum http_method)i;
nuclear@3 232 }
nuclear@3 233 }
nuclear@3 234 return HTTP_UNKNOWN;
nuclear@3 235 }