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 +}