Working on http body code

This commit is contained in:
2014-08-27 23:12:41 +01:00
parent 053a6a8e21
commit e15e94dad2
11 changed files with 650 additions and 112 deletions

View File

@@ -39,6 +39,7 @@ OBJECTFILES= \
${OBJECTDIR}/lib/ini.o \ ${OBJECTDIR}/lib/ini.o \
${OBJECTDIR}/src/config.o \ ${OBJECTDIR}/src/config.o \
${OBJECTDIR}/src/data-buffer.o \ ${OBJECTDIR}/src/data-buffer.o \
${OBJECTDIR}/src/http-body.o \
${OBJECTDIR}/src/http-reader.o \ ${OBJECTDIR}/src/http-reader.o \
${OBJECTDIR}/src/http-server.o \ ${OBJECTDIR}/src/http-server.o \
${OBJECTDIR}/src/http.o \ ${OBJECTDIR}/src/http.o \
@@ -103,6 +104,11 @@ ${OBJECTDIR}/src/data-buffer.o: nbproject/Makefile-${CND_CONF}.mk src/data-buffe
${RM} "$@.d" ${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 $(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 ${OBJECTDIR}/src/http-reader.o: nbproject/Makefile-${CND_CONF}.mk src/http-reader.c
${MKDIR} -p ${OBJECTDIR}/src ${MKDIR} -p ${OBJECTDIR}/src
${RM} "$@.d" ${RM} "$@.d"

View File

@@ -39,6 +39,7 @@ OBJECTFILES= \
${OBJECTDIR}/lib/ini.o \ ${OBJECTDIR}/lib/ini.o \
${OBJECTDIR}/src/config.o \ ${OBJECTDIR}/src/config.o \
${OBJECTDIR}/src/data-buffer.o \ ${OBJECTDIR}/src/data-buffer.o \
${OBJECTDIR}/src/http-body.o \
${OBJECTDIR}/src/http-reader.o \ ${OBJECTDIR}/src/http-reader.o \
${OBJECTDIR}/src/http-server.o \ ${OBJECTDIR}/src/http-server.o \
${OBJECTDIR}/src/http.o \ ${OBJECTDIR}/src/http.o \
@@ -103,6 +104,11 @@ ${OBJECTDIR}/src/data-buffer.o: nbproject/Makefile-${CND_CONF}.mk src/data-buffe
${RM} "$@.d" ${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 $(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 ${OBJECTDIR}/src/http-reader.o: nbproject/Makefile-${CND_CONF}.mk src/http-reader.c
${MKDIR} -p ${OBJECTDIR}/src ${MKDIR} -p ${OBJECTDIR}/src
${RM} "$@.d" ${RM} "$@.d"

View File

@@ -6,6 +6,7 @@
projectFiles="true"> projectFiles="true">
<itemPath>src/config.h</itemPath> <itemPath>src/config.h</itemPath>
<itemPath>src/data-buffer.h</itemPath> <itemPath>src/data-buffer.h</itemPath>
<itemPath>src/http-body.h</itemPath>
<itemPath>src/http-reader.h</itemPath> <itemPath>src/http-reader.h</itemPath>
<itemPath>src/http-server.h</itemPath> <itemPath>src/http-server.h</itemPath>
<itemPath>src/http.h</itemPath> <itemPath>src/http.h</itemPath>
@@ -14,6 +15,7 @@
<itemPath>src/log.h</itemPath> <itemPath>src/log.h</itemPath>
<itemPath>src/main.h</itemPath> <itemPath>src/main.h</itemPath>
<itemPath>src/mime.h</itemPath> <itemPath>src/mime.h</itemPath>
<itemPath>tests/minunit.h</itemPath>
<itemPath>src/queue.h</itemPath> <itemPath>src/queue.h</itemPath>
<itemPath>src/server-connection.h</itemPath> <itemPath>src/server-connection.h</itemPath>
<itemPath>src/server-loop.h</itemPath> <itemPath>src/server-loop.h</itemPath>
@@ -34,6 +36,7 @@
projectFiles="true"> projectFiles="true">
<itemPath>src/config.c</itemPath> <itemPath>src/config.c</itemPath>
<itemPath>src/data-buffer.c</itemPath> <itemPath>src/data-buffer.c</itemPath>
<itemPath>src/http-body.c</itemPath>
<itemPath>src/http-reader.c</itemPath> <itemPath>src/http-reader.c</itemPath>
<itemPath>src/http-server.c</itemPath> <itemPath>src/http-server.c</itemPath>
<itemPath>src/http.c</itemPath> <itemPath>src/http.c</itemPath>
@@ -109,6 +112,24 @@
</item> </item>
<item path="content/public_html/lorem.txt" ex="false" tool="3" flavor2="0"> <item path="content/public_html/lorem.txt" ex="false" tool="3" flavor2="0">
</item> </item>
<folder path="TestFiles/f1">
<cTool>
<incDir>
<pElem>.</pElem>
</incDir>
</cTool>
<ccTool>
<incDir>
<pElem>.</pElem>
</incDir>
</ccTool>
<linkerTool>
<output>${TESTDIR}/TestFiles/f1</output>
<linkerLibItems>
<linkerOptionItem>-lcunit</linkerOptionItem>
</linkerLibItems>
</linkerTool>
</folder>
<item path="lib/http_parser.c" ex="false" tool="0" flavor2="0"> <item path="lib/http_parser.c" ex="false" tool="0" flavor2="0">
</item> </item>
<item path="lib/http_parser.h" ex="false" tool="3" flavor2="0"> <item path="lib/http_parser.h" ex="false" tool="3" flavor2="0">
@@ -125,6 +146,10 @@
</item> </item>
<item path="src/data-buffer.h" ex="false" tool="3" flavor2="0"> <item path="src/data-buffer.h" ex="false" tool="3" flavor2="0">
</item> </item>
<item path="src/http-body.c" ex="false" tool="0" flavor2="0">
</item>
<item path="src/http-body.h" ex="false" tool="3" flavor2="0">
</item>
<item path="src/http-reader.c" ex="false" tool="0" flavor2="0"> <item path="src/http-reader.c" ex="false" tool="0" flavor2="0">
</item> </item>
<item path="src/http-reader.h" ex="false" tool="3" flavor2="0"> <item path="src/http-reader.h" ex="false" tool="3" flavor2="0">
@@ -193,6 +218,8 @@
</item> </item>
<item path="src/version.h" ex="false" tool="3" flavor2="0"> <item path="src/version.h" ex="false" tool="3" flavor2="0">
</item> </item>
<item path="tests/minunit.h" ex="false" tool="3" flavor2="0">
</item>
</conf> </conf>
<conf name="Release" type="1"> <conf name="Release" type="1">
<toolsSet> <toolsSet>
@@ -231,6 +258,24 @@
</item> </item>
<item path="content/public_html/lorem.txt" ex="false" tool="3" flavor2="0"> <item path="content/public_html/lorem.txt" ex="false" tool="3" flavor2="0">
</item> </item>
<folder path="TestFiles/f1">
<cTool>
<incDir>
<pElem>.</pElem>
</incDir>
</cTool>
<ccTool>
<incDir>
<pElem>.</pElem>
</incDir>
</ccTool>
<linkerTool>
<output>${TESTDIR}/TestFiles/f1</output>
<linkerLibItems>
<linkerOptionItem>-lcunit</linkerOptionItem>
</linkerLibItems>
</linkerTool>
</folder>
<item path="lib/http_parser.c" ex="false" tool="0" flavor2="0"> <item path="lib/http_parser.c" ex="false" tool="0" flavor2="0">
</item> </item>
<item path="lib/http_parser.h" ex="false" tool="3" flavor2="0"> <item path="lib/http_parser.h" ex="false" tool="3" flavor2="0">
@@ -247,6 +292,10 @@
</item> </item>
<item path="src/data-buffer.h" ex="false" tool="3" flavor2="0"> <item path="src/data-buffer.h" ex="false" tool="3" flavor2="0">
</item> </item>
<item path="src/http-body.c" ex="false" tool="0" flavor2="0">
</item>
<item path="src/http-body.h" ex="false" tool="3" flavor2="0">
</item>
<item path="src/http-reader.c" ex="false" tool="0" flavor2="0"> <item path="src/http-reader.c" ex="false" tool="0" flavor2="0">
</item> </item>
<item path="src/http-reader.h" ex="false" tool="3" flavor2="0"> <item path="src/http-reader.h" ex="false" tool="3" flavor2="0">
@@ -315,6 +364,8 @@
</item> </item>
<item path="src/version.h" ex="false" tool="3" flavor2="0"> <item path="src/version.h" ex="false" tool="3" flavor2="0">
</item> </item>
<item path="tests/minunit.h" ex="false" tool="3" flavor2="0">
</item>
</conf> </conf>
</confs> </confs>
</configurationDescriptor> </configurationDescriptor>

249
src/http-body.c Normal file
View File

@@ -0,0 +1,249 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#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->rOffset<http_body_len(body)) {
size_t write_len = http_body_len(body) - body->rOffset;
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->rOffset<http_body_len(body)) {
size_t write_len = http_body_len(body) - body->rOffset;
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) {
}

56
src/http-body.h Normal file
View File

@@ -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 <stddef.h>
#include <stdio.h>
#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 */

View File

@@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <time.h> #include <time.h>
#include <assert.h>
#include "ut/utlist.h" #include "ut/utlist.h"
#include "ut/utarray.h" #include "ut/utarray.h"
@@ -14,7 +15,7 @@
#include "version.h" #include "version.h"
void http_header_icd_init_f(void* elem) { 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) { void http_header_icd_dtor_f(void* elem) {
http_header *header = (http_header*)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}; 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) { char* http_method_getstring(http_request_method method, char* method_other) {
switch(method) { switch(method) {
case METHOD_GET: return "GET"; case METHOD_GET: return "GET";
case METHOD_POST: return "POST"; 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_TRACE: return "TRACE";
case METHOD_CONNECT:return "CONNECT"; case METHOD_CONNECT:return "CONNECT";
case METHOD_INVALID:return "<INVALID>"; case METHOD_INVALID:return "<INVALID>";
case METHOD_OTHER: return method_other; case METHOD_OTHER:
assert(method_other!=NULL);
return method_other;
default: return "<INVALID#>"; default: return "<INVALID#>";
} }
} }
@@ -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 *http_request_line_new(http_request_method method, const char* other) {
http_request_line *req = calloc(1, sizeof(http_request_line)); http_request_line *req = calloc(1, sizeof(http_request_line));
if (req == NULL) { ALLOC_CHECK(req);
fatal("calloc failed");
}
req->method = method; req->method = method;
if (req->method == METHOD_OTHER) { if (req->method == METHOD_OTHER) {
assert(other!=NULL);
req->method_other = calloc(strlen(other)+1, sizeof(char)); req->method_other = calloc(strlen(other)+1, sizeof(char));
strcpy(req->method_other, other); strcpy(req->method_other, other);
} else { } else {
@@ -69,6 +72,8 @@ http_request_line *http_request_line_new(http_request_method method, const char*
return req; return req;
} }
void http_request_line_delete(http_request_line *req) { void http_request_line_delete(http_request_line *req) {
assert(req!=NULL);
free(req->method_other); free(req->method_other);
free(req->uri); free(req->uri);
free(req); 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 *http_response_line_new(uint16_t code) {
http_response_line *resp = calloc(1, sizeof(http_response_line)); http_response_line *resp = calloc(1, sizeof(http_response_line));
if (resp == NULL) { ALLOC_CHECK(resp);
fatal("calloc failed");
}
resp->code = code; resp->code = code;
resp->version = HTTP11; resp->version = HTTP11;
return resp; return resp;
} }
char* http_response_line_get_message(http_response_line *resp) { char* http_response_line_get_message(http_response_line *resp) {
assert(resp!=NULL);
if (resp->custom_message != NULL) { if (resp->custom_message != NULL) {
return resp->custom_message; 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) { void http_response_line_delete(http_response_line *resp) {
assert(resp!=NULL);
free(resp->custom_message); free(resp->custom_message);
free(resp); free(resp);
} }
http_header *http_header_new(const char* name, const char* content) { http_header *http_header_new(const char* name, const char* content) {
assert(name!=NULL);
http_header *header = calloc(1, sizeof(http_header)); http_header *header = calloc(1, sizeof(http_header));
if (header == NULL) { ALLOC_CHECK(header);
fatal("calloc failed");
}
header->name = calloc(strlen(name)+1, sizeof(char)); header->name = calloc(strlen(name)+1, sizeof(char));
ALLOC_CHECK(header->name);
strcpy(header->name, name); strcpy(header->name, name);
if (content != NULL) { if (content != NULL) {
@@ -150,33 +156,38 @@ http_header *http_header_new(const char* name, const char* content) {
return header; return header;
} }
void http_header_append_content(http_header *header, const char* content) { void http_header_append_content(http_header *header, const char* content) {
assert(header!=NULL);
assert(content!=NULL);
if (header->content == NULL) { if (header->content == NULL) {
header->content = calloc(strlen(content)+1, sizeof(char)); header->content = calloc(strlen(content)+1, sizeof(char));
if (header->content == NULL) { ALLOC_CHECK(header->content);
fatal("calloc failed");
}
strcpy(header->content, content); strcpy(header->content, content);
} else { } else {
size_t newlen = strlen(header->content) + strlen(content) + 1; size_t newlen = strlen(header->content) + strlen(content) + 1;
header->content = realloc(header->content, newlen); header->content = realloc(header->content, newlen);
if (header->content == NULL) { ALLOC_CHECK(header->content);
fatal("calloc failed");
}
strcat(header->content, content); strcat(header->content, content);
} }
} }
void http_header_delete(http_header *header) { void http_header_delete(http_header *header) {
assert(header!=NULL);
free(header->name); free(header->name);
free(header->content); free(header->content);
free(header); free(header);
} }
http_header_list* http_header_list_new() { http_header_list* http_header_list_new() {
http_header_list* list = NULL; http_header_list* list = calloc(1, sizeof(http_header_list));
utarray_new(list, &http_header_icd); ALLOC_CHECK(list);
utarray_init(list, &http_header_icd);
return list; return list;
} }
void http_header_list_add(http_header_list* list, http_header *header, bool replace) { void http_header_list_add(http_header_list* list, http_header *header, bool replace) {
assert(list!=NULL);
assert(header!=NULL);
if (replace == true) { if (replace == true) {
http_header_list_remove(list, header->name); 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); free(header);
} }
http_header* http_header_list_get(http_header_list* list, const char* name) { 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 *elem;
HTTP_HEADER_FOREACH(list, elem) { HTTP_HEADER_FOREACH(list, elem) {
if (strcmp(elem->name, name) == 0) { 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; return NULL;
} }
http_header** http_header_list_getall(http_header_list* list, const char* name, size_t *out_header_count) { 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; http_header **headers = NULL;
size_t count = 0; size_t count = 0;
http_header *elem; 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) { if (strcmp(elem->name, name) == 0) {
count++; count++;
headers = realloc(headers, count * sizeof(http_header*)); headers = realloc(headers, count * sizeof(http_header*));
ALLOC_CHECK(headers);
headers[count-1] = elem; headers[count-1] = elem;
} }
} }
@@ -207,6 +229,9 @@ http_header** http_header_list_getall(http_header_list* list, const char* name,
return headers; return headers;
} }
void http_header_list_remove(http_header_list *list, const char* name) { void http_header_list_remove(http_header_list *list, const char* name) {
assert(list!=NULL);
assert(name!=NULL);
http_header **headers; http_header **headers;
size_t count; size_t count;
headers = http_header_list_getall(list, name, &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); free(headers);
} }
void http_header_list_delete(http_header_list *list) { void http_header_list_delete(http_header_list *list) {
assert(list!=NULL);
utarray_free(list); utarray_free(list);
} }
http_request *http_request_new() { http_request *http_request_new() {
http_request *req = calloc(1, sizeof(http_request)); http_request *req = calloc(1, sizeof(http_request));
if (req == NULL) { ALLOC_CHECK(req);
fatal("calloc failed");
}
req->headers = http_header_list_new(); req->headers = http_header_list_new();
req->parsestatus = PARSE_REQUESTLINE; req->parsestatus = PARSE_REQUESTLINE;
return req; return req;
} }
void http_request_append_body(http_request *req, const char* body) { void http_request_append_body(http_request *req, const char* body) {
assert(req!=NULL);
assert(body!=NULL);
uint32_t bodylen = 0; uint32_t bodylen = 0;
if (req->body != NULL) { if (req->body != NULL) {
bodylen = strlen(req->body); bodylen = strlen(req->body);
} }
bodylen += strlen(body) + 1; bodylen += strlen(body) + 1;
req->body = realloc(req->body, bodylen * sizeof(char)); req->body = realloc(req->body, bodylen * sizeof(char));
if (req->body == NULL) { ALLOC_CHECK(req->body);
fatal("calloc failed");
}
strcat(req->body, body); strcat(req->body, body);
} }
char* http_request_write(http_request *req) { char* http_request_write(http_request *req) {
assert(req!=NULL);
assert(req->req!=NULL);
UT_string *output = calloc(1, sizeof(UT_string)); UT_string *output = calloc(1, sizeof(UT_string));
ALLOC_CHECK(output);
utstring_init(output); utstring_init(output);
utstring_printf(output, "%s %s %s\r\n", utstring_printf(output, "%s %s %s\r\n",
@@ -259,51 +289,82 @@ char* http_request_write(http_request *req) {
utstring_printf(output, "\r\n"); utstring_printf(output, "\r\n");
if (req->body != NULL) { if (req->body_type == BODY_STRING) {
utstring_printf(output, "%s\r\n", req->body); 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); char* result = utstring_body(output);
free(output); free(output);
return result; return result;
} }
void http_request_delete(http_request *req) { void http_request_delete(http_request *req) {
assert(req!=NULL);
if (req->req != NULL) { if (req->req != NULL) {
http_request_line_delete(req->req); http_request_line_delete(req->req);
} }
http_header_list_delete(req->headers); 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); free(req);
} }
http_response* http_response_new(http_response_line *resp) { http_response* http_response_new(http_response_line *resp) {
assert(resp!=NULL);
http_response *response = calloc(1, sizeof(http_response)); http_response *response = calloc(1, sizeof(http_response));
ALLOC_CHECK(response);
response->resp = resp; response->resp = resp;
response->headers = http_header_list_new(); response->headers = http_header_list_new();
response->body_chunked = false; response->body_chunked = false;
response->body = NULL; response->body_type = BODY_NONE;
return response; return response;
} }
void http_response_append_body(http_response *resp, const char* body) { 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; size_t bodylen = 0;
if (resp->body != NULL) { if (resp->body.str != NULL) {
bodylen = strlen(resp->body); bodylen = strlen(resp->body.str);
} }
bodylen += strlen(body) + 1; bodylen += strlen(body) + 1;
if (resp->body == NULL) { if (resp->body.str == NULL) {
resp->body = calloc(bodylen, sizeof(char)); resp->body.str = calloc(bodylen, sizeof(char));
ALLOC_CHECK(resp->body.str);
} else { } 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) { void http_response_delete(http_response *resp) {
assert(resp!=NULL);
http_response_line_delete(resp->resp); http_response_line_delete(resp->resp);
http_header_list_delete(resp->headers); 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); free(resp);
} }
char* http_response_write(http_response *resp) { char* http_response_write(http_response *resp) {
assert(resp!=NULL);
assert(resp->resp !=NULL);
UT_string *output = calloc(1, sizeof(UT_string)); UT_string *output = calloc(1, sizeof(UT_string));
ALLOC_CHECK(output);
utstring_init(output); utstring_init(output);
if (resp->resp->version == HTTP10) { if (resp->resp->version == HTTP10) {
@@ -318,8 +379,10 @@ char* http_response_write(http_response *resp) {
if (resp->body_chunked == false) { if (resp->body_chunked == false) {
//Add content length header //Add content length header
uint32_t messageLength = 0; uint32_t messageLength = 0;
if (resp->body != NULL) { if (resp->body_type == BODY_STRING) {
messageLength = strlen(resp->body); messageLength = strlen(resp->body.str);
} else if (resp->body_type == BODY_FILE) {
messageLength = resp->body.file->size;
} }
char messageLengthStr[100]; char messageLengthStr[100];
snprintf(messageLengthStr, 99, "%u", messageLength); snprintf(messageLengthStr, 99, "%u", messageLength);
@@ -352,15 +415,22 @@ char* http_response_write(http_response *resp) {
} }
utstring_printf(output, "\r\n"); utstring_printf(output, "\r\n");
//Write the request //Write the request (if string)
if (resp->body_chunked == false && resp->body != NULL) { if (resp->body_type == BODY_STRING) {
utstring_printf(output, "%s\r\n", resp->body); 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 != NULL) { }
char *chunks = http_chunks_write(resp->body); if (resp->body_chunked == true && resp->body.str != NULL) {
utstring_printf(output, "%s", chunks); http_chunks_write(resp->body.str, output);
free(chunks); }
} 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); char* outputStr = utstring_body(output);
free(output); free(output);
return outputStr; 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* http_response_create_builtin(uint16_t code, const char* errmsg) {
http_response *resp = http_response_new(http_response_line_new(code)); 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); 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); char* title_message = http_response_line_get_message(resp->resp);
snprintf(buffer, 1023, "%s %hu - %s", (code >= 400) ? "Error" : "Response Code", code, title_message); 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; return resp;
} }
void http_chunks_write(char* source, UT_string* output) {
assert(source!=NULL);
assert(output!=NULL);
char* http_chunks_write(char* source) { size_t source_len = strlen(source);
size_t sourcelen = strlen(source);
UT_string *output = calloc(1, sizeof(UT_string));
utstring_init(output);
char buffer[HTTP_CHUNK_MAXSIZE+1] = {0}; 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); sprintf(buffer, "%zx;\r\n", (size_t)HTTP_CHUNK_MAXSIZE);
size_t overhead = strlen(buffer); size_t overhead = strlen(buffer);
overhead+=3;//account for terminating CRLF + \0 overhead+=3;//account for terminating CR + LF + \0
buffer[0] = '\0'; buffer[0] = '\0';
size_t i = 0; size_t i = 0;
while (i < sourcelen) { while (i < source_len) {
//how much can we write in this chunk? //how much can we write in this chunk?
size_t sourcerem = sourcelen - i; size_t sourcerem = source_len - i;
size_t chunklen = size_t chunk_len = sourcerem;
sourcerem > HTTP_CHUNK_MAXSIZE-overhead if (chunk_len > HTTP_CHUNK_MAXSIZE-overhead) {
? HTTP_CHUNK_MAXSIZE-overhead chunk_len = HTTP_CHUNK_MAXSIZE-overhead;
: sourcerem; }
utstring_printf(output, "%zx;\r\n", chunklen); //Write chunk length
memset(&buffer, 0, sizeof(buffer)); utstring_printf(output, "%zx;\r\n", chunk_len);
strncpy(buffer, source+i, chunklen); //Write chunk data
utstring_printf(output, "%s\r\n", buffer); utstring_bincpy(output, source+i, chunk_len);
i += chunklen; i += chunk_len;
} }
char* outputstr = utstring_body(output);
free(output);
return outputstr;
} }
char* http_chunks_terminate(http_header_list *footers) { void http_chunks_terminate(http_header_list *footers, UT_string* output) {
UT_string *output = calloc(1, sizeof(UT_string)); assert(output!=NULL);
utstring_init(output);
utstring_printf(output, "0\r\n"); utstring_printf(output, "0\r\n");
if (footers != NULL) { if (footers != NULL) {
@@ -436,29 +503,28 @@ char* http_chunks_terminate(http_header_list *footers) {
} }
} }
utstring_printf(output, "\r\n"); utstring_printf(output, "\r\n");
char* outputstr = utstring_body(output);
free(output);
return outputstr;
} }
http_response_list* http_response_list_new() { http_response_list* http_response_list_new() {
http_response_list *list = calloc(1, sizeof(http_response_list)); http_response_list *list = calloc(1, sizeof(http_response_list));
assert(list != NULL); ALLOC_CHECK(list);
list->first = NULL; list->first = NULL;
return list; return list;
} }
void http_response_list_append(http_response_list *list, http_response* response) { void http_response_list_append(http_response_list *list, http_response* response) {
assert(list != NULL); assert(list != NULL);
assert(response != NULL); assert(response != NULL);
LL_APPEND(list->first, response); LL_APPEND(list->first, response);
} }
http_response* http_response_list_next(http_response_list *list) { http_response* http_response_list_next(http_response_list *list) {
assert(list != NULL); assert(list != NULL);
return http_response_list_next2(list, true); return http_response_list_next2(list, true);
} }
http_response* http_response_list_next2(http_response_list *list, bool remove) { http_response* http_response_list_next2(http_response_list *list, bool remove) {
assert(list != NULL); assert(list != NULL);
if (list->first == NULL) { if (list->first == NULL) {
return NULL; return NULL;
} }

View File

@@ -13,6 +13,8 @@
#include <stdbool.h> #include <stdbool.h>
#include "ut/utarray.h" #include "ut/utarray.h"
#include "util.h"
#include "http-body.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -81,15 +83,18 @@ extern "C" {
http_request_line *req; http_request_line *req;
http_request_parsestatus parsestatus; http_request_parsestatus parsestatus;
http_header_list *headers; http_header_list *headers;
char *body; http_body_type body_type;
http_body body;
struct http_request *next; struct http_request *next;
} http_request; } http_request;
typedef struct http_response { typedef struct http_response {
http_response_line *resp; http_response_line *resp;
http_header_list *headers; http_header_list *headers;
bool body_chunked; bool body_chunked;
char* body; http_body_type body_type;
http_body body;
struct http_response *next; struct http_response *next;
} http_response; } http_response;
@@ -131,8 +136,8 @@ extern "C" {
char* http_response_write(http_response *resp); char* http_response_write(http_response *resp);
http_response* http_response_create_builtin(uint16_t code, const char* errmsg); http_response* http_response_create_builtin(uint16_t code, const char* errmsg);
char* http_chunks_write(char* source); void http_chunks_write(char* source, UT_string* output);
char* http_chunks_terminate(http_header_list *footers); void http_chunks_terminate(http_header_list *footers, UT_string* output);
http_response_list* http_response_list_new(); http_response_list* http_response_list_new();
void http_response_list_append(http_response_list *list, http_response* response); void http_response_list_append(http_response_list *list, http_response* response);

View File

@@ -70,6 +70,9 @@ void* server_loop_read(void* arg) {
LL_APPEND(conn->pending_requests, conn->parse_state->current_request); LL_APPEND(conn->pending_requests, conn->parse_state->current_request);
server_parser_status_reset(conn->parse_state); server_parser_status_reset(conn->parse_state);
queue_add(conn->server->pools[POOL_WORKER]->queue, queue_item_new2("REQ", (void*)conn)); 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) { if (error = true) {
//Write any error directly, this will also close the connection //Write any error directly, this will also close the connection

View File

@@ -10,6 +10,10 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <errno.h> #include <errno.h>
#include <assert.h>
#include <bits/stdio2.h>
#include "ut/utstring.h"
#include "util.h" #include "util.h"
#include "log.h" #include "log.h"
@@ -140,34 +144,6 @@ char* str_trimwhitespace(char *str)
return 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) { char* str_replace(char *haystack, const char *search, const char *replacement) {
size_t haystacklen = strlen(haystack); size_t haystacklen = strlen(haystack);
@@ -204,3 +180,48 @@ char* str_replace(char *haystack, const char *search, const char *replacement) {
} }
return result; return result;
} }
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);
}

View File

@@ -12,8 +12,18 @@
extern "C" { extern "C" {
#endif #endif
#include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#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 { typedef struct file_map {
char* map; char* map;
size_t size; size_t size;
@@ -29,7 +39,8 @@ extern "C" {
file_map* file_map_new(const char* filename); file_map* file_map_new(const char* filename);
void file_map_delete(file_map* map); 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 #ifdef __cplusplus
} }

64
tests/minunit.h Normal file
View File

@@ -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 <stdio.h>
#include <stdlib.h>
#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 */