diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index ac49c0a..ad1194c 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -112,7 +112,7 @@ - + @@ -258,7 +258,7 @@ - + diff --git a/src/http-body.c b/src/http-body.c index d8cd11a..96bed82 100644 --- a/src/http-body.c +++ b/src/http-body.c @@ -5,11 +5,34 @@ #include #include #include +#include #include "util.h" #include "ut/utstring.h" #include "http-body.h" +http_body_write_result _http_body_file_fill_buffer(char *buffer, size_t buf_len, size_t *read_len, FILE *src) { + assert(buffer!=NULL); + assert(buf_len>0); + assert(src!=NULL); + assert(read_len!=NULL); + + if (*read_len > buf_len) { + *read_len = buf_len; + } + size_t read_count = fread(buffer, sizeof(char), *read_len, src); + if (read_count < *read_len) { + if (ferror(src) != 0) { + return HBWRITE_ERROR; + } + } + *read_len = read_count; + if (feof(src) != 0) { + return HBWRITE_DONE; + } + return HBWRITE_MORE; +} + http_body* http_body_new(http_body_type type, void* dataptr) { http_body *body = calloc(1, sizeof(http_body)); ALLOC_CHECK(body); @@ -36,9 +59,8 @@ http_body* http_body_new(http_body_type type, void* dataptr) { return body; } -void http_body_delete(http_body *body) { +void http_body_clear(http_body *body) { assert(body!=NULL); - switch(body->type) { case BODY_NONE: break; @@ -62,6 +84,11 @@ void http_body_delete(http_body *body) { fatal("Invalid http body type"); break; } +} +void http_body_delete(http_body *body) { + assert(body!=NULL); + + http_body_clear(body); free(body); } @@ -111,6 +138,13 @@ size_t http_body_len(http_body *body) { } return len; } +void http_body_set_type(http_body *body, http_body_type newtype) { + assert(body!=NULL); + http_body_clear(body); + body->type = newtype; +} + +//TODO: replace these with single function w/callbacks http_body_write_result http_body_writeto_fd(http_body *body, int fd) { assert(body!=NULL); @@ -118,58 +152,52 @@ http_body_write_result http_body_writeto_fd(http_body *body, int fd) { assert(body->data.ptr!=NULL); } - char* buffer=NULL; const size_t buffer_len = 16*1024; + char* buffer= NULL; + if (body->type == BODY_FILE) { + buffer = calloc(buffer_len, sizeof(char)); + ALLOC_CHECK(buffer); + } while(body->rOffsetrOffset; errno = EINVAL; - ssize_t result = -1; + ssize_t written = -1; + http_body_write_result result = HBWRITE_MORE; switch(body->type) { case BODY_STRING: - result = write(fd, body->data.str+body->rOffset, write_len); + written = write(fd, body->data.str+body->rOffset, write_len); break; case BODY_FILEMAP: - result = write(fd, body->data.filemap->map+body->rOffset, write_len); + written = 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); + case BODY_FILE:; + size_t read_count = write_len; + http_body_write_result read_res = _http_body_file_fill_buffer(buffer, buffer_len, &read_count, body->data.file); + if (read_res == HBWRITE_ERROR) { + result = read_res; break; } - 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); + written = write(fd, buffer, read_count); } - if (feof(body->data.file) != 0) { - body->rOffset += result; - free(buffer); - return HBWRITE_DONE; + if (read_res == HBWRITE_DONE) { + result = read_res; break; } break; }//switch body->type - if (result < 0) { + if (written < 0 || result == HBWRITE_ERROR) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return HBWRITE_BLOCKED; } - warning(true, "Error writing http body"); return HBWRITE_ERROR; } - body->rOffset += result; + body->rOffset += written; + if (result == HBWRITE_DONE) { + break; + } }//While data remaining if (buffer != NULL) { @@ -178,7 +206,7 @@ http_body_write_result http_body_writeto_fd(http_body *body, int fd) { errno = 0; return HBWRITE_DONE; } -http_body_write_result http_body_writeto_str(http_body *body, const char** str) { +http_body_write_result http_body_writeto_str(http_body *body, char** str) { assert(body!=NULL); if (body->type!=BODY_NONE) { assert(body->data.ptr!=NULL); @@ -187,6 +215,10 @@ http_body_write_result http_body_writeto_str(http_body *body, const char** str) char* buffer=NULL; const size_t buffer_len = 16*1024; + if (body->type == BODY_FILE) { + buffer = calloc(buffer_len, sizeof(char)); + ALLOC_CHECK(buffer); + } while(body->rOffsetrOffset; @@ -197,53 +229,106 @@ http_body_write_result http_body_writeto_str(http_body *body, const char** str) } ALLOC_CHECK(*str); - errno = EINVAL; - ssize_t result = -1; + ssize_t written = -1; + http_body_write_result result = HBWRITE_MORE; switch(body->type) { case BODY_STRING: - case BODY_FILEMAP: + 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; + written = write_len; break; - case BODY_FILE: - if (buffer==NULL) { - buffer = calloc(buffer_len, sizeof(char)); - ALLOC_CHECK(buffer); + case BODY_FILE:; + size_t read_count = write_len; + http_body_write_result read_res = _http_body_file_fill_buffer(buffer, buffer_len, &read_count, body->data.file); + if (read_res == HBWRITE_ERROR) { + result = read_res; break; } - 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; + written = read_count; } - if (feof(body->data.file) != 0) { - body->rOffset += result; - free(buffer); - return HBWRITE_DONE; + if (read_res == HBWRITE_DONE) { + result = read_res; break; } break; }//Switch body->type - body->rOffset += result; + body->rOffset += written; + if (result == HBWRITE_ERROR) { + if (buffer != NULL) { + free(buffer); + } + return HBWRITE_ERROR; + } + if (result == HBWRITE_DONE) { + break; + } }//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) { + assert(body!=NULL); + if (body->type!=BODY_NONE) { + assert(body->data.ptr!=NULL); + } + assert(utstr!=NULL); + char* buffer=NULL; + const size_t buffer_len = 16*1024; + if (body->type == BODY_FILE) { + buffer = calloc(buffer_len, sizeof(char)); + ALLOC_CHECK(buffer); + } + + while(body->rOffsetrOffset; + + ssize_t written = -1; + http_body_write_result result = HBWRITE_MORE; + + switch(body->type) { + case BODY_STRING: + case BODY_FILEMAP:; + char* src = (body->type==BODY_STRING) ? body->data.str : body->data.filemap->map; + utstring_bincpy(utstr, src, write_len); + written = write_len; + break; + case BODY_FILE:; + size_t read_count = write_len; + http_body_write_result read_res = _http_body_file_fill_buffer(buffer, buffer_len, &read_count, body->data.file); + if (read_res == HBWRITE_ERROR) { + result = read_res; break; + } + if (read_count > 0) { + utstring_bincpy(utstr, src, write_len); + written = read_count; + } + if (read_res == HBWRITE_DONE) { + result = read_res; break; + } + break; + }//Switch body->type + + body->rOffset += written; + if (result == HBWRITE_ERROR) { + if (buffer != NULL) { + free(buffer); + } + return HBWRITE_ERROR; + } + if (result == HBWRITE_DONE) { + break; + } + }//while data remaining + + if (buffer != NULL) { + free(buffer); + } + return HBWRITE_DONE; } diff --git a/src/http-body.h b/src/http-body.h index f8df1ac..dcc49d5 100644 --- a/src/http-body.h +++ b/src/http-body.h @@ -38,13 +38,15 @@ extern "C" { } http_body; http_body* http_body_new(http_body_type type, void* dataptr); + void http_body_clear(http_body *body); void http_body_delete(http_body *body); - size_t http_body_append_str(http_body *body, const char** str_ptr); + size_t http_body_append_str(http_body *body, const char* str, ssize_t str_len); size_t http_body_len(http_body *body); + void http_body_set_type(http_body *body, http_body_type newtype); 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_str(http_body *body, char** str); http_body_write_result http_body_writeto_utstring(http_body *body, UT_string *utstr); diff --git a/src/http-server.c b/src/http-server.c index 9ff3cb2..8e360e7 100644 --- a/src/http-server.c +++ b/src/http-server.c @@ -40,7 +40,7 @@ http_response* server_process_request(config_server* config, http_request *reque } if (host_config == NULL) { //host not found and default not found - response = http_response_create_builtin(500, "Server configuration error (host/default not found)"); + response = http_response_create_builtin(500, "Server configuration error. Host not provided and default not found."); http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false); return response; } @@ -152,9 +152,19 @@ http_response* server_process_request(config_server* config, http_request *reque free(buffer); free(filepath); + bool close_connection = false; //Check to see if client requested the connection be closed http_header* request_connection = http_header_list_get(request->headers, HEADER_CONNECTION); if (request_connection != NULL && strcasecmp(request_connection->content, "close") == 0) { + close_connection = true; + } + //Close a http/1.0 unless the client requested keep-alive + if (close_connection == false && request->req->version == HTTP10 && request_connection != NULL) { + if (strcasecmp(request_connection->content, "Keep-Alive") != 0) { + close_connection = true; + } + } + if (close_connection == true) { http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false); } @@ -177,15 +187,14 @@ server_file_result* server_determine_file(config_host* hconfig, const char* requ return result; } - struct stat *pathstat = calloc(1, sizeof(struct stat)); - if (stat(filepath_actual, pathstat) < 0) { + struct stat pathstat; + if (stat(filepath_actual, &pathstat) < 0) { server_file_result_seterror(result, 404, "File not found"); free(filepath_actual); - free(pathstat); return result; } //If directory - if (S_ISDIR(pathstat->st_mode) != 0) { + if (S_ISDIR(pathstat.st_mode) != 0) { char* dir_index = server_find_directory_index(hconfig, filepath_actual); if (hconfig->index_files_count > 0 && dir_index != NULL) { //Use the directory index @@ -197,10 +206,9 @@ server_file_result* server_determine_file(config_host* hconfig, const char* requ } else { server_file_result_seterror(result, 403, "No index file was found and directory indexes are disabled"); } - } else if (S_ISREG(pathstat->st_mode) != 0) { + } else if (S_ISREG(pathstat.st_mode) != 0) { result->path = strdup(filepath_actual); } - free(pathstat); free(filepath_actual); return result; } diff --git a/src/http.c b/src/http.c index 61873e9..c2efe3e 100644 --- a/src/http.c +++ b/src/http.c @@ -69,6 +69,7 @@ http_request_line *http_request_line_new(http_request_method method, const char* } else { req->method_other = NULL; } + req->version = HTTPXX; return req; } void http_request_line_delete(http_request_line *req) { @@ -252,20 +253,15 @@ http_request *http_request_new() { ALLOC_CHECK(req); req->headers = http_header_list_new(); req->parsestatus = PARSE_REQUESTLINE; + req->body = http_body_new(BODY_NONE, NULL); return req; } void http_request_append_body(http_request *req, const char* body) { assert(req!=NULL); assert(body!=NULL); + assert(req->body->type == BODY_STRING); - uint32_t bodylen = 0; - if (req->body != NULL) { - bodylen = strlen(req->body); - } - bodylen += strlen(body) + 1; - req->body = realloc(req->body, bodylen * sizeof(char)); - ALLOC_CHECK(req->body); - strcat(req->body, body); + http_body_append_str(req->body, body, strlen(body)); } char* http_request_write(http_request *req) { assert(req!=NULL); @@ -289,15 +285,8 @@ char* http_request_write(http_request *req) { utstring_printf(output, "\r\n"); - 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); - } - } + http_body_writeto_utstring(req->body, output); + char* result = utstring_body(output); free(output); return result; @@ -309,11 +298,7 @@ void http_request_delete(http_request *req) { http_request_line_delete(req->req); } http_header_list_delete(req->headers); - 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); - } + http_body_delete(req->body); free(req); } @@ -321,42 +306,26 @@ http_response* http_response_new(http_response_line *resp) { assert(resp!=NULL); http_response *response = calloc(1, sizeof(http_response)); - ALLOC_CHECK(response); + ALLOC_CHECK(resp); response->resp = resp; response->headers = http_header_list_new(); response->body_chunked = false; - response->body_type = BODY_NONE; + response->body = http_body_new(BODY_NONE, NULL); 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); + assert(resp->body->type == BODY_STRING); - size_t bodylen = 0; - if (resp->body.str != NULL) { - bodylen = strlen(resp->body.str); - } - bodylen += strlen(body) + 1; - if (resp->body.str == NULL) { - resp->body.str = calloc(bodylen, sizeof(char)); - ALLOC_CHECK(resp->body.str); - } else { - resp->body.str = realloc(resp->body.str, bodylen * sizeof(char)); - ALLOC_CHECK(resp->body.str); - } - strcat(resp->body.str, body); + http_body_append_str(resp->body, body, strlen(body)); } void http_response_delete(http_response *resp) { assert(resp!=NULL); http_response_line_delete(resp->resp); http_header_list_delete(resp->headers); - 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); - } + http_body_delete(resp->body); free(resp); } char* http_response_write(http_response *resp) { @@ -378,12 +347,7 @@ char* http_response_write(http_response *resp) { if (resp->resp->code != 100) { //No additional headers for Continue messages if (resp->body_chunked == false) { //Add content length header - uint32_t messageLength = 0; - if (resp->body_type == BODY_STRING) { - messageLength = strlen(resp->body.str); - } else if (resp->body_type == BODY_FILE) { - messageLength = resp->body.file->size; - } + uint32_t messageLength = http_body_len(resp->body); char messageLengthStr[100]; snprintf(messageLengthStr, 99, "%u", messageLength); http_header_list_add(resp->headers, http_header_new(HEADER_CONTENT_LENGTH, messageLengthStr), true); @@ -416,21 +380,14 @@ char* http_response_write(http_response *resp) { utstring_printf(output, "\r\n"); //Write the request (if string) - if (resp->body_type == BODY_STRING) { - if (resp->body_chunked == false && resp->body.str != NULL) { - utstring_printf(output, "%s\r\n", resp->body.str); - } - if (resp->body_chunked == true && resp->body.str != NULL) { - http_chunks_write(resp->body.str, output); - } - } else if (resp->body_type == BODY_FILE) { + if (resp->body->type == BODY_STRING) { if (resp->body_chunked == false) { - file_map_copyto_utstring(resp->body.file, output); - } else { - http_chunks_write(resp->body.file->map, output); + http_body_writeto_utstring(resp->body, output); + } + if (resp->body_chunked == true) { + http_chunks_write(resp->body->data.str, output); } } - char* outputStr = utstring_body(output); free(output); return outputStr; @@ -438,7 +395,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_body_set_type(resp->body, BODY_STRING); http_header_list_add(resp->headers, http_header_new(HEADER_CONTENT_TYPE, "text/html"), false); @@ -456,9 +413,17 @@ 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 = 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); + char* str = resp->body->data.str; + + str = str_replace(str, "{{title}}", buffer); + str = str_replace(str, "{{body_title}}", buffer); + if (errmsg == NULL) { + errmsg = ""; + } + str = str_replace(str, "{{message}}", errmsg); + + + resp->body->data.str = str; return resp; } diff --git a/src/http.h b/src/http.h index 8387fac..8dccc17 100644 --- a/src/http.h +++ b/src/http.h @@ -45,7 +45,7 @@ extern "C" { } http_request_method; typedef enum http_version { - HTTP10, HTTP11 + HTTP10, HTTP11, HTTPXX } http_version; typedef struct http_request_line { @@ -83,18 +83,15 @@ extern "C" { http_request_line *req; http_request_parsestatus parsestatus; http_header_list *headers; - http_body_type body_type; - http_body body; + http_body *body; struct http_request *next; } http_request; - typedef struct http_response { http_response_line *resp; http_header_list *headers; bool body_chunked; - http_body_type body_type; - http_body body; + http_body *body; struct http_response *next; } http_response; diff --git a/src/server-connection.h b/src/server-connection.h index b2cb8e5..df2e3e1 100644 --- a/src/server-connection.h +++ b/src/server-connection.h @@ -24,6 +24,8 @@ extern "C" { #define CONN_LOCK(c) pthread_mutex_lock(&c->mutex) #define CONN_UNLOCK(c) pthread_mutex_unlock(&c->mutex) + +#define CONN_ENQUEUE(conn, pool, name) queue_add(conn->server->pools[pool]->queue, queue_item_new2(name, (void*)conn)) typedef enum server_parse_header_state { HSTATE_NONE, HSTATE_VALUE, HSTATE_FIELD diff --git a/src/server-loop-read.c b/src/server-loop-read.c index 7a6d487..20ecd76 100644 --- a/src/server-loop-read.c +++ b/src/server-loop-read.c @@ -69,14 +69,18 @@ void* server_loop_read(void* arg) { //Request has been read successfully, notify worker queue 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) { + CONN_ENQUEUE(conn, POOL_WORKER, "REQ"); + } else if (conn->parse_state->current_request != NULL && + conn->parse_state->current_request->req!=NULL && + conn->parse_state->current_request->req->version == HTTP11) { //Send 100 Continue message - + http_response *resp = http_response_new(http_response_line_new(100)); + http_response_list_append(conn->pending_responses, resp); + CONN_ENQUEUE(conn, POOL_WRITE, "RESP"); } if (error = true) { //Write any error directly, this will also close the connection - queue_add(conn->server->pools[POOL_WRITE]->queue, queue_item_new2("RESP", (void*)conn)); + CONN_ENQUEUE(conn, POOL_WRITE, "RESP"); break; } } diff --git a/src/server-loop-worker.c b/src/server-loop-worker.c index 8398391..c12985a 100644 --- a/src/server-loop-worker.c +++ b/src/server-loop-worker.c @@ -2,6 +2,8 @@ #include #include +#include "ut/utlist.h" + #include "util.h" #include "log.h" #include "config.h" @@ -9,7 +11,35 @@ #include "server-connection.h" #include "server-state.h" #include "server-loop.h" +#include "http.h" +#include "http-server.h" void* server_loop_worker(void* arg) { + thread *th = (thread*)arg; + while(th->stop == false) { + queue_item *item = queue_fetchone(th->pool->queue, true); + if (item == NULL) { + continue; + } + server_connection *conn = (server_connection*)item->data; + CONN_LOCK(conn); + + while (conn->pending_requests != NULL) { + http_request *request = conn->pending_requests; + http_response *resp = server_process_request(conn->server->config, request); + LL_DELETE(conn->pending_requests, request); + http_request_delete(request); + if (resp == NULL) { + LOG(LERROR, "Request did not generate a response"); + resp = http_response_create_builtin(500, "Request did not complete successfully"); + http_header_list_add(resp->headers, http_header_new(HEADER_CONNECTION, "Close"), false); + } + http_response_list_append(conn->pending_responses, resp); + CONN_ENQUEUE(conn, POOL_WRITE, "RESP"); + } + + CONN_UNLOCK(conn); + queue_return_item(th->pool->queue, item, true); + } } \ No newline at end of file diff --git a/src/server-loop-write.c b/src/server-loop-write.c index 1694fbd..8120e2a 100644 --- a/src/server-loop-write.c +++ b/src/server-loop-write.c @@ -14,6 +14,8 @@ #include "server-connection.h" #include "server-state.h" #include "server-loop.h" +#include "http.h" +#include "http-body.h" void* server_loop_write(void* arg) { thread *th = (thread*)arg; @@ -26,35 +28,7 @@ void* server_loop_write(void* arg) { server_connection *conn = (server_connection*)item->data; CONN_LOCK(conn); - size_t count = 0; - while(conn->pending_writes->first != NULL) { - BUFFER_LIST_RD_LOCK(conn->pending_writes); - data_buffer *next = conn->pending_writes->first; - - count = write(conn->skt->fd, next->buffer+next->rOffset, next->wOffset - next->rOffset); - if (count < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - item->blocked = true; - break; - } else { - if (errno != EBADF) { - char address[INET_ADDRSTRLEN]; - skt_clientaddr(conn->skt, address, INET_ADDRSTRLEN); - warning(true, "[#%lu %s] write error", conn->id, address); - } - conn->skt->error = true; - break; - } - } - next->rOffset += count; - - if (next->rOffset >= next->wOffset) { - LL_DELETE(conn->pending_writes->first, next); - data_buffer_free(next); - } - - BUFFER_LIST_RD_UNLOCK(conn->pending_writes); - } + CONN_UNLOCK(conn); queue_return_item(th->pool->queue, item, item->blocked == false); diff --git a/src/util.c b/src/util.c index dd09c3c..17f392c 100644 --- a/src/util.c +++ b/src/util.c @@ -11,7 +11,6 @@ #include #include #include -#include #include "ut/utstring.h" @@ -220,7 +219,7 @@ 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) { assert(map!=NULL); - assert(str!=NULL); + assert(string!=NULL); utstring_bincpy(string, map->map, map->size); }