From 98e5992c450dc7d805216bbca5f4717cfc65b04b Mon Sep 17 00:00:00 2001 From: Sam Stevens Date: Fri, 12 Sep 2014 19:15:59 +0100 Subject: [PATCH] Finished basic server --- content/dirindex.html | 4 +- content/public_html/{ => test}/lorem.txt | 0 nbproject/configurations.xml | 60 +++++------- src/data-buffer.c | 102 ++++++++++++++++----- src/data-buffer.h | 6 +- src/http-body.c | 31 +++++-- src/http-server.c | 42 ++++----- src/http.c | 112 ++++++++++++----------- src/http.h | 7 ++ src/log.c | 6 +- src/main.c | 25 ++++- src/server-connection.c | 3 +- src/server-loop-write.c | 61 +++++++++++- src/server-socket.c | 6 +- src/server-state.c | 1 + src/server.c | 5 +- src/socket.c | 4 +- src/thread-pool.c | 4 +- src/util.c | 19 ++++ src/util.h | 2 + testreq.txt | 1 - 21 files changed, 336 insertions(+), 165 deletions(-) rename content/public_html/{ => test}/lorem.txt (100%) diff --git a/content/dirindex.html b/content/dirindex.html index 7859534..cbcac73 100644 --- a/content/dirindex.html +++ b/content/dirindex.html @@ -1,12 +1,12 @@ - {{dirname}} + Index of {{dirname}}/ -

{{dirname}}

+

{{dirname}}/

