Http parsing and responses are now mostly complete

This commit is contained in:
2014-07-25 17:23:38 +01:00
parent bfe3a66903
commit a97b541174
14 changed files with 2865 additions and 114 deletions

View File

@@ -9,17 +9,16 @@
#include "../ut/utstring.h"
#include "http.h"
/*
* METHOD_GET, METHOD_POST, METHOD_HEAD, METHOD_PUT,
METHOD_DELETE, METHOD_OPTIONS, METHOD_TRACE,
METHOD_CONNECT, METHOD_OTHER
*/
void http_header_icd_init_f(void* elem) {
memset(elem, 1, sizeof(http_header));
}
void http_header_icd_dtor_f(void* elem) {
http_header *header = (http_header*)elem;
if (header->name!=NULL) free(header->name);
if (header->content!=NULL) free(header->content);
}
UT_icd http_header_icd = {sizeof(http_header), http_header_icd_init_f, NULL, NULL};
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) {
@@ -207,17 +206,11 @@ void http_header_list_remove(http_header_list *list, const char* name) {
headers = http_header_list_getall(list, name, &count);
for(int i=0; i<count; i++) {
int pos = utarray_eltidx(list,headers[i]);
free(headers[i]->name);
free(headers[i]->content);
utarray_erase(list, pos, 1);
}
free(headers);
}
void http_header_list_delete(http_header_list *list) {
HTTP_HEADER_FOREACH(list, elem) {
free(elem->name);
free(elem->content);
}
utarray_free(list);
}
@@ -242,6 +235,30 @@ void http_request_append_body(http_request *req, const char* body) {
}
strcat(req->body, body);
}
char* http_request_write(http_request *req) {
UT_string *output = calloc(1, sizeof(UT_string));
utstring_init(output);
utstring_printf(output, "%s %s %s\r\n",
http_method_getstring(req->req->method, req->req->method_other),
req->req->uri,
req->req->version == HTTP10 ? "HTTP/1.0" : "HTTP/1.1"
);
HTTP_HEADER_FOREACH(req->headers, elem) {
utstring_printf(output, "%s: %s\r\n",
elem->name, elem->content);
}
utstring_printf(output, "\r\n");
if (req->body != NULL) {
utstring_printf(output, "%s\r\n", req->body);
}
char* result = utstring_body(output);
free(output);
return result;
}
void http_request_delete(http_request *req) {
if (req->req != NULL) {
http_request_line_delete(req->req);
@@ -250,16 +267,17 @@ void http_request_delete(http_request *req) {
free(req->body);
free(req);
}
http_response* http_response_new(http_response_line *resp) {
http_response *response = calloc(1, sizeof(http_response));
response->resp = resp;
response->headers = http_header_list_new();
response->body_chunked = false;
response->body = NULL;
return response;
}
void http_response_append_body(http_response *resp, const char* body) {
uint32_t bodylen = 0;
size_t bodylen = 0;
if (resp->body != NULL) {
bodylen = strlen(resp->body);
}
@@ -290,14 +308,24 @@ char* http_response_write(http_response *resp) {
utstring_printf(output, "%hu %s\r\n", resp->resp->code, http_response_line_get_message(resp->resp));
if (resp->resp->code != 100) { //No additional headers for Continue messages
//Add content length header
uint32_t messageLength = 0;
if (resp->body != NULL) {
messageLength = strlen(resp->body);
if (resp->body_chunked == false) {
//Add content length header
uint32_t messageLength = 0;
if (resp->body != NULL) {
messageLength = strlen(resp->body);
}
char messageLengthStr[100];
snprintf(messageLengthStr, 99, "%u", messageLength);
http_header_list_add(resp->headers, http_header_new(HEADER_CONTENT_LENGTH, messageLengthStr), true);
} else { //Chunked encoding
http_header_list_add(resp->headers, http_header_new(HEADER_TRANSFER_ENCODING, "chunked"), true);
}
//Add content type if not defined
http_header* contenttype = http_header_list_get(resp->headers, HEADER_CONTENT_TYPE);
if (contenttype == NULL) {
http_header_list_add(resp->headers, http_header_new(HEADER_CONTENT_TYPE, DEFAULT_CONTENT_TYPE), false);
}
char messageLengthStr[100];
snprintf(messageLengthStr, 99, "%u", messageLength);
http_header_list_add(resp->headers, http_header_new(HEADER_CONTENT_LENGTH, messageLengthStr), true);
//Add date header
time_t timenow = time(NULL);
@@ -314,9 +342,13 @@ char* http_response_write(http_response *resp) {
utstring_printf(output, "\r\n");
//Write the request
//TODO: chunked support for output
if (resp->body != NULL) {
utstring_printf(output, "%s", resp->body);
if (resp->body_chunked == false && resp->body != NULL) {
utstring_printf(output, "%s\r\n", resp->body);
}
if (resp->body_chunked == true && resp->body != NULL) {
char *chunks = http_chunks_write(resp->body);
utstring_printf(output, "%s", chunks);
free(chunks);
}
char* outputStr = utstring_body(output);
free(output);
@@ -328,10 +360,10 @@ http_response* http_response_create_builtin(uint16_t code, char* errmsg) {
http_header_list_add(resp->headers, http_header_new(HEADER_CONTENT_TYPE, "text/html"), false);
file_map* errorpage = map_file("content/error.html");
file_map* errorpage = file_map_new("content/error.html");
if (errorpage != NULL) {
http_response_append_body(resp, errorpage->map);
free_mapped_file(errorpage);
file_map_delete(errorpage);
} else {
http_response_append_body(resp, "{{title}}\n\n{{message}}");
http_header_list_add(resp->headers, http_header_new(HEADER_CONTENT_TYPE, "text/plain"), true);
@@ -347,4 +379,53 @@ http_response* http_response_create_builtin(uint16_t code, char* errmsg) {
resp->body = str_replace(resp->body, "{{message}}", errmsg);
return resp;
}
char* http_chunks_write(char* source) {
size_t sourcelen = 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
sprintf(buffer, "%zx;\r\n", (size_t)HTTP_CHUNK_MAXSIZE);
size_t overhead = strlen(buffer);
overhead+=3;//account for terminating CRLF + \0
buffer[0] = '\0';
size_t i = 0;
while (i < sourcelen) {
//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;
}
char* outputstr = utstring_body(output);
free(output);
return outputstr;
}
char* http_chunks_terminate(http_header_list *footers) {
UT_string *output = calloc(1, sizeof(UT_string));
utstring_init(output);
utstring_printf(output, "0\r\n");
if (footers != NULL) {
//write footers
HTTP_HEADER_FOREACH(footers, elem) {
utstring_printf(output, "%s: %s\r\n", elem->name, elem->content);
}
}
utstring_printf(output, "\r\n");
char* outputstr = utstring_body(output);
free(output);
return outputstr;
}

View File

@@ -30,6 +30,9 @@ extern "C" {
#define HEADER_IF_UNMODIFIED_SINCE "If-Unmodified-Since"
#define FORMAT_HEADER_DATE "%a, %e %h %Y %T %Z"
#define DEFAULT_CONTENT_TYPE "text/plain"
#define HTTP_CHUNK_MAXSIZE 1024*16
typedef enum http_request_method {
METHOD_GET, METHOD_POST, METHOD_HEAD, METHOD_PUT,
@@ -82,6 +85,7 @@ extern "C" {
typedef struct http_response {
http_response_line *resp;
http_header_list *headers;
bool body_chunked;
char* body;
} http_response;
@@ -109,12 +113,17 @@ extern "C" {
http_request* http_request_new();
void http_request_append_body(http_request *req, const char* body);
char* http_request_write(http_request *req);
void http_request_delete(http_request *req);
http_response* http_response_new(http_response_line *resp);
void http_response_append_body(http_response *resp, const char* body);
void http_response_delete(http_response *resp);
char* http_response_write(http_response *resp);
http_response* http_response_create_builtin(uint16_t code, char* errmsg);
char* http_chunks_write(char* source);
char* http_chunks_terminate(http_header_list *footers);
#ifdef __cplusplus
}

View File

@@ -8,19 +8,14 @@
#include "http.h"
#include "parse.h"
#define GETSTR(str, at, length) do { \
#define GET_CB_STR(str, at, length) do { \
str = calloc(length+1, sizeof(char));\
strncpy(str, at, length);\
}while(0);
#define SKT(parser) ((skt_elem*)parser->data)
http_parser_settings *parser_settings = NULL;
skt_elem *current_socket=NULL;
void parser_set_currentskt(skt_elem *elem) {
current_socket = elem;
}
http_parser_settings* parser_get_settings(skt_elem *elem) {
if (parser_settings == NULL) {
parser_settings = calloc(1, sizeof(http_parser_settings));
@@ -30,111 +25,103 @@ http_parser_settings* parser_get_settings(skt_elem *elem) {
parser_settings->on_headers_complete = parser_cb_on_headers_complete;
parser_settings->on_message_begin = parser_cb_on_message_begin;
parser_settings->on_message_complete = parser_cb_on_message_complete;
parser_settings->on_status_complete = parser_cb_on_status;
parser_settings->on_status = parser_cb_on_status;
parser_settings->on_url = parser_cb_on_url;
}
parser_set_currentskt(elem);
return parser_settings;
}
int parser_cb_on_message_begin(http_parser* parser) {
info("parser_cb_on_message_begin");
if (current_socket->current_request != NULL) {
http_request_delete(current_socket->current_request);
if (SKT(parser)->current_request != NULL) {
http_request_delete(SKT(parser)->current_request);
}
current_socket->current_request = http_request_new();
SKT(parser)->current_request = http_request_new();
return 0;
}
int parser_cb_on_url(http_parser* parser, const char *at, size_t length) {
char* str;GETSTR(str,at,length);
info("parser_cb_on_url: %s",str);
current_socket->current_request->req = http_request_line_new(http_method_fromstring(http_method_str(parser->method)), NULL);
current_socket->current_request->req->uri = str;
char* str;GET_CB_STR(str,at,length);
SKT(parser)->current_request->req = http_request_line_new(http_method_fromstring(http_method_str(parser->method)), NULL);
SKT(parser)->current_request->req->uri = str;
return 0;
}
int parser_cb_on_status(http_parser* parser) {
int parser_cb_on_status(http_parser* parser, const char *at, size_t length) {
//Responses only, so ignored
info("parser_cb_on_status");
return 0;
}
int parser_cb_on_header_field(http_parser* parser, const char *at, size_t length) {
char* str;GETSTR(str,at,length);
info("parser_cb_on_header_field: %s",str);
char* str;GET_CB_STR(str,at,length);
if (current_socket->parser_header_state == HSTATE_NONE) {
if (SKT(parser)->parser_header_state == HSTATE_NONE) {
//First call, new header
if (current_socket->parser_current_header != NULL) {
http_header_delete(current_socket->parser_current_header);
if (SKT(parser)->parser_current_header != NULL) {
http_header_delete(SKT(parser)->parser_current_header);
}
current_socket->parser_current_header = http_header_new(str, NULL);
} else if (current_socket->parser_header_state == HSTATE_VALUE) {
SKT(parser)->parser_current_header = http_header_new(str, NULL);
//the http version should also be set now
if (parser->http_major == 1) {
if (parser->http_minor == 0) {
SKT(parser)->current_request->req->version = HTTP10;
} else if (parser->http_minor == 1) {
SKT(parser)->current_request->req->version = HTTP11;
} else {
return -1;
}
} else {
return -1;
}
} else if (SKT(parser)->parser_header_state == HSTATE_VALUE) {
//New header
if (current_socket->parser_current_header != NULL) {
http_header_list_add(current_socket->current_request->headers, current_socket->parser_current_header, false);
if (SKT(parser)->parser_current_header != NULL) {
http_header_list_add(SKT(parser)->current_request->headers, SKT(parser)->parser_current_header, false);
}
current_socket->parser_current_header = http_header_new(str, NULL);
} else if (current_socket->parser_header_state == HSTATE_FIELD) {
SKT(parser)->parser_current_header = http_header_new(str, NULL);
} else if (SKT(parser)->parser_header_state == HSTATE_FIELD) {
//continuation of current headers name
http_header* header = current_socket->parser_current_header;
http_header* header = SKT(parser)->parser_current_header;
size_t newlen = strlen(header->name) + length +1;
header->name = realloc(header->name, newlen * sizeof(char));
} else {
return 1;
}
current_socket->parser_header_state = HSTATE_FIELD;
SKT(parser)->parser_header_state = HSTATE_FIELD;
free(str);
return 0;
}
int parser_cb_on_header_value(http_parser* parser, const char *at, size_t length) {
char* str;GETSTR(str,at,length);
info("parser_cb_on_header_value: %s",str);
char* str;GET_CB_STR(str,at,length);
http_header_append_content(current_socket->parser_current_header, str);
http_header_append_content(SKT(parser)->parser_current_header, str);
current_socket->parser_header_state = HSTATE_VALUE;
SKT(parser)->parser_header_state = HSTATE_VALUE;
free(str);
return 0;
}
int parser_cb_on_headers_complete(http_parser* parser) {
info("parser_cb_on_headers_complete");
//save current header
if (current_socket->parser_current_header != NULL) {
http_header_list_add(current_socket->current_request->headers, current_socket->parser_current_header, false);
current_socket->parser_current_header = NULL;
}
//the http version should also be set now
if (parser->http_major == 1) {
if (parser->http_minor == 0) {
current_socket->current_request->req->version = HTTP10;
} else if (parser->http_minor == 1) {
current_socket->current_request->req->version = HTTP11;
} else {
return -1;
}
} else {
return -1;
if (SKT(parser)->parser_current_header != NULL) {
http_header_list_add(SKT(parser)->current_request->headers, SKT(parser)->parser_current_header, false);
SKT(parser)->parser_current_header = NULL;
}
return 0;
}
int parser_cb_on_body(http_parser* parser, const char *at, size_t length) {
char* str;GETSTR(str,at,length);
info("parser_cb_on_body: %s",str);
char* str;GET_CB_STR(str,at,length);
http_request_append_body(current_socket->current_request, str);
http_request_append_body(SKT(parser)->current_request, str);
free(str);
return 0;
}
int parser_cb_on_message_complete(http_parser* parser) {
info("parser_cb_on_message_complete");
current_socket->request_complete = true;
SKT(parser)->request_complete = true;
return 0;
}

View File

@@ -12,7 +12,7 @@
extern "C" {
#endif
#include <http_parser.h>
#include "http_parser.h"
#include "http.h"
#include "../main.h"
@@ -23,7 +23,7 @@ extern "C" {
int parser_cb_on_message_begin(http_parser* parser);
int parser_cb_on_url(http_parser* parser, const char *at, size_t length);
int parser_cb_on_status(http_parser* parser);
int parser_cb_on_status(http_parser* parser, const char *at, size_t length);
int parser_cb_on_header_field(http_parser* parser, const char *at, size_t length);
int parser_cb_on_header_value(http_parser* parser, const char *at, size_t length);
int parser_cb_on_headers_complete(http_parser* parser);

View File

@@ -14,7 +14,8 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <http_parser.h>
#include "http_parser.h"
#include "ut/utlist.h"
#include "ut/utarray.h"
@@ -60,11 +61,20 @@ int main(int argc, char** argv) {
utstring_body(elem->info->read),
utstring_len(elem->info->read));
if (parsedcount != utstring_len(elem->info->read)) {
warning("error parsing request. closing connection", false);
char warningmsg[2048] = {0};
snprintf(warningmsg, 2048,
"error parsing request (%s: %s). closing connection",
http_errno_name(elem->parser->http_errno),
http_errno_description(elem->parser->http_errno));
warning(warningmsg, false);
elem->info->close = true;
}
utstring_clear(elem->info->read);
if (elem->request_complete == true) {
char* reqstr = http_request_write(elem->current_request);
info("\n%s\n", reqstr);
free(reqstr);
http_response* resp = http_response_create_builtin(200, elem->current_request->req->uri);
utstring_printf(elem->info->write, "%s", http_response_write(resp));
http_response_delete(resp);
@@ -123,6 +133,7 @@ skt_elem* skt_elem_new(skt_info *info) {
elem->info = info;
elem->parser = calloc(1, sizeof(http_parser));
http_parser_init(elem->parser, HTTP_REQUEST);
elem->parser->data = (void*)elem;
elem->parser_header_state = HSTATE_NONE;
elem->request_complete = false;
return elem;
@@ -139,10 +150,9 @@ void fatal(char* msg) {
exit(EXIT_FAILURE);
}
void warning(char* msg, bool showPError) {
char warning[512];
bzero(&warning, sizeof warning);
snprintf(warning, 511, "Warning: %s", msg);
char warning[1024];
memset(&warning, 0, 1024*sizeof(char));
snprintf(warning, 1024, "Warning: %s", msg);
if (showPError == true) {
perror(warning);
@@ -200,7 +210,7 @@ char** str_splitlines(char *str, size_t *line_count) {
return result;
}
file_map* map_file(const char* filename) {
file_map* file_map_new(const char* filename) {
int fd = open(filename, O_RDONLY);
if (fd < 0) {
@@ -221,7 +231,7 @@ file_map* map_file(const char* filename) {
filemap->size = size;
return filemap;
}
void free_mapped_file(file_map* file) {
void file_map_delete(file_map* file) {
if (munmap((void*)file->map, file->size) < 0) {
warning("failed to unmap file", true);
}

View File

@@ -13,7 +13,7 @@ extern "C" {
#endif
#include <stdbool.h>
#include <http_parser.h>
#include "http_parser.h"
#include "socket.h"
#include "http/http.h"
@@ -45,8 +45,8 @@ extern "C" {
char** str_splitlines(char *str, size_t *line_count);
char* str_replace(char *str, const char *search, const char *replacement);
file_map* map_file(const char* filename);
void free_mapped_file(file_map* map);
file_map* file_map_new(const char* filename);
void file_map_delete(file_map* map);
#ifdef __cplusplus