# HG changeset patch # User John Tsiombikas # Date 1429225020 -10800 # Node ID def49a046566c36d626ae74289a43ddcbf802fb1 # Parent 9e054c0024894f062ff9e25f00bac7b60faa434d serialization of responses diff -r 9e054c002489 -r def49a046566 src/http.c --- a/src/http.c Thu Apr 16 17:34:15 2015 +0300 +++ b/src/http.c Fri Apr 17 01:57:00 2015 +0300 @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include "http.h" @@ -195,6 +196,91 @@ free(hdr->uri); } +int http_init_resp(struct http_resp_header *resp) +{ + memset(resp, 0, sizeof *resp); + resp->status = 200; + resp->ver_major = resp->ver_minor = 1; + resp->fields = 0; + resp->num_fields = 0; + + return 0; +} + +int http_add_resp_field(struct http_resp_header *resp, const char *fmt, ...) +{ + int sz; + va_list ap; + char *field, *newarr, tmp; + + va_start(ap, fmt); + sz = vsnprintf(&tmp, 0, fmt, ap); + va_end(ap); + + if(sz <= 0) sz = 1023; + if(!(field = malloc(sz + 1))) { + return -1; + } + va_start(ap, fmt); + vsnprintf(field, sz + 1, fmt, ap); + va_end(ap); + + if(!(newarr = realloc(resp->fields, (resp->num_fields + 1) * sizeof *resp->fields))) { + free(field); + return -1; + } + resp->fields[resp->num_fields++] = newarr; + return 0; +} + +void http_destroy_resp(struct http_resp_header *resp) +{ + int i; + if(resp->fields) { + for(i=0; inum_fields; i++) { + free(resp->fields[i]); + } + free(resp->fields); + resp->fields = 0; + } + resp->num_fields = 0; +} + +int http_serialize_resp(struct http_resp_header *resp, char *buf) +{ + int i, stsize, size, *fsize; + char *ptr, tmp; + + stsize = snprintf(&tmp, 0, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor, + resp->status, http_strmsg(resp->status)); + + fsize = alloca(resp->num_fields * sizeof *fsize); + + size = stsize; + for(i=0; inum_fields; i++) { + int len = strlen(resp->fields[i]) + 2; + fsize[i] = len; + size += len; + } + size += 2; /* CRLF and null */ + + if(buf) { + sprintf(buf, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor, + resp->status, http_strmsg(resp->status)); + + ptr = buf + stsize; + for(i=0; inum_fields; i++) { + sprintf(ptr, "%s\r\n", resp->fields[i]); + ptr += fsize[i]; + } + *ptr++ = '\r'; + *ptr++ = '\n'; + *ptr++ = 0; + } + + return size; +} + const char *http_strmsg(int code) { static const char **msgxxx[] = { diff -r 9e054c002489 -r def49a046566 src/http.h --- a/src/http.h Thu Apr 16 17:34:15 2015 +0300 +++ b/src/http.h Fri Apr 17 01:57:00 2015 +0300 @@ -24,6 +24,13 @@ int body_offset; }; +struct http_resp_header { + int status; + int ver_major, ver_minor; + char **fields; + int num_fields; +}; + #define HTTP_HDR_OK 0 #define HTTP_HDR_INVALID -1 #define HTTP_HDR_NOMEM -2 @@ -33,6 +40,11 @@ void http_print_header(struct http_req_header *hdr); void http_destroy_header(struct http_req_header *hdr); +int http_init_resp(struct http_resp_header *resp); +int http_add_resp_field(struct http_resp_header *resp, const char *fmt, ...); +void http_destroy_resp(struct http_resp_header *resp); +int http_serialize_resp(struct http_resp_header *resp, char *buf); + const char *http_strmsg(int code); #endif /* HTTP_H_ */ diff -r 9e054c002489 -r def49a046566 src/main.c --- a/src/main.c Thu Apr 16 17:34:15 2015 +0300 +++ b/src/main.c Fri Apr 17 01:57:00 2015 +0300 @@ -6,11 +6,14 @@ #include #include #include +#include #include +#include #include #include #include #include "http.h" +#include "mime.h" /* HTTP version */ #define HTTP_VER_MAJOR 1 @@ -31,7 +34,7 @@ int accept_conn(int lis); void close_conn(struct client *c); int handle_client(struct client *c); -void do_get_head(struct client *c); +int do_get(struct client *c, const char *uri, int with_body); void respond_error(struct client *c, int errcode); void sighandler(int s); int parse_args(int argc, char **argv); @@ -40,6 +43,14 @@ static int port = 8080; static struct client *clist; +static const char *indexfiles[] = { + "index.cgi", + "index.html", + "index.htm", + 0 +}; + + int main(int argc, char **argv) { if(parse_args(argc, argv) == -1) { @@ -209,8 +220,11 @@ /* we only support GET and HEAD at this point, so freak out on anything else */ switch(hdr.method) { case HTTP_GET: + do_get(c, hdr.uri, 1); + break; + case HTTP_HEAD: - do_get_head(c); + do_get(c, hdr.uri, 0); break; default: @@ -222,8 +236,80 @@ return 0; } -void do_get_head(struct client *c) +int do_get(struct client *c, const char *uri, int with_body) { + const char *ptr; + struct http_resp_header resp; + + if((ptr = strstr(uri, "://"))) { + /* skip the host part */ + if(!(uri = strchr(ptr + 3, '/'))) { + respond_error(c, 404); + return -1; + } + ++uri; + } + + if(*uri) { + struct stat st; + char *path = 0; + char *buf; + const char *type; + int fd, size; + + if(stat(uri, &st) == -1) { + respond_error(c, 404); + return -1; + } + + if(S_ISDIR(st.st_mode)) { + int i; + path = alloca(strlen(uri) + 64); + + for(i=0; indexfiles[i]; i++) { + sprintf(path, "%s/%s", uri, indexfiles[i]); + if(stat(path, &st) == 0 && !S_ISDIR(st.st_mode)) { + break; + } + } + + if(indexfiles[i] == 0) { + respond_error(c, 404); + return -1; + } + } else { + path = (char*)uri; + } + + if((fd = open(path, O_RDONLY)) == -1) { + respond_error(c, 403); + return -1; + } + + /* construct response header */ + http_init_resp(&resp); + http_add_resp_field(&resp, "Content-Length: %d", st.st_size); + if((type = mime_type(path))) { + http_add_resp_field(&resp, "Content-Type: %s", type); + } + + size = http_serialize_resp(&resp, 0); + buf = alloca(size); + http_serialize_resp(&resp, buf); + send(c->s, buf, size, 0); + + if(with_body) { + char *cont = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if(cont == (void*)-1) { + respond_error(c, 503); + close(fd); + return -1; + } + } + + close(fd); + } + return 0; } void respond_error(struct client *c, int errcode)