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}/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"

View File

@@ -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"

View File

@@ -6,6 +6,7 @@
projectFiles="true">
<itemPath>src/config.h</itemPath>
<itemPath>src/data-buffer.h</itemPath>
<itemPath>src/http-body.h</itemPath>
<itemPath>src/http-reader.h</itemPath>
<itemPath>src/http-server.h</itemPath>
<itemPath>src/http.h</itemPath>
@@ -14,6 +15,7 @@
<itemPath>src/log.h</itemPath>
<itemPath>src/main.h</itemPath>
<itemPath>src/mime.h</itemPath>
<itemPath>tests/minunit.h</itemPath>
<itemPath>src/queue.h</itemPath>
<itemPath>src/server-connection.h</itemPath>
<itemPath>src/server-loop.h</itemPath>
@@ -34,6 +36,7 @@
projectFiles="true">
<itemPath>src/config.c</itemPath>
<itemPath>src/data-buffer.c</itemPath>
<itemPath>src/http-body.c</itemPath>
<itemPath>src/http-reader.c</itemPath>
<itemPath>src/http-server.c</itemPath>
<itemPath>src/http.c</itemPath>
@@ -109,6 +112,24 @@
</item>
<item path="content/public_html/lorem.txt" ex="false" tool="3" flavor2="0">
</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>
<item path="lib/http_parser.h" ex="false" tool="3" flavor2="0">
@@ -125,6 +146,10 @@
</item>
<item path="src/data-buffer.h" ex="false" tool="3" flavor2="0">
</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>
<item path="src/http-reader.h" ex="false" tool="3" flavor2="0">
@@ -193,6 +218,8 @@
</item>
<item path="src/version.h" ex="false" tool="3" flavor2="0">
</item>
<item path="tests/minunit.h" ex="false" tool="3" flavor2="0">
</item>
</conf>
<conf name="Release" type="1">
<toolsSet>
@@ -231,6 +258,24 @@
</item>
<item path="content/public_html/lorem.txt" ex="false" tool="3" flavor2="0">
</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>
<item path="lib/http_parser.h" ex="false" tool="3" flavor2="0">
@@ -247,6 +292,10 @@
</item>
<item path="src/data-buffer.h" ex="false" tool="3" flavor2="0">
</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>
<item path="src/http-reader.h" ex="false" tool="3" flavor2="0">
@@ -315,6 +364,8 @@
</item>
<item path="src/version.h" ex="false" tool="3" flavor2="0">
</item>
<item path="tests/minunit.h" ex="false" tool="3" flavor2="0">
</item>
</conf>
</confs>
</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 <stdbool.h>
#include <time.h>
#include <assert.h>
#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 "<INVALID>";
case METHOD_OTHER: return method_other;
case METHOD_OTHER:
assert(method_other!=NULL);
return method_other;
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 *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) {
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);
//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 != NULL) {
char *chunks = http_chunks_write(resp->body);
utstring_printf(output, "%s", chunks);
free(chunks);
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;
}
void http_chunks_write(char* source, UT_string* output) {
assert(source!=NULL);
assert(output!=NULL);
char* http_chunks_write(char* source) {
size_t sourcelen = strlen(source);
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;
}
char* outputstr = utstring_body(output);
free(output);
return outputstr;
//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* 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;
}

View File

@@ -13,6 +13,8 @@
#include <stdbool.h>
#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);

View File

@@ -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

View File

@@ -10,6 +10,10 @@
#include <stdarg.h>
#include <stdint.h>
#include <errno.h>
#include <assert.h>
#include <bits/stdio2.h>
#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);
@@ -204,3 +180,48 @@ char* str_replace(char *haystack, const char *search, const char *replacement) {
}
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" {
#endif
#include <stddef.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 {
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
}

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 */