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 @@
-
+
+
+
+ .
+
+
+
+
+ .
+
+
+
+
+
+ -lcunit
+
+
+
-
-
@@ -125,6 +146,10 @@
-
+ -
+
+ -
+
-
-
@@ -193,6 +218,8 @@
-
+ -
+
@@ -231,6 +258,24 @@
-
+
+
+
+ .
+
+
+
+
+ .
+
+
+
+
+
+ -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 */
+