diff --git a/content/public_html/lorem.txt b/content/public_html/test/lorem.txt similarity index 100% rename from content/public_html/lorem.txt rename to content/public_html/test/lorem.txt diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index ad1194c..9acec3a 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -1,4 +1,22 @@ + content/error.html content/public_html/index.html content/khttpd.ini - content/public_html/lorem.txt + content/public_html/test/lorem.txt Makefile @@ -110,26 +128,8 @@ - + - @@ -256,26 +256,8 @@ - + - diff --git a/src/data-buffer.c b/src/data-buffer.c index 2813aa4..dec3248 100644 --- a/src/data-buffer.c +++ b/src/data-buffer.c @@ -5,30 +5,29 @@ #include #include #include +#include +#include #include "ut/utlist.h" #include "data-buffer.h" +#include "util.h" data_buffer_list* data_buffer_list_new() { data_buffer_list *list = calloc(1, sizeof(data_buffer_list)); list->first = NULL; - list->wrlock = calloc(1, sizeof(pthread_mutex_t)); - pthread_mutex_init(list->wrlock, NULL); - list->rdlock = calloc(1, sizeof(pthread_mutex_t)); - pthread_mutex_init(list->rdlock, NULL); + pthread_mutex_init(&list->rdlock, NULL); + pthread_mutex_init(&list->wrlock, NULL); return list; } void data_buffer_list_delete(data_buffer_list *list) { assert(list!=NULL); - pthread_mutex_destroy(list->wrlock); - pthread_mutex_destroy(list->rdlock); + pthread_mutex_destroy(&list->wrlock); + pthread_mutex_destroy(&list->rdlock); data_buffer *elem, *tmp; LL_FOREACH_SAFE(list->first, elem, tmp) { LL_DELETE(list->first, elem); - data_buffer_free(elem); + data_buffer_delete(elem); } - free(list->wrlock); - free(list->rdlock); free(list); } void data_buffer_list_append(data_buffer_list *list, const char* src, size_t n) { @@ -36,48 +35,103 @@ void data_buffer_list_append(data_buffer_list *list, const char* src, size_t n) assert(src!=NULL && n>0); BUFFER_LIST_WR_LOCK(list); - int blocks = 1; - data_buffer *newbuf = data_buffer_new(DATA_BUFFER_SIZE); - while(blocks * DATA_BUFFER_SIZE < n) { - blocks++; - LL_PREPEND(newbuf, data_buffer_new(DATA_BUFFER_SIZE)); + //Fetch last buffer in list, in case it has space left + data_buffer *newbuf = NULL, *elem = NULL; + bool first_is_new = false; + LL_FOREACH(list->first, elem) { + if (elem->next == NULL) { + newbuf = elem; + } } + //Use a new buffer if list empty or last buffer is full + if (newbuf == NULL || newbuf->wOffset == newbuf->size) { + newbuf = data_buffer_new(DATA_BUFFER_SIZE); + first_is_new = true; + } + //Add new buffers until we have enough allocated + size_t allocated = newbuf->size - newbuf->wOffset; + while(allocated < n) { + data_buffer *buffer = data_buffer_new(DATA_BUFFER_SIZE); + allocated += buffer->size; + LL_APPEND(newbuf, buffer); + } + //Add data to the buffers size_t offset = 0; - data_buffer *elem; + elem = NULL; LL_FOREACH(newbuf, elem) { size_t copy_count = n - offset; - if (copy_count > elem->size) { - copy_count = elem->size; + if (copy_count > (elem->size - elem->wOffset)) { + copy_count = (elem->size - elem->wOffset); } - memcpy(elem->buffer, src+offset, copy_count); + memcpy(elem->buffer+elem->wOffset, src+offset, copy_count); offset += copy_count; elem->wOffset += copy_count; } - + //Don't re-append the last buffer + if (first_is_new == false) { + LL_DELETE(newbuf, newbuf); + } LL_CONCAT(list->first, newbuf); BUFFER_LIST_WR_UNLOCK(list); } void data_buffer_list_lock(data_buffer_list *list, bool rd, bool wr) { assert(list != NULL); - if (wr == true) pthread_mutex_lock(list->wrlock); - if (rd == true) pthread_mutex_lock(list->rdlock); + if (wr == true) pthread_mutex_lock(&list->wrlock); + if (rd == true) pthread_mutex_lock(&list->rdlock); } void data_buffer_list_unlock(data_buffer_list *list, bool rd, bool wr) { assert(list != NULL); - if (rd == true) pthread_mutex_unlock(list->rdlock); - if (wr == true) pthread_mutex_unlock(list->wrlock); + if (rd == true) pthread_mutex_unlock(&list->rdlock); + if (wr == true) pthread_mutex_unlock(&list->wrlock); +} +ssize_t data_buffer_list_writeto_fd(data_buffer_list *list, int fd) { + assert(list != NULL); + + ssize_t result = 0; + + BUFFER_LIST_RD_LOCK(list); + data_buffer *next = list->first; + while(next != NULL) { + size_t write_count = next->wOffset - next->rOffset; + if (write_count > 0) { + ssize_t written = write(fd, next->buffer+next->rOffset, write_count); + if (written <= 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + result = -1; + goto DONE; + } + if (written < 0) { + result = -1; + goto DONE; + } + } + result += written; + next->rOffset += written; + } else { + LL_DELETE(list->first, next); + data_buffer_delete(next); + next = list->first; + } + } + + DONE: + BUFFER_LIST_RD_UNLOCK(list); + + return result; } data_buffer* data_buffer_new(size_t size) { assert(size > 0); data_buffer* buf = calloc(1, sizeof(data_buffer)); + ALLOC_CHECK(buf); buf->buffer = calloc(size, sizeof(char)); + ALLOC_CHECK(buf->buffer); buf->size = size; return buf; } -void data_buffer_free(data_buffer *buf) { +void data_buffer_delete(data_buffer *buf) { assert(buf != NULL); free(buf->buffer); diff --git a/src/data-buffer.h b/src/data-buffer.h index e659eb9..99e32e6 100644 --- a/src/data-buffer.h +++ b/src/data-buffer.h @@ -27,7 +27,8 @@ extern "C" { typedef struct data_buffer_list { struct data_buffer *first; - pthread_mutex_t *wrlock, *rdlock; + pthread_mutex_t wrlock; + pthread_mutex_t rdlock; } data_buffer_list; typedef struct data_buffer { @@ -43,9 +44,10 @@ extern "C" { void data_buffer_list_append(data_buffer_list *list, const char* src, size_t n); void data_buffer_list_lock(data_buffer_list *list, bool rd, bool wr); void data_buffer_list_unlock(data_buffer_list *list, bool rd, bool wr); + ssize_t data_buffer_list_writeto_fd(data_buffer_list *list, int fd); data_buffer* data_buffer_new(size_t size); - void data_buffer_free(data_buffer *buffer); + void data_buffer_delete(data_buffer *buffer); #ifdef __cplusplus } diff --git a/src/http-body.c b/src/http-body.c index 96bed82..22dee58 100644 --- a/src/http-body.c +++ b/src/http-body.c @@ -10,8 +10,9 @@ #include "util.h" #include "ut/utstring.h" #include "http-body.h" +#include "log.h" -http_body_write_result _http_body_file_fill_buffer(char *buffer, size_t buf_len, size_t *read_len, FILE *src) { +http_body_write_result _http_body_file_fill_buffer(char *buffer, size_t buf_len, size_t *read_len, FILE *src, size_t offset) { assert(buffer!=NULL); assert(buf_len>0); assert(src!=NULL); @@ -20,6 +21,7 @@ http_body_write_result _http_body_file_fill_buffer(char *buffer, size_t buf_len, if (*read_len > buf_len) { *read_len = buf_len; } + fseek(src, offset, SEEK_SET); size_t read_count = fread(buffer, sizeof(char), *read_len, src); if (read_count < *read_len) { if (ferror(src) != 0) { @@ -84,6 +86,7 @@ void http_body_clear(http_body *body) { fatal("Invalid http body type"); break; } + body->rOffset = 0; } void http_body_delete(http_body *body) { assert(body!=NULL); @@ -99,9 +102,15 @@ size_t http_body_append_str(http_body *body, const char* str, ssize_t str_len) { 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); + size_t new_len = str_len+1; + if (body->data.str != NULL) { + new_len += strlen(body->data.str); + } + if (body->data.str == NULL) { + body->data.str = calloc(new_len, sizeof(char)); + } else { + 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); @@ -161,7 +170,7 @@ http_body_write_result http_body_writeto_fd(http_body *body, int fd) { while(body->rOffsetrOffset; - errno = EINVAL; + ssize_t written = -1; http_body_write_result result = HBWRITE_MORE; @@ -174,7 +183,7 @@ http_body_write_result http_body_writeto_fd(http_body *body, int fd) { 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); + http_body_write_result read_res = _http_body_file_fill_buffer(buffer, buffer_len, &read_count, body->data.file, body->rOffset); if (read_res == HBWRITE_ERROR) { result = read_res; break; } @@ -190,8 +199,10 @@ http_body_write_result http_body_writeto_fd(http_body *body, int fd) { if (written < 0 || result == HBWRITE_ERROR) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return HBWRITE_BLOCKED; + } else { + perror("Write Error"); + return HBWRITE_ERROR; } - return HBWRITE_ERROR; } body->rOffset += written; @@ -241,7 +252,7 @@ http_body_write_result http_body_writeto_str(http_body *body, char** str) { 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); + http_body_write_result read_res = _http_body_file_fill_buffer(buffer, buffer_len, &read_count, body->data.file, body->rOffset); if (read_res == HBWRITE_ERROR) { result = read_res; break; } @@ -301,12 +312,12 @@ http_body_write_result http_body_writeto_utstring(http_body *body, UT_string *ut 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); + http_body_write_result read_res = _http_body_file_fill_buffer(buffer, buffer_len, &read_count, body->data.file, body->rOffset); if (read_res == HBWRITE_ERROR) { result = read_res; break; } if (read_count > 0) { - utstring_bincpy(utstr, src, write_len); + utstring_bincpy(utstr, buffer, read_count); written = read_count; } if (read_res == HBWRITE_DONE) { diff --git a/src/http-server.c b/src/http-server.c index 8e360e7..d0891a4 100644 --- a/src/http-server.c +++ b/src/http-server.c @@ -127,29 +127,22 @@ http_response* server_process_request(config_server* config, http_request *reque return response; } - //File is ok and can be served to the client - fseek(file, 0, SEEK_END); - size_t filesize = ftell(file); - rewind(file); - //Read file into response - //TODO: send file directly from the write loop - char* buffer = calloc(filesize+1, sizeof(char)); - if (fread(buffer, sizeof(char), filesize, file) != filesize) { - warning(true, "failed to read file into memory"); - response = http_response_create_builtin(500, "Could not read file"); - } else { - response = http_response_new(http_response_line_new(200)); - response->resp->version = request->req->version; - const char* mime_type = "text/html"; - if (filepath != NULL) { - mime_type = mime_get_type(filepath, DEFAULT_CONTENT_TYPE); - } - http_header_list_add(response->headers, http_header_new(HEADER_CONTENT_TYPE, mime_type), false); - http_response_append_body(response, buffer); + response = http_response_new(http_response_line_new(200)); + response->resp->version = request->req->version; + const char* mime_type = "text/html"; + if (filepath != NULL) { + mime_type = mime_get_type(filepath, DEFAULT_CONTENT_TYPE); } - fclose(file); - free(buffer); + http_header_list_add(response->headers, http_header_new(HEADER_CONTENT_TYPE, mime_type), false); + if (request->req->method == METHOD_HEAD) { + fclose(file); + http_body_set_type(response->body, BODY_NONE); + } else { + http_body_set_type(response->body, BODY_FILE); + response->body->data.file = file; + } + free(filepath); bool close_connection = false; @@ -275,9 +268,12 @@ FILE * server_generate_directory_index(config_host *hconfig, const char* dirpath char* file_mod_time = calloc(32, sizeof(char)); ctime_r(&file_mtime, file_mod_time); - utstring_printf(index, "\r\n", uri, uri, + char *file_basename = basename_r(uri); + + utstring_printf(index, "\r\n", uri, file_basename, (filesize!=NULL)?filesize:"N/A", (file_mod_time!=NULL)?file_mod_time:"N/A"); + free(file_basename); free(file_mod_time); free(filepath); free(filesize); @@ -286,7 +282,7 @@ FILE * server_generate_directory_index(config_host *hconfig, const char* dirpath closedir(dir); free(filestat); char *dirname = strdup(dirpath); - dirname = str_replace(dirname, hconfig->serve_dir, "/"); + dirname = str_replace(dirname, hconfig->serve_dir, ""); file_map *dirindex_map = file_map_new("dirindex.html"); if (dirindex_map == NULL) { diff --git a/src/http.c b/src/http.c index c2efe3e..275d89c 100644 --- a/src/http.c +++ b/src/http.c @@ -74,8 +74,9 @@ http_request_line *http_request_line_new(http_request_method method, const char* } void http_request_line_delete(http_request_line *req) { assert(req!=NULL); - - free(req->method_other); + if (req->method == METHOD_OTHER) { + free(req->method_other); + } free(req->uri); free(req); } @@ -311,6 +312,7 @@ http_response* http_response_new(http_response_line *resp) { response->headers = http_header_list_new(); response->body_chunked = false; response->body = http_body_new(BODY_NONE, NULL); + response->send_status = SEND_RESPONSE_LINE; return response; } void http_response_append_body(http_response *resp, const char* body) { @@ -332,62 +334,64 @@ char* http_response_write(http_response *resp) { assert(resp!=NULL); assert(resp->resp !=NULL); + if (resp->send_status == SEND_BODY || resp->send_status == SEND_DONE) { + return NULL; + } + UT_string *output = calloc(1, sizeof(UT_string)); ALLOC_CHECK(output); utstring_init(output); - if (resp->resp->version == HTTP10) { - utstring_printf(output, "HTTP/1.0 "); - } else if (resp->resp->version == HTTP11) { - utstring_printf(output, "HTTP/1.1 "); - } - //Write the response line - 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 - if (resp->body_chunked == false) { - //Add content length header - 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); - } else { //Chunked encoding - http_header_list_add(resp->headers, http_header_new(HEADER_TRANSFER_ENCODING, "chunked"), true); + if (resp->send_status == SEND_RESPONSE_LINE) { + if (resp->resp->version == HTTP10) { + utstring_printf(output, "HTTP/1.0 "); + } else if (resp->resp->version == HTTP11) { + utstring_printf(output, "HTTP/1.1 "); } + //Write the response line + utstring_printf(output, "%hu %s\r\n", resp->resp->code, http_response_line_get_message(resp->resp)); - //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); - } - - //Add date header - time_t timenow = time(NULL); - struct tm * timeinfo = gmtime(&timenow); - char dateStr[100] = {0}; - strftime(dateStr, 99, FORMAT_HEADER_DATE, timeinfo); - http_header_list_add(resp->headers, http_header_new(HEADER_DATE, dateStr), true); - - //Add server identifier header - http_header_list_add(resp->headers, http_header_new(HEADER_SERVER, SERVER_NAME), true); + resp->send_status = SEND_HEADERS; } - //write headers - http_header *elem; - HTTP_HEADER_FOREACH(resp->headers, elem) { - utstring_printf(output, "%s: %s\r\n", elem->name, elem->content); + if (resp->send_status == SEND_HEADERS) { + if (resp->resp->code != 100) { //No additional headers for Continue messages + if (resp->body_chunked == false) { + //Add content length header + 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); + } 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); + } + + //Add date header + time_t timenow = time(NULL); + struct tm * timeinfo = gmtime(&timenow); + char dateStr[100] = {0}; + strftime(dateStr, 99, FORMAT_HEADER_DATE, timeinfo); + http_header_list_add(resp->headers, http_header_new(HEADER_DATE, dateStr), true); + + //Add server identifier header + http_header_list_add(resp->headers, http_header_new(HEADER_SERVER, SERVER_NAME), true); + } + //write headers + http_header *elem; + HTTP_HEADER_FOREACH(resp->headers, elem) { + utstring_printf(output, "%s: %s\r\n", elem->name, elem->content); + } + utstring_printf(output, "\r\n"); + + resp->send_status = SEND_BODY; } - utstring_printf(output, "\r\n"); - //Write the request (if string) - if (resp->body->type == BODY_STRING) { - if (resp->body_chunked == false) { - 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; @@ -482,10 +486,16 @@ void http_response_list_append(http_response_list *list, http_response* response LL_APPEND(list->first, response); } +void http_response_list_remove(http_response_list *list, http_response* response) { + assert(list != NULL); + assert(response != NULL); + + LL_DELETE(list->first, response); +} http_response* http_response_list_next(http_response_list *list) { assert(list != NULL); - return http_response_list_next2(list, true); + return http_response_list_next2(list, false); } http_response* http_response_list_next2(http_response_list *list, bool remove) { assert(list != NULL); @@ -503,8 +513,8 @@ http_response* http_response_list_next2(http_response_list *list, bool remove) { void http_response_list_delete(http_response_list *list) { assert(list != NULL); - http_response *elem; - HTTP_RESPONSE_LIST_FOREACH(list, elem) { + http_response *elem, *tmp; + HTTP_RESPONSE_LIST_FOREACH_SAFE(list, elem, tmp) { http_response_delete(elem); } free(list); diff --git a/src/http.h b/src/http.h index 8dccc17..6ce7527 100644 --- a/src/http.h +++ b/src/http.h @@ -87,15 +87,21 @@ extern "C" { struct http_request *next; } http_request; + typedef enum http_response_send_status { + SEND_RESPONSE_LINE, SEND_HEADERS, SEND_BODY, SEND_DONE + } http_response_send_status; + typedef struct http_response { http_response_line *resp; http_header_list *headers; bool body_chunked; http_body *body; + http_response_send_status send_status; struct http_response *next; } http_response; #define HTTP_RESPONSE_LIST_FOREACH(list, elem) LL_FOREACH(list->first, elem) +#define HTTP_RESPONSE_LIST_FOREACH_SAFE(list, elem, tmp) LL_FOREACH_SAFE(list->first, elem, tmp) typedef struct http_response_list { http_response *first; @@ -138,6 +144,7 @@ extern "C" { http_response_list* http_response_list_new(); void http_response_list_append(http_response_list *list, http_response* response); + void http_response_list_remove(http_response_list *list, http_response* response); http_response* http_response_list_next(http_response_list *list); http_response* http_response_list_next2(http_response_list *list, bool remove); void http_response_list_delete(http_response_list *list); diff --git a/src/log.c b/src/log.c index 5fc058b..a40cd75 100644 --- a/src/log.c +++ b/src/log.c @@ -80,7 +80,7 @@ void*log_loop(void* arg) { void** buf = calloc(1, sizeof(void*)); char* timestr = calloc(32, sizeof(char)); time_t ctime; - struct tm *tinfo = calloc(1,sizeof(struct tm)); + struct tm tinfo = {0}; while(true) { //Read next message pointer from pipe if (read(l->pRead, buf, sizeof(void*)) <= 0) { @@ -95,8 +95,8 @@ void*log_loop(void* arg) { break; } ctime = time(NULL); - localtime_r(&ctime, tinfo); - if (strftime(timestr, 32, "%F %R", tinfo) == 0) { + localtime_r(&ctime, &tinfo); + if (strftime(timestr, 32, "%F %R", &tinfo) == 0) { strcpy(timestr, "N/A"); } log_msg* msg = (log_msg*)(*buf); diff --git a/src/main.c b/src/main.c index 5bfae3f..6aa9125 100644 --- a/src/main.c +++ b/src/main.c @@ -15,13 +15,22 @@ #include #include #include -#include +#include #include "util.h" +#include "log.h" #include "main.h" #include "server.h" #include "server-state.h" +static server_state *current_state = NULL; + +static void signal_handle(int sig) { + if (current_state != NULL) { + current_state->shutdown_requested = true; + } +} + int main(int argc, char** argv) { //Load the config @@ -32,8 +41,22 @@ int main(int argc, char** argv) { server_state *state = server_status_new(config); + current_state = state; + char sig_error_buf[128]; + if (signal(SIGINT, signal_handle) == SIG_ERR) { + char *errstr = strerror_r(errno, sig_error_buf, 127); + LOG(LERROR, "Failed to attach signal handler to SIGINT: %s", errstr); + } + if (signal(SIGTERM, signal_handle) == SIG_ERR) { + char *errstr = strerror_r(errno, sig_error_buf, 127); + LOG(LERROR, "Failed to attach signal handler to SIGTERM: %s", errstr); + } + //Run the server server_start(state); + current_state = NULL; + server_status_delete(state); + return (EXIT_SUCCESS); } \ No newline at end of file diff --git a/src/server-connection.c b/src/server-connection.c index 39578a3..7f80469 100644 --- a/src/server-connection.c +++ b/src/server-connection.c @@ -48,6 +48,7 @@ server_parse_status* server_parse_status_new() { state->parser_header_state = HSTATE_NONE; state->parser = calloc(1, sizeof(http_parser)); http_parser_init(state->parser, HTTP_REQUEST); + state->parser->data = (void*)state; return state; } @@ -67,7 +68,7 @@ void server_parse_status_delete(server_parse_status* state) { } void server_parser_status_reset(server_parse_status* state) { assert(state!=NULL); - + state->current_request = NULL; state->request_complete = false; state->parser_header_state = HSTATE_NONE; if (state->parser_current_header != NULL) { diff --git a/src/server-loop-write.c b/src/server-loop-write.c index 8120e2a..412e234 100644 --- a/src/server-loop-write.c +++ b/src/server-loop-write.c @@ -28,7 +28,66 @@ void* server_loop_write(void* arg) { server_connection *conn = (server_connection*)item->data; CONN_LOCK(conn); - + if (conn->pending_writes->first != NULL) { + errno = 0; + if (data_buffer_list_writeto_fd(conn->pending_writes, conn->skt->fd) < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + item->blocked = true; + } else { + char address[INET_ADDRSTRLEN]; + skt_clientaddr(conn->skt, address, INET_ADDRSTRLEN); + warning(true, "[#%lu %s] write error", conn->id, address); + conn->skt->error = true; + } + } + } + if (conn->pending_writes->first == NULL) { + http_response *response = http_response_list_next(conn->pending_responses); + while (response != NULL) { + char* resp_str = http_response_write(response); + if (resp_str != NULL) { + data_buffer_list_append(conn->pending_writes, resp_str, strlen(resp_str)); + free(resp_str); + } + if (conn->pending_writes->first != NULL) { + errno = 0; + if (data_buffer_list_writeto_fd(conn->pending_writes, conn->skt->fd) < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + item->blocked = true; + } else { + char address[INET_ADDRSTRLEN]; + skt_clientaddr(conn->skt, address, INET_ADDRSTRLEN); + warning(true, "[#%lu %s] write error", conn->id, address); + conn->skt->error = true; + } + } + } + if (conn->skt->error == false && + conn->pending_writes->first == NULL) { + + if (response->send_status == SEND_BODY) { + if (response->body->type == BODY_NONE) { + response->send_status = SEND_DONE; + } else { + http_body_write_result result = http_body_writeto_fd(response->body, conn->skt->fd); + if (result == HBWRITE_DONE) { + response->send_status = SEND_DONE; + } else if (result == HBWRITE_ERROR) { + conn->skt->error = true; + response->send_status = SEND_DONE; + } else if (result == HBWRITE_BLOCKED || result == HBWRITE_MORE) { + response = NULL; + } + } + } + if (response != NULL && response->send_status == SEND_DONE) { + http_response_list_remove(conn->pending_responses, response); + http_response_delete(response); + response = http_response_list_next(conn->pending_responses); + } + } + } //response != null + }//if no pending writes CONN_UNLOCK(conn); queue_return_item(th->pool->queue, item, item->blocked == false); diff --git a/src/server-socket.c b/src/server-socket.c index e92ac66..2028753 100644 --- a/src/server-socket.c +++ b/src/server-socket.c @@ -73,12 +73,14 @@ void server_socket_release(int fd) { } socket_info* server_socket_accept(int fd, int flags) { assert(fd>=0); - struct sockaddr_in* clientaddr = calloc(1, sizeof(struct sockaddr_in)); - int clientfd=0; + struct sockaddr_in* clientaddr = calloc(1, sizeof(struct sockaddr_in)); socklen_t clientaddr_len = (socklen_t)sizeof(struct sockaddr_in); + int clientfd=0; + clientfd = accept4(fd, (struct sockaddr*)clientaddr, &clientaddr_len, flags); if (clientfd < 0) { + free(clientaddr); if (errno == EAGAIN || errno == EWOULDBLOCK) { return NULL; } diff --git a/src/server-state.c b/src/server-state.c index fb387a9..5e4ebb6 100644 --- a/src/server-state.c +++ b/src/server-state.c @@ -75,6 +75,7 @@ void server_stop_pools(server_state *status) { thread_pool_stop(status->pools[i]); queue_delete(status->pools[i]->queue); thread_pool_stop(status->pools[i]); + thread_pool_delete(status->pools[i]); } memset(status->pools, 0, sizeof(status->pools)); } \ No newline at end of file diff --git a/src/server.c b/src/server.c index 1ce3034..1c3e5c1 100644 --- a/src/server.c +++ b/src/server.c @@ -72,7 +72,9 @@ void server_teardown(server_state *status) { //Close server socket close(status->epollfd); + status->epollfd = 0; server_socket_release(status->sfd); + status->sfd = 0; //Free mime data mime_destroy(); @@ -82,6 +84,5 @@ void server_teardown(server_state *status) { //Delete config config_server_delete(status->config); - - server_status_delete(status); + status->config = NULL; } \ No newline at end of file diff --git a/src/socket.c b/src/socket.c index d8f593d..4070809 100644 --- a/src/socket.c +++ b/src/socket.c @@ -93,7 +93,7 @@ int skt_data_buffer(socket_info *skt, data_buffer_list *list) { BUFFER_LIST_WRONLY_LOCK(list); LL_DELETE(list->first, elem); BUFFER_LIST_WRONLY_UNLOCK(list); - data_buffer_free(elem); + data_buffer_delete(elem); } BUFFER_LIST_RD_UNLOCK(list); } while(list->first != NULL); @@ -128,7 +128,7 @@ char* skt_clientaddr(socket_info *skt, char* address, size_t address_len) { inet_ntop(AF_INET, &skt->clientaddr->sin_addr, address, address_len); if (address == NULL) { warning(true, "error fetching client address"); - free(address); + address[0] = '\0'; } return address; } \ No newline at end of file diff --git a/src/thread-pool.c b/src/thread-pool.c index aa9d8f7..cf11b79 100644 --- a/src/thread-pool.c +++ b/src/thread-pool.c @@ -85,7 +85,9 @@ void thread_pool_stop(thread_pool *pool) { pool->shutdown = true; void* ret; if (pthread_join(pool->management_thread->pthread, &ret) != 0) { - fatal("Could not join thread pool manager"); + if (errno != EINTR) { + fatal("Could not join thread pool manager"); + } } } void thread_pool_add_thread(thread_pool *pool, thread *th) { diff --git a/src/util.c b/src/util.c index 17f392c..5d6a709 100644 --- a/src/util.c +++ b/src/util.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "ut/utstring.h" @@ -180,6 +181,24 @@ char* str_replace(char *haystack, const char *search, const char *replacement) { return result; } +char* basename_r(char* path) { + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + char* response = NULL; + + pthread_mutex_lock(&mutex); + + char* tmp = basename(path); + + response = calloc(strlen(tmp)+1, sizeof(char)); + ALLOC_CHECK(response); + strcpy(response, tmp); + + pthread_mutex_unlock(&mutex); + + return response; +} + file_map* file_map_new(const char* filename) { int fd = open(filename, O_RDONLY); diff --git a/src/util.h b/src/util.h index 351a1a0..e46f40a 100644 --- a/src/util.h +++ b/src/util.h @@ -36,6 +36,8 @@ extern "C" { char* str_trimwhitespace(char *str); char** str_splitlines(char *str, size_t *line_count); char* str_replace(char *str, const char *search, const char *replacement); + + char* basename_r(char* path); file_map* file_map_new(const char* filename); void file_map_delete(file_map* map); diff --git a/testreq.txt b/testreq.txt index 7b7f32c..43f35b0 100644 --- a/testreq.txt +++ b/testreq.txt @@ -4,5 +4,4 @@ Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0 User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36 Referer: https://www.google.co.uk/ Accept-Language: en-US,en;q=0.8 -Connection: close
%s%s%s
%s%s%s