diff --git a/nbproject/Makefile-Debug.mk b/nbproject/Makefile-Debug.mk index aca0d44..74fc791 100644 --- a/nbproject/Makefile-Debug.mk +++ b/nbproject/Makefile-Debug.mk @@ -39,6 +39,7 @@ OBJECTFILES= \ ${OBJECTDIR}/lib/ini.o \ ${OBJECTDIR}/src/config.o \ ${OBJECTDIR}/src/data-buffer.o \ + ${OBJECTDIR}/src/http-body.o \ ${OBJECTDIR}/src/http-reader.o \ ${OBJECTDIR}/src/http-server.o \ ${OBJECTDIR}/src/http.o \ @@ -103,6 +104,11 @@ ${OBJECTDIR}/src/data-buffer.o: nbproject/Makefile-${CND_CONF}.mk src/data-buffe ${RM} "$@.d" $(COMPILE.c) -g -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/data-buffer.o src/data-buffer.c +${OBJECTDIR}/src/http-body.o: nbproject/Makefile-${CND_CONF}.mk src/http-body.c + ${MKDIR} -p ${OBJECTDIR}/src + ${RM} "$@.d" + $(COMPILE.c) -g -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/http-body.o src/http-body.c + ${OBJECTDIR}/src/http-reader.o: nbproject/Makefile-${CND_CONF}.mk src/http-reader.c ${MKDIR} -p ${OBJECTDIR}/src ${RM} "$@.d" diff --git a/nbproject/Makefile-Release.mk b/nbproject/Makefile-Release.mk index 40274f1..7cd09f3 100644 --- a/nbproject/Makefile-Release.mk +++ b/nbproject/Makefile-Release.mk @@ -39,6 +39,7 @@ OBJECTFILES= \ ${OBJECTDIR}/lib/ini.o \ ${OBJECTDIR}/src/config.o \ ${OBJECTDIR}/src/data-buffer.o \ + ${OBJECTDIR}/src/http-body.o \ ${OBJECTDIR}/src/http-reader.o \ ${OBJECTDIR}/src/http-server.o \ ${OBJECTDIR}/src/http.o \ @@ -103,6 +104,11 @@ ${OBJECTDIR}/src/data-buffer.o: nbproject/Makefile-${CND_CONF}.mk src/data-buffe ${RM} "$@.d" $(COMPILE.c) -O2 -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/data-buffer.o src/data-buffer.c +${OBJECTDIR}/src/http-body.o: nbproject/Makefile-${CND_CONF}.mk src/http-body.c + ${MKDIR} -p ${OBJECTDIR}/src + ${RM} "$@.d" + $(COMPILE.c) -O2 -Werror -DINI_ALLOW_BOM=0 -DINI_ALLOW_MULTILINE=0 -D_GNU_SOURCE -Ilib -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/src/http-body.o src/http-body.c + ${OBJECTDIR}/src/http-reader.o: nbproject/Makefile-${CND_CONF}.mk src/http-reader.c ${MKDIR} -p ${OBJECTDIR}/src ${RM} "$@.d" diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index d36544f..ac49c0a 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -6,6 +6,7 @@ projectFiles="true"> src/config.h src/data-buffer.h + src/http-body.h src/http-reader.h src/http-server.h src/http.h @@ -14,6 +15,7 @@ src/log.h src/main.h src/mime.h + tests/minunit.h src/queue.h src/server-connection.h src/server-loop.h @@ -34,6 +36,7 @@ projectFiles="true"> src/config.c src/data-buffer.c + src/http-body.c src/http-reader.c src/http-server.c src/http.c @@ -109,6 +112,24 @@ + + + + . + + + + + . + + + + ${TESTDIR}/TestFiles/f1 + + -lcunit + + + @@ -125,6 +146,10 @@ + + + + @@ -193,6 +218,8 @@ + + @@ -231,6 +258,24 @@ + + + + . + + + + + . + + + + ${TESTDIR}/TestFiles/f1 + + -lcunit + + + @@ -247,6 +292,10 @@ + + + + @@ -315,6 +364,8 @@ + + diff --git a/src/http-body.c b/src/http-body.c new file mode 100644 index 0000000..d8cd11a --- /dev/null +++ b/src/http-body.c @@ -0,0 +1,249 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "ut/utstring.h" +#include "http-body.h" + +http_body* http_body_new(http_body_type type, void* dataptr) { + http_body *body = calloc(1, sizeof(http_body)); + ALLOC_CHECK(body); + + body->type = type; + body->rOffset = 0; + switch(body->type) { + case BODY_NONE: + break; + case BODY_STRING: + body->data.str = (char*)dataptr; + break; + case BODY_FILEMAP: + body->data.filemap = (file_map*)dataptr; + break; + case BODY_FILE: + body->data.file = (FILE*)dataptr; + break; + default: + errno = EINVAL; + fatal("Invalid http body type"); + break; + } + + return body; +} +void http_body_delete(http_body *body) { + assert(body!=NULL); + + switch(body->type) { + case BODY_NONE: + break; + case BODY_STRING: + if (body->data.str != NULL) { + free(body->data.str); + } + break; + case BODY_FILEMAP: + if (body->data.filemap != NULL) { + file_map_delete(body->data.filemap); + } + break; + case BODY_FILE: + if (body->data.file != NULL) { + fclose(body->data.file); + } + break; + default: + errno = EINVAL; + fatal("Invalid http body type"); + break; + } + free(body); +} + +size_t http_body_append_str(http_body *body, const char* str, ssize_t str_len) { + assert(body!=NULL); + assert(body->type==BODY_STRING); + assert(str!=NULL); + if (str_len == 0) { + return 0; + } + size_t new_len = strlen(body->data.str)+str_len+1; + + body->data.str = realloc(body->data.str, new_len); + ALLOC_CHECK(body->data.str); + body->data.str[new_len-1] = '\0'; + strncat(body->data.str, str, new_len-1); + return new_len-1; +} +size_t http_body_len(http_body *body) { + assert(body!=NULL); + size_t len = 0; + switch(body->type) { + case BODY_NONE: + break; + case BODY_STRING: + if (body->data.str !=NULL) { + len = strlen(body->data.str); + } + break; + case BODY_FILEMAP: + if (body->data.filemap != NULL) { + len = body->data.filemap->size; + } + break; + case BODY_FILE: + if (body->data.file != NULL) { + size_t curpos = ftell(body->data.file); + fseek(body->data.file, 0, SEEK_END); + len = ftell(body->data.file); + fseek(body->data.file, curpos, SEEK_SET); + } + break; + default: + errno = EINVAL; + fatal("Invalid http body type"); + break; + } + return len; +} + +http_body_write_result http_body_writeto_fd(http_body *body, int fd) { + assert(body!=NULL); + if (body->type!=BODY_NONE) { + assert(body->data.ptr!=NULL); + } + + char* buffer=NULL; + const size_t buffer_len = 16*1024; + + while(body->rOffsetrOffset; + errno = EINVAL; + ssize_t result = -1; + + switch(body->type) { + case BODY_STRING: + result = write(fd, body->data.str+body->rOffset, write_len); + break; + case BODY_FILEMAP: + result = write(fd, body->data.filemap->map+body->rOffset, write_len); + break; + case BODY_FILE: + if (buffer==NULL) { + buffer = calloc(buffer_len, sizeof(char)); + ALLOC_CHECK(buffer); + } + errno=0; + if (write_len > buffer_len) { + write_len = buffer_len; + } + size_t read_count = fread(buffer, sizeof(char), write_len, body->data.file); + if (read_count < write_len) { + if (ferror(body->data.file) != 0) { + free(buffer); + return HBWRITE_ERROR; + } + } + result = 0; + if (read_count > 0) { + result = write(fd, buffer, read_count); + } + if (feof(body->data.file) != 0) { + body->rOffset += result; + free(buffer); + return HBWRITE_DONE; + } + break; + }//switch body->type + + if (result < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return HBWRITE_BLOCKED; + } + warning(true, "Error writing http body"); + return HBWRITE_ERROR; + } + + body->rOffset += result; + }//While data remaining + + if (buffer != NULL) { + free(buffer); + } + errno = 0; + return HBWRITE_DONE; +} +http_body_write_result http_body_writeto_str(http_body *body, const char** str) { + assert(body!=NULL); + if (body->type!=BODY_NONE) { + assert(body->data.ptr!=NULL); + } + assert(str!=NULL); + + char* buffer=NULL; + const size_t buffer_len = 16*1024; + + while(body->rOffsetrOffset; + if (*str==NULL) { + *str = calloc(write_len, sizeof(char)); + } else { + *str = realloc(*str, sizeof(char)*(strlen(*str)+write_len+1)); + } + ALLOC_CHECK(*str); + + errno = EINVAL; + ssize_t result = -1; + + switch(body->type) { + case BODY_STRING: + case BODY_FILEMAP: + char* src = (body->type==BODY_STRING) ? body->data.str : body->data.filemap->map; + strncat(*str, src+body->rOffset, write_len); + result = write_len; + break; + case BODY_FILE: + if (buffer==NULL) { + buffer = calloc(buffer_len, sizeof(char)); + ALLOC_CHECK(buffer); + } + if (write_len > buffer_len) { + write_len = buffer_len; + } + size_t read_count = fread(buffer, sizeof(char), write_len, body->data.file); + if (read_count < write_len) { + if (ferror(body->data.file) != 0) { + free(buffer); + return HBWRITE_ERROR; + } + } + result = 0; + if (read_count > 0) { + strncat(*str, buffer, read_count); + result = read_count; + } + if (feof(body->data.file) != 0) { + body->rOffset += result; + free(buffer); + return HBWRITE_DONE; + } + break; + }//Switch body->type + + body->rOffset += result; + }//while data remaining + + if (buffer != NULL) { + free(buffer); + } + errno = 0; + return HBWRITE_DONE; +} +http_body_write_result http_body_writeto_utstring(http_body *body, UT_string *utstr) { + +} diff --git a/src/http-body.h b/src/http-body.h new file mode 100644 index 0000000..f8df1ac --- /dev/null +++ b/src/http-body.h @@ -0,0 +1,56 @@ +/* + * File: http-body.h + * Author: sam + * + * Created on 27 August 2014, 20:17 + */ + +#ifndef HTTP_BODY_H +#define HTTP_BODY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "util.h" +#include "ut/utstring.h" + + typedef enum http_body_type { + BODY_NONE, BODY_STRING, BODY_FILEMAP, BODY_FILE + } http_body_type; + + typedef enum http_body_write_result { + HBWRITE_DONE, HBWRITE_MORE, HBWRITE_BLOCKED, HBWRITE_ERROR + } http_body_write_result; + + typedef struct http_body { + http_body_type type; + size_t rOffset; + union { + char* str; + file_map *filemap; + FILE *file; + void *ptr; + } data; + } http_body; + + http_body* http_body_new(http_body_type type, void* dataptr); + void http_body_delete(http_body *body); + + size_t http_body_append_str(http_body *body, const char** str_ptr); + size_t http_body_len(http_body *body); + + http_body_write_result http_body_writeto_fd(http_body *body, int fd); + http_body_write_result http_body_writeto_str(http_body *body, const char* str, size_t str_len); + http_body_write_result http_body_writeto_utstring(http_body *body, UT_string *utstr); + + +#ifdef __cplusplus +} +#endif + +#endif /* HTTP_BODY_H */ + diff --git a/src/http.c b/src/http.c index dc2d5ac..61873e9 100644 --- a/src/http.c +++ b/src/http.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "ut/utlist.h" #include "ut/utarray.h" @@ -14,7 +15,7 @@ #include "version.h" void http_header_icd_init_f(void* elem) { - memset(elem, 1, sizeof(http_header)); + memset(elem, 0, sizeof(http_header)); } void http_header_icd_dtor_f(void* elem) { http_header *header = (http_header*)elem; @@ -25,6 +26,7 @@ void http_header_icd_dtor_f(void* elem) { UT_icd http_header_icd = {sizeof(http_header), http_header_icd_init_f, NULL, http_header_icd_dtor_f}; char* http_method_getstring(http_request_method method, char* method_other) { + switch(method) { case METHOD_GET: return "GET"; case METHOD_POST: return "POST"; @@ -35,7 +37,9 @@ char* http_method_getstring(http_request_method method, char* method_other) { case METHOD_TRACE: return "TRACE"; case METHOD_CONNECT:return "CONNECT"; case METHOD_INVALID:return ""; - case METHOD_OTHER: return method_other; + case METHOD_OTHER: + assert(method_other!=NULL); + return method_other; default: return ""; } } @@ -56,11 +60,10 @@ http_request_method http_method_fromstring(const char* method) { http_request_line *http_request_line_new(http_request_method method, const char* other) { http_request_line *req = calloc(1, sizeof(http_request_line)); - if (req == NULL) { - fatal("calloc failed"); - } + ALLOC_CHECK(req); req->method = method; if (req->method == METHOD_OTHER) { + assert(other!=NULL); req->method_other = calloc(strlen(other)+1, sizeof(char)); strcpy(req->method_other, other); } else { @@ -69,6 +72,8 @@ http_request_line *http_request_line_new(http_request_method method, const char* return req; } void http_request_line_delete(http_request_line *req) { + assert(req!=NULL); + free(req->method_other); free(req->uri); free(req); @@ -76,14 +81,13 @@ void http_request_line_delete(http_request_line *req) { http_response_line *http_response_line_new(uint16_t code) { http_response_line *resp = calloc(1, sizeof(http_response_line)); - if (resp == NULL) { - fatal("calloc failed"); - } + ALLOC_CHECK(resp); resp->code = code; resp->version = HTTP11; return resp; } char* http_response_line_get_message(http_response_line *resp) { + assert(resp!=NULL); if (resp->custom_message != NULL) { return resp->custom_message; } @@ -131,16 +135,18 @@ char* http_response_line_get_message(http_response_line *resp) { } } void http_response_line_delete(http_response_line *resp) { + assert(resp!=NULL); free(resp->custom_message); free(resp); } http_header *http_header_new(const char* name, const char* content) { + assert(name!=NULL); + http_header *header = calloc(1, sizeof(http_header)); - if (header == NULL) { - fatal("calloc failed"); - } + ALLOC_CHECK(header); header->name = calloc(strlen(name)+1, sizeof(char)); + ALLOC_CHECK(header->name); strcpy(header->name, name); if (content != NULL) { @@ -150,33 +156,38 @@ http_header *http_header_new(const char* name, const char* content) { return header; } void http_header_append_content(http_header *header, const char* content) { + assert(header!=NULL); + assert(content!=NULL); + if (header->content == NULL) { header->content = calloc(strlen(content)+1, sizeof(char)); - if (header->content == NULL) { - fatal("calloc failed"); - } + ALLOC_CHECK(header->content); strcpy(header->content, content); } else { size_t newlen = strlen(header->content) + strlen(content) + 1; header->content = realloc(header->content, newlen); - if (header->content == NULL) { - fatal("calloc failed"); - } + ALLOC_CHECK(header->content); strcat(header->content, content); } } void http_header_delete(http_header *header) { + assert(header!=NULL); + free(header->name); free(header->content); free(header); } http_header_list* http_header_list_new() { - http_header_list* list = NULL; - utarray_new(list, &http_header_icd); + http_header_list* list = calloc(1, sizeof(http_header_list)); + ALLOC_CHECK(list); + utarray_init(list, &http_header_icd); return list; } void http_header_list_add(http_header_list* list, http_header *header, bool replace) { + assert(list!=NULL); + assert(header!=NULL); + if (replace == true) { http_header_list_remove(list, header->name); } @@ -184,6 +195,12 @@ void http_header_list_add(http_header_list* list, http_header *header, bool repl free(header); } http_header* http_header_list_get(http_header_list* list, const char* name) { + assert(list!=NULL); + + if (name == NULL || strlen(name) == 0) { + return NULL; + } + http_header *elem; HTTP_HEADER_FOREACH(list, elem) { if (strcmp(elem->name, name) == 0) { @@ -193,6 +210,10 @@ http_header* http_header_list_get(http_header_list* list, const char* name) { return NULL; } http_header** http_header_list_getall(http_header_list* list, const char* name, size_t *out_header_count) { + assert(list!=NULL); + assert(name!=NULL); + assert(out_header_count!=NULL); + http_header **headers = NULL; size_t count = 0; http_header *elem; @@ -200,6 +221,7 @@ http_header** http_header_list_getall(http_header_list* list, const char* name, if (strcmp(elem->name, name) == 0) { count++; headers = realloc(headers, count * sizeof(http_header*)); + ALLOC_CHECK(headers); headers[count-1] = elem; } } @@ -207,6 +229,9 @@ http_header** http_header_list_getall(http_header_list* list, const char* name, return headers; } void http_header_list_remove(http_header_list *list, const char* name) { + assert(list!=NULL); + assert(name!=NULL); + http_header **headers; size_t count; headers = http_header_list_getall(list, name, &count); @@ -217,32 +242,37 @@ void http_header_list_remove(http_header_list *list, const char* name) { free(headers); } void http_header_list_delete(http_header_list *list) { + assert(list!=NULL); + utarray_free(list); } http_request *http_request_new() { http_request *req = calloc(1, sizeof(http_request)); - if (req == NULL) { - fatal("calloc failed"); - } + ALLOC_CHECK(req); req->headers = http_header_list_new(); req->parsestatus = PARSE_REQUESTLINE; return req; } void http_request_append_body(http_request *req, const char* body) { + assert(req!=NULL); + assert(body!=NULL); + uint32_t bodylen = 0; if (req->body != NULL) { bodylen = strlen(req->body); } bodylen += strlen(body) + 1; req->body = realloc(req->body, bodylen * sizeof(char)); - if (req->body == NULL) { - fatal("calloc failed"); - } + ALLOC_CHECK(req->body); strcat(req->body, body); } char* http_request_write(http_request *req) { + assert(req!=NULL); + assert(req->req!=NULL); + UT_string *output = calloc(1, sizeof(UT_string)); + ALLOC_CHECK(output); utstring_init(output); utstring_printf(output, "%s %s %s\r\n", @@ -259,51 +289,82 @@ char* http_request_write(http_request *req) { utstring_printf(output, "\r\n"); - if (req->body != NULL) { - utstring_printf(output, "%s\r\n", req->body); + if (req->body_type == BODY_STRING) { + if (req->body.str != NULL) { + utstring_printf(output, "%s\r\n", req->body); + } + } else if (req->body_type == BODY_FILE) { + if (req->body.file != NULL) { + utstring_printf(output, "%s\r\n", req->body.file->map); + } } char* result = utstring_body(output); free(output); return result; } void http_request_delete(http_request *req) { + assert(req!=NULL); + if (req->req != NULL) { http_request_line_delete(req->req); } http_header_list_delete(req->headers); - free(req->body); + if (req->body_type == BODY_STRING) { + free(req->body.str); + } else if (req->body_type == BODY_FILE && req->body.file != NULL) { + file_map_delete(req->body.file); + } free(req); } http_response* http_response_new(http_response_line *resp) { + assert(resp!=NULL); + http_response *response = calloc(1, sizeof(http_response)); + ALLOC_CHECK(response); response->resp = resp; response->headers = http_header_list_new(); response->body_chunked = false; - response->body = NULL; + response->body_type = BODY_NONE; return response; } void http_response_append_body(http_response *resp, const char* body) { + assert(resp!=NULL); + assert(body!=NULL); + assert(resp->body_type == BODY_STRING); + size_t bodylen = 0; - if (resp->body != NULL) { - bodylen = strlen(resp->body); + if (resp->body.str != NULL) { + bodylen = strlen(resp->body.str); } bodylen += strlen(body) + 1; - if (resp->body == NULL) { - resp->body = calloc(bodylen, sizeof(char)); + if (resp->body.str == NULL) { + resp->body.str = calloc(bodylen, sizeof(char)); + ALLOC_CHECK(resp->body.str); } else { - resp->body = realloc(resp->body, bodylen * sizeof(char)); + resp->body.str = realloc(resp->body.str, bodylen * sizeof(char)); + ALLOC_CHECK(resp->body.str); } - strcat(resp->body, body); + strcat(resp->body.str, body); } void http_response_delete(http_response *resp) { + assert(resp!=NULL); + http_response_line_delete(resp->resp); http_header_list_delete(resp->headers); - free(resp->body); + if (resp->body_type == BODY_STRING) { + free(resp->body.str); + } else if (resp->body_type == BODY_FILE && resp->body.file != NULL) { + file_map_delete(resp->body.file); + } free(resp); } char* http_response_write(http_response *resp) { + assert(resp!=NULL); + assert(resp->resp !=NULL); + UT_string *output = calloc(1, sizeof(UT_string)); + ALLOC_CHECK(output); utstring_init(output); if (resp->resp->version == HTTP10) { @@ -318,8 +379,10 @@ char* http_response_write(http_response *resp) { if (resp->body_chunked == false) { //Add content length header uint32_t messageLength = 0; - if (resp->body != NULL) { - messageLength = strlen(resp->body); + if (resp->body_type == BODY_STRING) { + messageLength = strlen(resp->body.str); + } else if (resp->body_type == BODY_FILE) { + messageLength = resp->body.file->size; } char messageLengthStr[100]; snprintf(messageLengthStr, 99, "%u", messageLength); @@ -352,15 +415,22 @@ char* http_response_write(http_response *resp) { } utstring_printf(output, "\r\n"); - //Write the request - if (resp->body_chunked == false && resp->body != NULL) { - utstring_printf(output, "%s\r\n", resp->body); - } - if (resp->body_chunked == true && resp->body != NULL) { - char *chunks = http_chunks_write(resp->body); - utstring_printf(output, "%s", chunks); - free(chunks); + //Write the request (if string) + if (resp->body_type == BODY_STRING) { + if (resp->body_chunked == false && resp->body.str != NULL) { + utstring_printf(output, "%s\r\n", resp->body.str); + } + if (resp->body_chunked == true && resp->body.str != NULL) { + http_chunks_write(resp->body.str, output); + } + } else if (resp->body_type == BODY_FILE) { + if (resp->body_chunked == false) { + file_map_copyto_utstring(resp->body.file, output); + } else { + http_chunks_write(resp->body.file->map, output); + } } + char* outputStr = utstring_body(output); free(output); return outputStr; @@ -368,6 +438,7 @@ char* http_response_write(http_response *resp) { http_response* http_response_create_builtin(uint16_t code, const char* errmsg) { http_response *resp = http_response_new(http_response_line_new(code)); + resp->body_type = BODY_STRING; http_header_list_add(resp->headers, http_header_new(HEADER_CONTENT_TYPE, "text/html"), false); @@ -384,48 +455,44 @@ http_response* http_response_create_builtin(uint16_t code, const char* errmsg) { char* title_message = http_response_line_get_message(resp->resp); snprintf(buffer, 1023, "%s %hu - %s", (code >= 400) ? "Error" : "Response Code", code, title_message); - resp->body = str_replace(resp->body, "{{title}}", buffer); - resp->body = str_replace(resp->body, "{{body_title}}", buffer); - resp->body = str_replace(resp->body, "{{message}}", errmsg); + resp->body.str = str_replace(resp->body.str, "{{title}}", buffer); + resp->body.str = str_replace(resp->body.str, "{{body_title}}", buffer); + resp->body.str = str_replace(resp->body.str, "{{message}}", errmsg); return resp; } - -char* http_chunks_write(char* source) { - size_t sourcelen = strlen(source); +void http_chunks_write(char* source, UT_string* output) { + assert(source!=NULL); + assert(output!=NULL); + + size_t source_len = strlen(source); - UT_string *output = calloc(1, sizeof(UT_string)); - utstring_init(output); char buffer[HTTP_CHUNK_MAXSIZE+1] = {0}; - //determine max chars for length line + //determine how long the length line will be sprintf(buffer, "%zx;\r\n", (size_t)HTTP_CHUNK_MAXSIZE); size_t overhead = strlen(buffer); - overhead+=3;//account for terminating CRLF + \0 + overhead+=3;//account for terminating CR + LF + \0 buffer[0] = '\0'; size_t i = 0; - while (i < sourcelen) { + while (i < source_len) { //how much can we write in this chunk? - size_t sourcerem = sourcelen - i; - size_t chunklen = - sourcerem > HTTP_CHUNK_MAXSIZE-overhead - ? HTTP_CHUNK_MAXSIZE-overhead - : sourcerem; - utstring_printf(output, "%zx;\r\n", chunklen); - memset(&buffer, 0, sizeof(buffer)); - strncpy(buffer, source+i, chunklen); - utstring_printf(output, "%s\r\n", buffer); - i += chunklen; + size_t sourcerem = source_len - i; + size_t chunk_len = sourcerem; + if (chunk_len > HTTP_CHUNK_MAXSIZE-overhead) { + chunk_len = HTTP_CHUNK_MAXSIZE-overhead; + } + //Write chunk length + utstring_printf(output, "%zx;\r\n", chunk_len); + //Write chunk data + utstring_bincpy(output, source+i, chunk_len); + i += chunk_len; } - char* outputstr = utstring_body(output); - free(output); - return outputstr; } -char* http_chunks_terminate(http_header_list *footers) { - UT_string *output = calloc(1, sizeof(UT_string)); - utstring_init(output); +void http_chunks_terminate(http_header_list *footers, UT_string* output) { + assert(output!=NULL); utstring_printf(output, "0\r\n"); if (footers != NULL) { @@ -436,29 +503,28 @@ char* http_chunks_terminate(http_header_list *footers) { } } utstring_printf(output, "\r\n"); - - char* outputstr = utstring_body(output); - free(output); - return outputstr; } http_response_list* http_response_list_new() { http_response_list *list = calloc(1, sizeof(http_response_list)); - assert(list != NULL); + ALLOC_CHECK(list); list->first = NULL; return list; } void http_response_list_append(http_response_list *list, http_response* response) { assert(list != NULL); assert(response != NULL); + LL_APPEND(list->first, response); } http_response* http_response_list_next(http_response_list *list) { assert(list != NULL); + return http_response_list_next2(list, true); } http_response* http_response_list_next2(http_response_list *list, bool remove) { assert(list != NULL); + if (list->first == NULL) { return NULL; } diff --git a/src/http.h b/src/http.h index d004cd4..8387fac 100644 --- a/src/http.h +++ b/src/http.h @@ -13,6 +13,8 @@ #include #include "ut/utarray.h" +#include "util.h" +#include "http-body.h" #ifdef __cplusplus extern "C" { @@ -81,15 +83,18 @@ extern "C" { http_request_line *req; http_request_parsestatus parsestatus; http_header_list *headers; - char *body; + http_body_type body_type; + http_body body; struct http_request *next; } http_request; + typedef struct http_response { http_response_line *resp; http_header_list *headers; bool body_chunked; - char* body; + http_body_type body_type; + http_body body; struct http_response *next; } http_response; @@ -131,8 +136,8 @@ extern "C" { char* http_response_write(http_response *resp); http_response* http_response_create_builtin(uint16_t code, const char* errmsg); - char* http_chunks_write(char* source); - char* http_chunks_terminate(http_header_list *footers); + void http_chunks_write(char* source, UT_string* output); + void http_chunks_terminate(http_header_list *footers, UT_string* output); http_response_list* http_response_list_new(); void http_response_list_append(http_response_list *list, http_response* response); diff --git a/src/server-loop-read.c b/src/server-loop-read.c index 8c38ef7..7a6d487 100644 --- a/src/server-loop-read.c +++ b/src/server-loop-read.c @@ -70,6 +70,9 @@ void* server_loop_read(void* arg) { LL_APPEND(conn->pending_requests, conn->parse_state->current_request); server_parser_status_reset(conn->parse_state); queue_add(conn->server->pools[POOL_WORKER]->queue, queue_item_new2("REQ", (void*)conn)); + } else if (conn->parse_state->current_request != NULL && conn->parse_state->current_request->req!=NULL) { + //Send 100 Continue message + } if (error = true) { //Write any error directly, this will also close the connection diff --git a/src/util.c b/src/util.c index a35bc2d..dd09c3c 100644 --- a/src/util.c +++ b/src/util.c @@ -10,6 +10,10 @@ #include #include #include +#include +#include + +#include "ut/utstring.h" #include "util.h" #include "log.h" @@ -140,34 +144,6 @@ char* str_trimwhitespace(char *str) return str; } -file_map* file_map_new(const char* filename) { - - int fd = open(filename, O_RDONLY); - if (fd < 0) { - warning(true, "Failed to open file for memory mapping"); - return NULL; - } - size_t size = lseek(fd, 0L, SEEK_END); - void* map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - if (map == MAP_FAILED) { - warning(true, "Failed to mmap file"); - close(fd); - return NULL; - } - close(fd); - - file_map* filemap = calloc(1, sizeof(file_map)); - filemap->map = (char*)map; - filemap->size = size; - return filemap; -} -void file_map_delete(file_map* file) { - if (munmap((void*)file->map, file->size) < 0) { - warning(true, "failed to unmap file"); - } - free(file); -} - char* str_replace(char *haystack, const char *search, const char *replacement) { size_t haystacklen = strlen(haystack); @@ -203,4 +179,49 @@ char* str_replace(char *haystack, const char *search, const char *replacement) { result = newstr; } return result; -} \ No newline at end of file +} + +file_map* file_map_new(const char* filename) { + + int fd = open(filename, O_RDONLY); + if (fd < 0) { + warning(true, "Failed to open file for memory mapping"); + return NULL; + } + size_t size = lseek(fd, 0L, SEEK_END); + void* map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (map == MAP_FAILED) { + warning(true, "Failed to mmap file"); + close(fd); + return NULL; + } + close(fd); + + file_map* filemap = calloc(1, sizeof(file_map)); + filemap->map = (char*)map; + filemap->size = size; + return filemap; +} +void file_map_delete(file_map* file) { + if (munmap((void*)file->map, file->size) < 0) { + warning(true, "failed to unmap file"); + } + free(file); +} +char* file_map_copyto_string(file_map* map, char* str, size_t str_len) { + assert(map!=NULL); + + size_t newsize = str_len+map->size; + str = realloc(str, (newsize+1)*sizeof(char)); + ALLOC_CHECK(str); + strncat(str, map->map, map->size); + + return str; +} +void file_map_copyto_utstring(file_map* map, UT_string* string) { + assert(map!=NULL); + assert(str!=NULL); + + utstring_bincpy(string, map->map, map->size); +} + diff --git a/src/util.h b/src/util.h index 3eddd56..351a1a0 100644 --- a/src/util.h +++ b/src/util.h @@ -12,8 +12,18 @@ extern "C" { #endif +#include #include +#include "ut/utstring.h" + +#define ALLOC_CHECK(ptr) \ + do { \ + if (ptr==NULL) { \ + fatal("Memory allocation failed. Out of memory?"); \ + } \ + } while(0) + typedef struct file_map { char* map; size_t size; @@ -29,7 +39,8 @@ extern "C" { file_map* file_map_new(const char* filename); void file_map_delete(file_map* map); - + char* file_map_copyto_string(file_map* map, char* str, size_t str_len); + void file_map_copyto_utstring(file_map* map, UT_string* string); #ifdef __cplusplus } diff --git a/tests/minunit.h b/tests/minunit.h new file mode 100644 index 0000000..2ab906f --- /dev/null +++ b/tests/minunit.h @@ -0,0 +1,64 @@ +/* + * File: minunit.h + * Author: Zed. A. Shaw, Sam + * + * @see http://c.learncodethehardway.org/book/ex30.html + * + * Created on 27 August 2014, 22:14 + */ + +#ifndef MINUNIT_H +#define MINUNIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define log_err(message) printf("\tError: %s\n", message) + +#define mu_suite_start() char *message = NULL + +#define mu_assert(test, message) \ + do { \ + if (!(test)) { \ + log_err(message); \ + return message; \ + } \ + } while(0) + +#define mu_run_test(test) \ + do { \ + printf("\n-----%s", " " #test); \ + message = test(); \ + tests_run++; \ + if (message) { return message; } \ + while(0) + +#define RUN_TESTS(name) \ + int main(int argc, char *argv[]) { \ + tests_run = 0; \ + argc = 1; \ + printf("----\nRUNNING: %s\n", argv[0]); \ + char *result = name(); \ + if (result != 0) { \ + printf("FAILED: %s\n", result); \ + } \ + else { \ + printf("ALL TESTS PASSED\n"); \ + } \ + printf("Tests run: %d\n", tests_run); \ + exit(result == 0 ? EXIT_SUCCESS : EXIT_FAILURE);\ + } + + +int tests_run; + +#ifdef __cplusplus +} +#endif + +#endif /* MINUNIT_H */ +