tinywebd
diff 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 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libtinyweb/src/http.c Sat Apr 18 22:47:57 2015 +0300 1.3 @@ -0,0 +1,324 @@ 1.4 +#include <stdio.h> 1.5 +#include <stdlib.h> 1.6 +#include <string.h> 1.7 +#include <stdarg.h> 1.8 +#include <ctype.h> 1.9 +#include <alloca.h> 1.10 +#include "http.h" 1.11 +#include "logger.h" 1.12 + 1.13 + 1.14 +static const char *http_method_str[] = { 1.15 + "<unknown>", 1.16 + "OPTIONS", 1.17 + "GET", 1.18 + "HEAD", 1.19 + "POST", 1.20 + "PUT", 1.21 + "DELETE", 1.22 + "TRACE", 1.23 + "CONNECT", 1.24 + 0 1.25 +}; 1.26 + 1.27 + 1.28 +/* HTTP 1xx message strings */ 1.29 +static const char *http_msg1xx[] = { 1.30 + "Continue", /* 100 */ 1.31 + "Switching Protocols" /* 101 */ 1.32 +}; 1.33 + 1.34 +/* HTTP 2xx message strings */ 1.35 +static const char *http_msg2xx[] = { 1.36 + "OK", /* 200 */ 1.37 + "Created", /* 201 */ 1.38 + "Accepted", /* 202 */ 1.39 + "Non-Authoritative Information", /* 203 */ 1.40 + "No Content", /* 204 */ 1.41 + "Reset Content", /* 205 */ 1.42 + "Partial Content" /* 206 */ 1.43 +}; 1.44 + 1.45 +/* HTTP 3xx message strings */ 1.46 +static const char *http_msg3xx[] = { 1.47 + "Multiple Choices", /* 300 */ 1.48 + "Moved Permanently", /* 301 */ 1.49 + "Found", /* 302 */ 1.50 + "See Other", /* 303 */ 1.51 + "Not Modified", /* 304 */ 1.52 + "Use Proxy", /* 305 */ 1.53 + "<unknown>", /* 306 is undefined? */ 1.54 + "Temporary Redirect" /* 307 */ 1.55 +}; 1.56 + 1.57 +/* HTTP 4xx error strings */ 1.58 +static const char *http_msg4xx[] = { 1.59 + "Bad Request", /* 400 */ 1.60 + "Unauthorized", /* 401 */ 1.61 + "What the Fuck?", /* 402 */ 1.62 + "Forbidden", /* 403 */ 1.63 + "Not Found", /* 404 */ 1.64 + "Method Not Allowed", /* 405 */ 1.65 + "Not Acceptable", /* 406 */ 1.66 + "Proxy Authentication Required", /* 407 */ 1.67 + "Request Time-out", /* 408 */ 1.68 + "Conflict", /* 409 */ 1.69 + "Gone", /* 410 */ 1.70 + "Length Required", /* 411 */ 1.71 + "Precondition Failed", /* 412 */ 1.72 + "Request Entity Too Large", /* 413 */ 1.73 + "Request-URI Too Large", /* 414 */ 1.74 + "Unsupported Media Type", /* 415 */ 1.75 + "Request range not satisfiable", /* 416 */ 1.76 + "Expectation Failed" /* 417 */ 1.77 +}; 1.78 + 1.79 +/* HTTP 5xx error strings */ 1.80 +static const char *http_msg5xx[] = { 1.81 + "Internal Server Error", /* 500 */ 1.82 + "Not Implemented", /* 501 */ 1.83 + "Bad Gateway", /* 502 */ 1.84 + "Service Unavailable", /* 503 */ 1.85 + "Gateway Time-out", /* 504 */ 1.86 + "HTTP Version not supported" /* 505 */ 1.87 +}; 1.88 + 1.89 + 1.90 +static enum http_method parse_method(const char *s); 1.91 + 1.92 + 1.93 +int http_parse_request(struct http_req_header *hdr, const char *buf, int bufsz) 1.94 +{ 1.95 + int i, nlines = 0; 1.96 + char *rqline = 0; 1.97 + char *method, *uri, *version, *ptr; 1.98 + const char *startln, *endln; 1.99 + 1.100 + memset(hdr, 0, sizeof *hdr); 1.101 + 1.102 + for(i=1; i<bufsz; i++) { 1.103 + if(buf[i] == '\n' && buf[i - 1] == '\r') { 1.104 + if(!rqline) { 1.105 + rqline = alloca(i); 1.106 + memcpy(rqline, buf, i - 1); 1.107 + rqline[i - 1] = 0; 1.108 + } 1.109 + ++nlines; 1.110 + 1.111 + if(i > 4 && buf[i - 2] == '\n' && buf[i - 3] == '\r') { 1.112 + hdr->body_offset = i + 1; 1.113 + break; 1.114 + } 1.115 + } 1.116 + } 1.117 + 1.118 + if(!rqline) { 1.119 + return HTTP_HDR_PARTIAL; 1.120 + } 1.121 + 1.122 + ptr = rqline; 1.123 + while(*ptr && isspace(*ptr)) ++ptr; 1.124 + method = ptr; 1.125 + 1.126 + /* parse the request line */ 1.127 + while(*ptr && !isspace(*ptr)) ++ptr; 1.128 + while(*ptr && isspace(*ptr)) *ptr++ = 0; 1.129 + 1.130 + uri = ptr; 1.131 + while(*ptr && !isspace(*ptr)) ++ptr; 1.132 + while(*ptr && isspace(*ptr)) *ptr++ = 0; 1.133 + 1.134 + version = ptr; 1.135 + while(*ptr && !isspace(*ptr)) ++ptr; 1.136 + while(*ptr && isspace(*ptr)) *ptr++ = 0; 1.137 + 1.138 + hdr->method = parse_method(method); 1.139 + hdr->uri = strdup(uri); 1.140 + if(sscanf(version, "HTTP/%d.%d", &hdr->ver_major, &hdr->ver_minor) != 2) { 1.141 + fprintf(stderr, "warning: failed to parse HTTP version \"%s\"\n", version); 1.142 + hdr->ver_major = 1; 1.143 + hdr->ver_minor = 1; 1.144 + } 1.145 + 1.146 + if(!(hdr->hdrfields = malloc(nlines * sizeof *hdr->hdrfields))) { 1.147 + perror("failed to allocate memory for the header fields"); 1.148 + return HTTP_HDR_NOMEM; 1.149 + } 1.150 + hdr->num_hdrfields = 0; 1.151 + 1.152 + startln = buf; 1.153 + endln = buf; 1.154 + for(i=1; i<hdr->body_offset - 2; i++) { 1.155 + if(buf[i] == '\n' && buf[i - 1] == '\r') { 1.156 + int linesz; 1.157 + 1.158 + endln = buf + i - 1; 1.159 + linesz = endln - startln; 1.160 + 1.161 + if(startln > buf) { /* skip first line */ 1.162 + int idx = hdr->num_hdrfields++; 1.163 + hdr->hdrfields[idx] = malloc(linesz + 1); 1.164 + memcpy(hdr->hdrfields[idx], startln, linesz); 1.165 + hdr->hdrfields[idx][linesz] = 0; 1.166 + } 1.167 + startln = endln = buf + i + 1; 1.168 + } 1.169 + } 1.170 + 1.171 + return HTTP_HDR_OK; 1.172 +} 1.173 + 1.174 +void http_log_request(struct http_req_header *hdr) 1.175 +{ 1.176 + int i; 1.177 + 1.178 + logmsg("HTTP request header\n"); 1.179 + logmsg(" method: %s\n", http_method_str[hdr->method]); 1.180 + logmsg(" uri: %s\n", hdr->uri); 1.181 + logmsg(" version: %d.%d\n", hdr->ver_major, hdr->ver_minor); 1.182 + logmsg(" fields (%d):\n", hdr->num_hdrfields); 1.183 + 1.184 + for(i=0; i<hdr->num_hdrfields; i++) { 1.185 + logmsg(" %s\n", hdr->hdrfields[i]); 1.186 + } 1.187 + logmsg("\n"); 1.188 +} 1.189 + 1.190 +void http_destroy_request(struct http_req_header *hdr) 1.191 +{ 1.192 + int i; 1.193 + 1.194 + if(hdr->hdrfields) { 1.195 + for(i=0; i<hdr->num_hdrfields; i++) { 1.196 + free(hdr->hdrfields[i]); 1.197 + } 1.198 + free(hdr->hdrfields); 1.199 + } 1.200 + free(hdr->uri); 1.201 +} 1.202 + 1.203 +int http_init_resp(struct http_resp_header *resp) 1.204 +{ 1.205 + memset(resp, 0, sizeof *resp); 1.206 + resp->status = 200; 1.207 + resp->ver_major = resp->ver_minor = 1; 1.208 + resp->fields = 0; 1.209 + resp->num_fields = 0; 1.210 + 1.211 + return 0; 1.212 +} 1.213 + 1.214 +int http_add_resp_field(struct http_resp_header *resp, const char *fmt, ...) 1.215 +{ 1.216 + int sz; 1.217 + va_list ap; 1.218 + char *field, **newarr, tmp; 1.219 + 1.220 + va_start(ap, fmt); 1.221 + sz = vsnprintf(&tmp, 0, fmt, ap); 1.222 + va_end(ap); 1.223 + 1.224 + if(sz <= 0) sz = 1023; 1.225 + if(!(field = malloc(sz + 1))) { 1.226 + return -1; 1.227 + } 1.228 + va_start(ap, fmt); 1.229 + vsnprintf(field, sz + 1, fmt, ap); 1.230 + va_end(ap); 1.231 + 1.232 + if(!(newarr = realloc(resp->fields, (resp->num_fields + 1) * sizeof *resp->fields))) { 1.233 + free(field); 1.234 + return -1; 1.235 + } 1.236 + resp->fields = newarr; 1.237 + 1.238 + resp->fields[resp->num_fields++] = field; 1.239 + return 0; 1.240 +} 1.241 + 1.242 +void http_destroy_resp(struct http_resp_header *resp) 1.243 +{ 1.244 + int i; 1.245 + if(resp->fields) { 1.246 + for(i=0; i<resp->num_fields; i++) { 1.247 + free(resp->fields[i]); 1.248 + } 1.249 + free(resp->fields); 1.250 + resp->fields = 0; 1.251 + } 1.252 + resp->num_fields = 0; 1.253 +} 1.254 + 1.255 +int http_serialize_resp(struct http_resp_header *resp, char *buf) 1.256 +{ 1.257 + int i, stsize, size, *fsize; 1.258 + char *ptr, tmp; 1.259 + 1.260 + stsize = snprintf(&tmp, 0, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor, 1.261 + resp->status, http_strmsg(resp->status)); 1.262 + 1.263 + fsize = alloca(resp->num_fields * sizeof *fsize); 1.264 + 1.265 + size = stsize; 1.266 + for(i=0; i<resp->num_fields; i++) { 1.267 + int len = strlen(resp->fields[i]) + 2; 1.268 + fsize[i] = len; 1.269 + size += len; 1.270 + } 1.271 + size += 2; /* CRLF and null */ 1.272 + 1.273 + if(buf) { 1.274 + sprintf(buf, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor, 1.275 + resp->status, http_strmsg(resp->status)); 1.276 + 1.277 + ptr = buf + stsize; 1.278 + for(i=0; i<resp->num_fields; i++) { 1.279 + sprintf(ptr, "%s\r\n", resp->fields[i]); 1.280 + ptr += fsize[i]; 1.281 + } 1.282 + *ptr++ = '\r'; 1.283 + *ptr++ = '\n'; 1.284 + *ptr++ = 0; 1.285 + } 1.286 + 1.287 + return size; 1.288 +} 1.289 + 1.290 +const char *http_strmsg(int code) 1.291 +{ 1.292 + static const char **msgxxx[] = { 1.293 + 0, http_msg1xx, http_msg2xx, http_msg3xx, http_msg4xx, http_msg5xx 1.294 + }; 1.295 + static int msgcount[] = { 1.296 + 0, 1.297 + sizeof http_msg1xx / sizeof *http_msg1xx, 1.298 + sizeof http_msg2xx / sizeof *http_msg2xx, 1.299 + sizeof http_msg3xx / sizeof *http_msg3xx, 1.300 + sizeof http_msg4xx / sizeof *http_msg4xx, 1.301 + sizeof http_msg5xx / sizeof *http_msg5xx 1.302 + }; 1.303 + 1.304 + int type = code / 100; 1.305 + int idx = code % 100; 1.306 + 1.307 + if(type < 1 || type >= sizeof msgxxx / sizeof *msgxxx) { 1.308 + return "Invalid HTTP Status"; 1.309 + } 1.310 + 1.311 + if(idx < 0 || idx >= msgcount[type]) { 1.312 + return "Unknown HTTP Status"; 1.313 + } 1.314 + 1.315 + return msgxxx[type][idx]; 1.316 +} 1.317 + 1.318 +static enum http_method parse_method(const char *s) 1.319 +{ 1.320 + int i; 1.321 + for(i=0; http_method_str[i]; i++) { 1.322 + if(strcmp(s, http_method_str[i]) == 0) { 1.323 + return (enum http_method)i; 1.324 + } 1.325 + } 1.326 + return HTTP_UNKNOWN; 1.327 +}