Finished basic server
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{dirname}}</title>
|
||||
<title>Index of {{dirname}}/</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{dirname}}</h1>
|
||||
<h1>{{dirname}}/</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--<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>-->
|
||||
<configurationDescriptor version="94">
|
||||
<logicalFolder name="root" displayName="root" projectFiles="true" kind="ROOT">
|
||||
<logicalFolder name="HeaderFiles"
|
||||
@@ -72,7 +90,7 @@
|
||||
<itemPath>content/error.html</itemPath>
|
||||
<itemPath>content/public_html/index.html</itemPath>
|
||||
<itemPath>content/khttpd.ini</itemPath>
|
||||
<itemPath>content/public_html/lorem.txt</itemPath>
|
||||
<itemPath>content/public_html/test/lorem.txt</itemPath>
|
||||
</logicalFolder>
|
||||
<projectmakefile>Makefile</projectmakefile>
|
||||
<confs>
|
||||
@@ -110,26 +128,8 @@
|
||||
</item>
|
||||
<item path="content/public_html/index.html" ex="false" tool="3" flavor2="0">
|
||||
</item>
|
||||
<item path="content/public_html/lorem.txt" ex="false" tool="3" flavor2="0">
|
||||
<item path="content/public_html/test/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">
|
||||
@@ -256,26 +256,8 @@
|
||||
</item>
|
||||
<item path="content/public_html/index.html" ex="false" tool="3" flavor2="0">
|
||||
</item>
|
||||
<item path="content/public_html/lorem.txt" ex="false" tool="3" flavor2="0">
|
||||
<item path="content/public_html/test/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">
|
||||
|
||||
@@ -5,30 +5,29 @@
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
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->rOffset<http_body_len(body)) {
|
||||
size_t write_len = http_body_len(body) - body->rOffset;
|
||||
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,9 +199,11 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
body->rOffset += written;
|
||||
if (result == HBWRITE_DONE) {
|
||||
@@ -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) {
|
||||
|
||||
@@ -127,18 +127,7 @@ 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";
|
||||
@@ -146,10 +135,14 @@ http_response* server_process_request(config_server* config, http_request *reque
|
||||
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);
|
||||
}
|
||||
if (request->req->method == METHOD_HEAD) {
|
||||
fclose(file);
|
||||
free(buffer);
|
||||
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, "<tr><td><a href=\"%s\">%s</a></td><td>%s</td><td>%s</td></tr>\r\n", uri, uri,
|
||||
char *file_basename = basename_r(uri);
|
||||
|
||||
utstring_printf(index, "<tr><td><a href=\"%s\">%s</a></td><td>%s</td><td>%s</td></tr>\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) {
|
||||
|
||||
36
src/http.c
36
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);
|
||||
|
||||
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,10 +334,15 @@ 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->send_status == SEND_RESPONSE_LINE) {
|
||||
if (resp->resp->version == HTTP10) {
|
||||
utstring_printf(output, "HTTP/1.0 ");
|
||||
} else if (resp->resp->version == HTTP11) {
|
||||
@@ -344,6 +351,10 @@ char* http_response_write(http_response *resp) {
|
||||
//Write the response line
|
||||
utstring_printf(output, "%hu %s\r\n", resp->resp->code, http_response_line_get_message(resp->resp));
|
||||
|
||||
resp->send_status = SEND_HEADERS;
|
||||
}
|
||||
|
||||
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
|
||||
@@ -371,7 +382,6 @@ char* http_response_write(http_response *resp) {
|
||||
//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) {
|
||||
@@ -379,15 +389,9 @@ 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) {
|
||||
http_body_writeto_utstring(resp->body, output);
|
||||
}
|
||||
if (resp->body_chunked == true) {
|
||||
http_chunks_write(resp->body->data.str, output);
|
||||
}
|
||||
resp->send_status = SEND_BODY;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
25
src/main.c
25
src/main.c
@@ -15,13 +15,22 @@
|
||||
#include <sys/mman.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include <bits/stdio2.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -85,8 +85,10 @@ void thread_pool_stop(thread_pool *pool) {
|
||||
pool->shutdown = true;
|
||||
void* ret;
|
||||
if (pthread_join(pool->management_thread->pthread, &ret) != 0) {
|
||||
if (errno != EINTR) {
|
||||
fatal("Could not join thread pool manager");
|
||||
}
|
||||
}
|
||||
}
|
||||
void thread_pool_add_thread(thread_pool *pool, thread *th) {
|
||||
thread_start(th, pool->func);
|
||||
|
||||
19
src/util.c
19
src/util.c
@@ -11,6 +11,7 @@
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#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);
|
||||
|
||||
@@ -37,6 +37,8 @@ extern "C" {
|
||||
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);
|
||||
char* file_map_copyto_string(file_map* map, char* str, size_t str_len);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user