tinywebd

changeset 5:def49a046566

serialization of responses
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 17 Apr 2015 01:57:00 +0300
parents 9e054c002489
children 4f191dbfac7e
files src/http.c src/http.h src/main.c
diffstat 3 files changed, 187 insertions(+), 3 deletions(-) [+]
line diff
     1.1 --- a/src/http.c	Thu Apr 16 17:34:15 2015 +0300
     1.2 +++ b/src/http.c	Fri Apr 17 01:57:00 2015 +0300
     1.3 @@ -1,6 +1,7 @@
     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 @@ -195,6 +196,91 @@
    1.12  	free(hdr->uri);
    1.13  }
    1.14  
    1.15 +int http_init_resp(struct http_resp_header *resp)
    1.16 +{
    1.17 +	memset(resp, 0, sizeof *resp);
    1.18 +	resp->status = 200;
    1.19 +	resp->ver_major = resp->ver_minor = 1;
    1.20 +	resp->fields = 0;
    1.21 +	resp->num_fields = 0;
    1.22 +
    1.23 +	return 0;
    1.24 +}
    1.25 +
    1.26 +int http_add_resp_field(struct http_resp_header *resp, const char *fmt, ...)
    1.27 +{
    1.28 +	int sz;
    1.29 +	va_list ap;
    1.30 +	char *field, *newarr, tmp;
    1.31 +
    1.32 +	va_start(ap, fmt);
    1.33 +	sz = vsnprintf(&tmp, 0, fmt, ap);
    1.34 +	va_end(ap);
    1.35 +
    1.36 +	if(sz <= 0) sz = 1023;
    1.37 +	if(!(field = malloc(sz + 1))) {
    1.38 +		return -1;
    1.39 +	}
    1.40 +	va_start(ap, fmt);
    1.41 +	vsnprintf(field, sz + 1, fmt, ap);
    1.42 +	va_end(ap);
    1.43 +
    1.44 +	if(!(newarr = realloc(resp->fields, (resp->num_fields + 1) * sizeof *resp->fields))) {
    1.45 +		free(field);
    1.46 +		return -1;
    1.47 +	}
    1.48 +	resp->fields[resp->num_fields++] = newarr;
    1.49 +	return 0;
    1.50 +}
    1.51 +
    1.52 +void http_destroy_resp(struct http_resp_header *resp)
    1.53 +{
    1.54 +	int i;
    1.55 +	if(resp->fields) {
    1.56 +		for(i=0; i<resp->num_fields; i++) {
    1.57 +			free(resp->fields[i]);
    1.58 +		}
    1.59 +		free(resp->fields);
    1.60 +		resp->fields = 0;
    1.61 +	}
    1.62 +	resp->num_fields = 0;
    1.63 +}
    1.64 +
    1.65 +int http_serialize_resp(struct http_resp_header *resp, char *buf)
    1.66 +{
    1.67 +	int i, stsize, size, *fsize;
    1.68 +	char *ptr, tmp;
    1.69 +
    1.70 +	stsize = snprintf(&tmp, 0, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor,
    1.71 +			resp->status, http_strmsg(resp->status));
    1.72 +
    1.73 +	fsize = alloca(resp->num_fields * sizeof *fsize);
    1.74 +
    1.75 +	size = stsize;
    1.76 +	for(i=0; i<resp->num_fields; i++) {
    1.77 +		int len = strlen(resp->fields[i]) + 2;
    1.78 +		fsize[i] = len;
    1.79 +		size += len;
    1.80 +	}
    1.81 +	size += 2;	/* CRLF and null */
    1.82 +
    1.83 +	if(buf) {
    1.84 +		sprintf(buf, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor,
    1.85 +				resp->status, http_strmsg(resp->status));
    1.86 +
    1.87 +		ptr = buf + stsize;
    1.88 +		for(i=0; i<resp->num_fields; i++) {
    1.89 +			sprintf(ptr, "%s\r\n", resp->fields[i]);
    1.90 +			ptr += fsize[i];
    1.91 +		}
    1.92 +		*ptr++ = '\r';
    1.93 +		*ptr++ = '\n';
    1.94 +		*ptr++ = 0;
    1.95 +	}
    1.96 +
    1.97 +	return size;
    1.98 +}
    1.99 +
   1.100  const char *http_strmsg(int code)
   1.101  {
   1.102  	static const char **msgxxx[] = {
     2.1 --- a/src/http.h	Thu Apr 16 17:34:15 2015 +0300
     2.2 +++ b/src/http.h	Fri Apr 17 01:57:00 2015 +0300
     2.3 @@ -24,6 +24,13 @@
     2.4  	int body_offset;
     2.5  };
     2.6  
     2.7 +struct http_resp_header {
     2.8 +	int status;
     2.9 +	int ver_major, ver_minor;
    2.10 +	char **fields;
    2.11 +	int num_fields;
    2.12 +};
    2.13 +
    2.14  #define HTTP_HDR_OK			0
    2.15  #define HTTP_HDR_INVALID	-1
    2.16  #define HTTP_HDR_NOMEM		-2
    2.17 @@ -33,6 +40,11 @@
    2.18  void http_print_header(struct http_req_header *hdr);
    2.19  void http_destroy_header(struct http_req_header *hdr);
    2.20  
    2.21 +int http_init_resp(struct http_resp_header *resp);
    2.22 +int http_add_resp_field(struct http_resp_header *resp, const char *fmt, ...);
    2.23 +void http_destroy_resp(struct http_resp_header *resp);
    2.24 +int http_serialize_resp(struct http_resp_header *resp, char *buf);
    2.25 +
    2.26  const char *http_strmsg(int code);
    2.27  
    2.28  #endif	/* HTTP_H_ */
     3.1 --- a/src/main.c	Thu Apr 16 17:34:15 2015 +0300
     3.2 +++ b/src/main.c	Fri Apr 17 01:57:00 2015 +0300
     3.3 @@ -6,11 +6,14 @@
     3.4  #include <errno.h>
     3.5  #include <unistd.h>
     3.6  #include <fcntl.h>
     3.7 +#include <sys/stat.h>
     3.8  #include <sys/select.h>
     3.9 +#include <sys/mman.h>
    3.10  #include <sys/types.h>
    3.11  #include <sys/socket.h>
    3.12  #include <arpa/inet.h>
    3.13  #include "http.h"
    3.14 +#include "mime.h"
    3.15  
    3.16  /* HTTP version */
    3.17  #define HTTP_VER_MAJOR	1
    3.18 @@ -31,7 +34,7 @@
    3.19  int accept_conn(int lis);
    3.20  void close_conn(struct client *c);
    3.21  int handle_client(struct client *c);
    3.22 -void do_get_head(struct client *c);
    3.23 +int do_get(struct client *c, const char *uri, int with_body);
    3.24  void respond_error(struct client *c, int errcode);
    3.25  void sighandler(int s);
    3.26  int parse_args(int argc, char **argv);
    3.27 @@ -40,6 +43,14 @@
    3.28  static int port = 8080;
    3.29  static struct client *clist;
    3.30  
    3.31 +static const char *indexfiles[] = {
    3.32 +	"index.cgi",
    3.33 +	"index.html",
    3.34 +	"index.htm",
    3.35 +	0
    3.36 +};
    3.37 +
    3.38 +
    3.39  int main(int argc, char **argv)
    3.40  {
    3.41  	if(parse_args(argc, argv) == -1) {
    3.42 @@ -209,8 +220,11 @@
    3.43  	/* we only support GET and HEAD at this point, so freak out on anything else */
    3.44  	switch(hdr.method) {
    3.45  	case HTTP_GET:
    3.46 +		do_get(c, hdr.uri, 1);
    3.47 +		break;
    3.48 +
    3.49  	case HTTP_HEAD:
    3.50 -		do_get_head(c);
    3.51 +		do_get(c, hdr.uri, 0);
    3.52  		break;
    3.53  
    3.54  	default:
    3.55 @@ -222,8 +236,80 @@
    3.56  	return 0;
    3.57  }
    3.58  
    3.59 -void do_get_head(struct client *c)
    3.60 +int do_get(struct client *c, const char *uri, int with_body)
    3.61  {
    3.62 +	const char *ptr;
    3.63 +	struct http_resp_header resp;
    3.64 +
    3.65 +	if((ptr = strstr(uri, "://"))) {
    3.66 +		/* skip the host part */
    3.67 +		if(!(uri = strchr(ptr + 3, '/'))) {
    3.68 +			respond_error(c, 404);
    3.69 +			return -1;
    3.70 +		}
    3.71 +		++uri;
    3.72 +	}
    3.73 +
    3.74 +	if(*uri) {
    3.75 +		struct stat st;
    3.76 +		char *path = 0;
    3.77 +		char *buf;
    3.78 +		const char *type;
    3.79 +		int fd, size;
    3.80 +
    3.81 +		if(stat(uri, &st) == -1) {
    3.82 +			respond_error(c, 404);
    3.83 +			return -1;
    3.84 +		}
    3.85 +
    3.86 +		if(S_ISDIR(st.st_mode)) {
    3.87 +			int i;
    3.88 +			path = alloca(strlen(uri) + 64);
    3.89 +
    3.90 +			for(i=0; indexfiles[i]; i++) {
    3.91 +				sprintf(path, "%s/%s", uri, indexfiles[i]);
    3.92 +				if(stat(path, &st) == 0 && !S_ISDIR(st.st_mode)) {
    3.93 +					break;
    3.94 +				}
    3.95 +			}
    3.96 +
    3.97 +			if(indexfiles[i] == 0) {
    3.98 +				respond_error(c, 404);
    3.99 +				return -1;
   3.100 +			}
   3.101 +		} else {
   3.102 +			path = (char*)uri;
   3.103 +		}
   3.104 +
   3.105 +		if((fd = open(path, O_RDONLY)) == -1) {
   3.106 +			respond_error(c, 403);
   3.107 +			return -1;
   3.108 +		}
   3.109 +
   3.110 +		/* construct response header */
   3.111 +		http_init_resp(&resp);
   3.112 +		http_add_resp_field(&resp, "Content-Length: %d", st.st_size);
   3.113 +		if((type = mime_type(path))) {
   3.114 +			http_add_resp_field(&resp, "Content-Type: %s", type);
   3.115 +		}
   3.116 +
   3.117 +		size = http_serialize_resp(&resp, 0);
   3.118 +		buf = alloca(size);
   3.119 +		http_serialize_resp(&resp, buf);
   3.120 +		send(c->s, buf, size, 0);
   3.121 +
   3.122 +		if(with_body) {
   3.123 +			char *cont = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   3.124 +			if(cont == (void*)-1) {
   3.125 +				respond_error(c, 503);
   3.126 +				close(fd);
   3.127 +				return -1;
   3.128 +			}
   3.129 +		}
   3.130 +
   3.131 +		close(fd);
   3.132 +	}
   3.133 +	return 0;
   3.134  }
   3.135  
   3.136  void respond_error(struct client *c, int errcode)