Working on http body code
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
249
src/http-body.c
Normal 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
56
src/http-body.h
Normal 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 */
|
||||
|
||||
216
src/http.c
216
src/http.c
@@ -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;
|
||||
}
|
||||
|
||||
13
src/http.h
13
src/http.h
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
77
src/util.c
77
src/util.c
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
13
src/util.h
13
src/util.h
@@ -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
64
tests/minunit.h
Normal 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 */
|
||||
|
||||
Reference in New Issue
Block a user