add responding to requests

This commit is contained in:
Rokas Puzonas 2024-10-06 00:34:32 +03:00
parent 25730681a9
commit a387896ff2
11 changed files with 702 additions and 197 deletions

View File

@ -1,58 +1,135 @@
#ifndef ARENA_ #ifndef ARENA_
#define ARENA_ #define ARENA_
#include <assert.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#define ARENA_BLOCK_SIZE 256 #define ARENA_BLOCK_SIZE 128
#define DEFAULT_ALIGNMENT (sizeof(void*))
#include "byte_slice.c" #include "byte_slice.c"
struct arena_block {
uint8_t *ptr;
struct arena_block *prev;
};
struct arena { struct arena {
uint8_t *ptr; uint8_t *ptr;
size_t len; size_t len;
size_t capacity; size_t offset;
struct arena_block *prev_blocks;
}; };
int arena_init(struct arena *arena) { int arena_init(struct arena *arena) {
arena->ptr = malloc(ARENA_BLOCK_SIZE); arena->ptr = aligned_alloc(DEFAULT_ALIGNMENT, ARENA_BLOCK_SIZE);
if (!arena->ptr) { if (!arena->ptr) {
return -1; return -1;
} }
arena->len = 0; arena->len = ARENA_BLOCK_SIZE;
arena->capacity = ARENA_BLOCK_SIZE; arena->offset = 0;
arena->prev_blocks = NULL;
return 0; return 0;
} }
void arena_deinit(struct arena *arena) { void arena_deinit(struct arena *arena) {
free(arena->ptr); free(arena->ptr);
arena->ptr = NULL; arena->ptr = NULL;
struct arena_block *current = arena->prev_blocks;
while (current) {
struct arena_block *prev = current->prev;
free(current->ptr);
free(current);
current = prev;
}
arena->prev_blocks = NULL;
} }
static uint64_t div_round_up(uint64_t a, uint64_t b) { static bool is_power_of_two(uintptr_t x) {
return a % b == 0 ? a / b * b : (a / b + 1) * b; return (x & (x-1)) == 0;
} }
void *arena_alloc(struct arena *arena, size_t size) { static uintptr_t align_forward(uintptr_t ptr, uintptr_t align) {
assert(is_power_of_two(align));
uintptr_t modulo = ptr & (align - 1);
if (modulo != 0) {
return ptr + (align - modulo);
} else {
return ptr;
}
}
static uint32_t arena_get_next_offset(struct arena *arena, size_t alignemnt) {
return align_forward((uintptr_t)arena->ptr + arena->offset, alignemnt) - (uintptr_t)arena->ptr;
}
static int arena_push_prev_block(struct arena *arena, void *ptr) {
struct arena_block *block = malloc(sizeof(struct arena_block));
if (!block) {
free(ptr);
return -1;
}
block->ptr = ptr;
block->prev = arena->prev_blocks;
arena->prev_blocks = block;
return 0;
}
void *arena_alloc_aligned(struct arena *arena, size_t size, size_t alignemnt) {
if (arena->ptr == NULL && arena_init(arena)) { if (arena->ptr == NULL && arena_init(arena)) {
return NULL; return NULL;
} }
if (arena->len + size > arena->capacity) { // Just allocate the request amount if it is larger than a block.
size_t new_capacity = div_round_up(arena->len + size, ARENA_BLOCK_SIZE); if (size >= ARENA_BLOCK_SIZE) {
void *new_ptr = realloc(arena->ptr, new_capacity); void *ptr = aligned_alloc(alignemnt, size);
if (!ptr) {
return NULL;
}
if (arena_push_prev_block(arena, ptr)) {
free(ptr);
return NULL;
}
return ptr;
}
// Start a new block, if allocation will not fit
if (arena_get_next_offset(arena, alignemnt) + size > arena->len) {
void *new_ptr = aligned_alloc(DEFAULT_ALIGNMENT, ARENA_BLOCK_SIZE);
if (!new_ptr) { if (!new_ptr) {
return NULL; return NULL;
} }
if (arena_push_prev_block(arena, arena->ptr)) {
free(new_ptr);
return NULL;
}
arena->ptr = new_ptr; arena->ptr = new_ptr;
arena->capacity = new_capacity; arena->offset = 0;
arena->len = ARENA_BLOCK_SIZE;
} }
void *result = (void*)&arena->ptr[arena->len]; uint32_t offset = arena_get_next_offset(arena, alignemnt);
arena->len += size; assert(offset + size <= arena->len);
return result; void *ptr = &arena->ptr[offset];
arena->offset = offset + size;
return ptr;
}
void *arena_alloc(struct arena *arena, size_t size) {
return arena_alloc_aligned(arena, size, DEFAULT_ALIGNMENT);
} }
void *arena_dupe(struct arena *arena, void *ptr, size_t len) { void *arena_dupe(struct arena *arena, void *ptr, size_t len) {

View File

@ -3,10 +3,13 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "utils.c"
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
struct byte_slice { struct byte_slice {
uint8_t *ptr; uint8_t *ptr;
@ -113,4 +116,27 @@ void byte_slice_trim_str(struct byte_slice *slice, const char *str) {
byte_slice_trim(slice, str_slice); byte_slice_trim(slice, str_slice);
} }
void byte_slice_print(struct byte_slice slice) {
printf("%.*s", (int)slice.len, slice.ptr);
}
int byte_slice_to_uint32(struct byte_slice slice, uint32_t *result) {
if (slice.len == 0) {
return -1;
}
*result = 0;
for (size_t i = 0; i < slice.len; i++) {
uint8_t c = slice.ptr[i];
if (!is_digit(c)) {
return -1;
}
(*result) *= 10;
(*result) += c - '0';
}
return 0;
}
#endif //BYTE_SLICE_ #endif //BYTE_SLICE_

49
src/byte_slice_writer.c Normal file
View File

@ -0,0 +1,49 @@
#ifndef BUFFER_WRITER_
#define BUFFER_WRITER_
#include "byte_slice.c"
#include <stdarg.h>
#include <stdio.h>
struct byte_slice_writer {
struct byte_slice *buffer;
uint32_t capacity;
};
int writer_append(struct byte_slice_writer writer, struct byte_slice slice) {
if (writer.buffer->len + slice.len <= writer.capacity) {
memcpy(writer.buffer->ptr + writer.buffer->len, slice.ptr, slice.len);
writer.buffer->len += slice.len;
return 0;
}
return -1;
}
int writer_appendf(struct byte_slice_writer writer, const char *format, ...) {
int rc = -1;
va_list args;
va_start(args, format);
int size = vsnprintf(NULL, 0, format, args);
va_end(args);
if (writer.buffer->len + size + 1 <= writer.capacity) {
va_list args;
va_start(args, format);
vsnprintf(
(char*)writer.buffer->ptr + writer.buffer->len,
writer.capacity - writer.buffer->len,
format,
args
);
writer.buffer->len += size;
va_end(args);
return 0;
}
return -1;
}
#endif //BUFFER_WRITER_

View File

@ -2,35 +2,70 @@
#define CLIENT_ #define CLIENT_
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <errno.h> #include <errno.h>
#include <unistd.h>
#include "parser.c" #include "request_parser.c"
#include "request.c" #include "request.c"
#include "response.c"
#include "log.c" #include "log.c"
#include "byte_slice_writer.c"
#define CLIENT_READ_BUFFER_SIZE 4096 #define CLIENT_READ_BUFFER_SIZE 4096
#define CLIENT_WRITE_BUFFER_SIZE 4096
enum parser_state { enum http_client_state {
PARSER_STATE_REQUEST_LINE, CLIENT_STATE_INIT,
PARSER_STATE_HEADERS, CLIENT_STATE_REQUEST_LINE,
PARSER_STATE_BODY, CLIENT_STATE_HEADERS,
PARSER_STATE_DONE, CLIENT_STATE_BODY,
CLIENT_STATE_REQUEST_DONE,
CLIENT_STATE_WAIT_FOR_RESPONSE,
CLIENT_STATE_SEND_RESPONSE,
CLIENT_STATE_RESPONSE_DONE,
}; };
struct http_client { struct http_client {
int fd; int fd;
bool close;
struct sockaddr_storage addr; struct sockaddr_storage addr;
socklen_t addr_len; socklen_t addr_len;
// TODO: Make this buffer dynamic
uint8_t read_bufer[CLIENT_READ_BUFFER_SIZE]; uint8_t read_bufer[CLIENT_READ_BUFFER_SIZE];
size_t read_bufer_len; size_t read_bufer_len;
enum parser_state parse_state; // TODO: Make this buffer dynamic
struct http_request parse_request; uint8_t write_bufer[CLIENT_WRITE_BUFFER_SIZE];
size_t write_bufer_len;
// TODO: Maybe response and request should share the same memory arena?
enum http_client_state state;
struct http_request request;
struct http_response response;
}; };
void http_client_deinit(struct http_client *client) {
if (client->fd > 0) {
close(client->fd);
client->fd = 0;
}
http_request_deinit(&client->request);
http_response_deinit(&client->response);
}
bool http_client_can_recv(struct http_client *client) {
return client->read_bufer_len < CLIENT_READ_BUFFER_SIZE;
}
bool http_client_can_send(struct http_client *client) {
return client->write_bufer_len > 0;
}
int http_client_recv(struct http_client *client) { int http_client_recv(struct http_client *client) {
int result = recv( int result = recv(
client->fd, client->fd,
@ -41,81 +76,193 @@ int http_client_recv(struct http_client *client) {
if (result == 0) { if (result == 0) {
// TODO: Mark connection for closing // TODO: Mark connection for closing
return -1;
} else if (result < 0) { } else if (result < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
// TODO: Ignore error // TODO: Ignore error
return 0;
} else if (errno == ECONNRESET) { } else if (errno == ECONNRESET) {
// TODO: Mark connection for closing // TODO: Mark connection for closing
return -1;
} else {
// TODO: Research what else can happen
return -1;
} }
} else { }
struct http_request *request = &client->parse_request;
client->read_bufer_len += result; struct http_request *request = &client->request;
log_debug("Received %d bytes", result);
struct byte_slice buffer = byte_slice_init(client->read_bufer, client->read_bufer_len); client->read_bufer_len += result;
struct byte_slice line = { 0 };
while (buffer.len > 0) {
if (client->parse_state == PARSER_STATE_REQUEST_LINE) {
if (find_next_line(buffer, &line)) {
break;
}
log_debug("request-line: %.*s", line.len, (char*)line.ptr); struct byte_slice buffer = byte_slice_init(client->read_bufer, client->read_bufer_len);
while (true) {
if (client->state == CLIENT_STATE_INIT) {
http_request_deinit(&client->request);
http_response_deinit(&client->response);
if (parse_request_line(line, &request->method, &request->uri, &request->version)) { client->response.status_code = 200;
remove_line(&buffer, line);
break;
}
log_debug("- method: '%.*s'", request->method.len, (char*)request->method.ptr); client->state = CLIENT_STATE_REQUEST_LINE;
log_debug("- uri: '%.*s'", request->uri.len, (char*)request->uri.ptr);
log_debug("- version: 'HTTP/%d.%d'", request->version.major, request->version.minor);
remove_line(&buffer, line); } else if (client->state == CLIENT_STATE_REQUEST_LINE) {
client->parse_state = PARSER_STATE_HEADERS; struct byte_slice line = { 0 };
if (find_next_line(buffer, &line)) {
} else if (client->parse_state == PARSER_STATE_HEADERS) {
if (find_next_line(buffer, &line)) {
break;
}
if (line.len == 0) {
client->parse_state = PARSER_STATE_BODY;
remove_line(&buffer, line);
continue;
}
struct http_header header = { 0 };
if (parse_header_line(line, &header)) {
remove_line(&buffer, line);
break;
}
log_debug("header: %.*s: %.*s", header.name.len, (char*)header.name.ptr, header.value.len, (char*)header.value.ptr);
if (http_request_append_header(request, header)) {
remove_line(&buffer, line);
break;
}
remove_line(&buffer, line);
} else if (client->parse_state == PARSER_STATE_BODY) {
log_debug("data: %.*s", buffer.len, (char*)buffer.ptr);
buffer.len = 0;
client->parse_state = PARSER_STATE_DONE;
break; break;
} }
}
client->read_bufer_len = buffer.len; struct byte_slice method = { 0 };
log_debug("Left over %d bytes", client->read_bufer_len); struct byte_slice uri = { 0 };
if (parse_request_line(line, &method, &uri, &request->version)) {
remove_line(&buffer, line);
break;
}
client->response.version = request->version;
request->method = arena_dupe_slice(&request->arena, method);
if (request->method.len == 0) {
remove_line(&buffer, line);
break;
}
request->uri = arena_dupe_slice(&request->arena, uri);
if (request->uri.len == 0) {
remove_line(&buffer, line);
break;
}
remove_line(&buffer, line);
client->state = CLIENT_STATE_HEADERS;
} else if (client->state == CLIENT_STATE_HEADERS) {
struct byte_slice line = { 0 };
if (find_next_line(buffer, &line)) {
break;
}
if (line.len == 0) {
client->state = CLIENT_STATE_BODY;
remove_line(&buffer, line);
continue;
}
struct http_header header = { 0 };
if (parse_header_line(line, &header)) {
remove_line(&buffer, line);
break;
}
if (http_request_append_header(request, header)) {
remove_line(&buffer, line);
break;
}
remove_line(&buffer, line);
} else if (client->state == CLIENT_STATE_BODY) {
int content_length = http_headers_get_content_length(&request->headers);
struct byte_slice *transfer_encoding = http_request_find_header_str(request, "transfer-encoding");
if (content_length != -1) {
if (content_length > 0) {
if (buffer.len < content_length) {
// Wait for more data
// TODO: This will not work if the amount of data sent is more than 'CLIENT_READ_BUFFER_SIZE'
break;
}
struct byte_slice sub_buffer = byte_slice_init(buffer.ptr, content_length);
request->body = arena_dupe_slice(&request->arena, sub_buffer);
if (request->body.len == 0) {
return -1;
}
}
buffer.len = 0;
client->state = CLIENT_STATE_REQUEST_DONE;
} else if (transfer_encoding) {
log_debug("Transfer encoding not supported");
break;
} else {
client->state = CLIENT_STATE_REQUEST_DONE;
}
} else {
break;
}
}
client->read_bufer_len = buffer.len;
return 0;
}
int http_client_send(struct http_client *client) {
int result = send(
client->fd,
client->write_bufer,
client->write_bufer_len,
MSG_DONTWAIT
);
if (result == 0) {
// TODO: Mark connection for closing
return -1;
} else if (result < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// TODO: Ignore error
return 0;
} else if (errno == ECONNRESET) {
// TODO: Mark connection for closing
return -1;
} else {
// TODO: Research what else can happen
return -1;
}
}
memmove(client->write_bufer, client->write_bufer + result, client->write_bufer_len - result);
client->write_bufer_len -= result;
if (client->state == CLIENT_STATE_SEND_RESPONSE && client->write_bufer_len == 0) {
client->state = CLIENT_STATE_RESPONSE_DONE;
client->close = true;
} }
return 0; return 0;
} }
void http_client_respond(struct http_client *client) {
if (client->state != CLIENT_STATE_WAIT_FOR_RESPONSE) {
return;
}
struct byte_slice write_buffer = byte_slice_init(client->write_bufer, client->write_bufer_len);
struct byte_slice_writer writer = {
.buffer = &write_buffer,
.capacity = CLIENT_WRITE_BUFFER_SIZE
};
struct http_response *response = &client->response;
writer_appendf(writer, "HTTP/%d.%d %d\r\n", response->version.major, response->version.minor, response->status_code);
for (size_t i = 0; i < response->headers.len; i++) {
struct http_header *header = &response->headers.ptr[i];
writer_append(writer, header->name); // TODO: Assert that name contains only valid characters
writer_appendf(writer, ": ");
writer_append(writer, header->value); // TODO: Assert that value contains only valid characters
writer_appendf(writer, "\r\n");
}
if (response->body.len > 0) {
writer_appendf(writer, "Content-Length: %d\r\n", response->body.len);
writer_appendf(writer, "\r\n");
writer_append(writer, response->body);
}
client->write_bufer_len = write_buffer.len;
client->state = CLIENT_STATE_SEND_RESPONSE;
}
#endif //CLIENT_ #endif //CLIENT_

143
src/headers.c Normal file
View File

@ -0,0 +1,143 @@
#ifndef HEADERS_
#define HEADERS_
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include "arena.c"
#include "utils.c"
struct http_header {
struct byte_slice name;
struct byte_slice value;
};
struct http_headers {
struct http_header *ptr;
size_t len;
size_t capacity;
};
void http_headers_deinit(struct http_headers *headers) {
memset(headers, 0, sizeof(*headers));
}
int http_headers_append(struct http_headers *headers, struct arena *arena, struct http_header header) {
if (headers->len + 1 > headers->capacity) {
size_t new_capacity = headers->capacity * 1.5;
if (new_capacity == 0) {
new_capacity = 8;
}
struct http_header *new_ptr = arena_alloc(arena, new_capacity * sizeof(struct http_header));
if (!new_ptr) {
return -1;
}
if (headers->ptr) {
memcpy(new_ptr, headers->ptr, headers->len * sizeof(struct http_header));
}
headers->ptr = new_ptr;
headers->capacity = new_capacity;
}
struct byte_slice name = arena_dupe_slice(arena, header.name);
if (name.len == 0) {
return -1;
}
struct byte_slice value = arena_dupe_slice(arena, header.value);
if (value.len == 0) {
return -1;
}
headers->ptr[headers->len] = (struct http_header){
.name = name,
.value = value,
};
headers->len++;
return 0;
}
static bool case_insensitive_eql(struct byte_slice a, struct byte_slice b) {
if (a.len != b.len) {
return false;
}
for (size_t i = 0; i < a.len; i++) {
uint8_t a_char = a.ptr[i];
uint8_t b_char = b.ptr[i];
if (is_alpha(a_char)) {
a_char = toupper(a_char);
}
if (is_alpha(b_char)) {
b_char = toupper(b_char);
}
if (a_char != b_char) {
return false;
}
}
return true;
}
struct byte_slice *http_headers_find(struct http_headers *headers, struct byte_slice name) {
for (size_t i = 0; i < headers->len; i++) {
if (case_insensitive_eql(headers->ptr[i].name, name)) {
return &headers->ptr[i].value;
}
}
return NULL;
}
int http_headers_put(struct http_headers *headers, struct arena *arena, struct http_header header) {
struct byte_slice *existing_value = http_headers_find(headers, header.name);
if (existing_value != NULL) {
struct byte_slice value = arena_dupe_slice(arena, header.value);
if (value.len == 0) {
return -1;
}
*existing_value = value;
return 0;
} else {
return http_headers_append(headers, arena, header);
}
}
int http_headers_get_content_length(struct http_headers *headers) {
struct byte_slice name = byte_slice_init_str("content-length");
struct byte_slice *value = http_headers_find(headers, name);
if (!value) {
return -1;
}
uint32_t content_length = 0;
if (byte_slice_to_uint32(*value, &content_length)) {
return -1;
}
return content_length;
}
void http_header_print(struct http_header header) {
byte_slice_print(header.name);
printf(": ");
byte_slice_print(header.value);
}
void http_headers_print(struct http_headers headers) {
for (size_t i = 0; i< headers.len; i++) {
http_header_print(headers.ptr[i]);
printf("\n");
}
}
#endif //HEADERS_

11
src/http_version.c Normal file
View File

@ -0,0 +1,11 @@
#ifndef HTTP_VERSION_
#define HTTP_VERSION_
#include <stdint.h>
struct http_version {
uint8_t major;
uint8_t minor;
};
#endif //HTTP_VERSION_

View File

@ -4,16 +4,28 @@
#include <stdbool.h> #include <stdbool.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <poll.h> #include <poll.h>
#include <signal.h>
#include <netinet/in.h> #include <netinet/in.h>
#include "server.c" #include "server.c"
struct http_server server = { 0 };
void sighandler(int sig) {
http_server_deinit(&server);
exit(0);
}
int main() { int main() {
signal(SIGPIPE, SIG_IGN);
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
if (http_server_init(&server)) {
return -1;
}
uint16_t server_port = 8080; uint16_t server_port = 8080;
struct http_server server = { 0 };
http_server_init(&server);
if (http_server_listen(&server, server_port)) { if (http_server_listen(&server, server_port)) {
printf("Failed to listen on port %d\n", server_port); printf("Failed to listen on port %d\n", server_port);
return -1; return -1;
@ -22,8 +34,8 @@ int main() {
printf("Listening on port %d\n", server_port); printf("Listening on port %d\n", server_port);
while (true) { while (true) {
struct http_request request = { 0 }; struct http_client *client = NULL;
enum poll_result result = http_server_poll_request(&server, &request, -1); enum poll_result result = http_server_poll_request(&server, &client, -1);
if (result == POLL_RESULT_TIMEOUT) { if (result == POLL_RESULT_TIMEOUT) {
log_debug("Poll timeout\n"); log_debug("Poll timeout\n");
continue; continue;
@ -32,7 +44,17 @@ int main() {
break; break;
} }
printf("Got request\n"); struct byte_slice uri = client->request.uri;
if (byte_slice_eql_str(uri, "/secret")) {
http_response_append_header(&client->response, (struct http_header){
.name = byte_slice_init_str("Secret-header"),
.value = byte_slice_init_str("Juicy secret"),
});
http_response_set_body(&client->response, byte_slice_init_str("Hello, World! SECRET!\n"));
} else {
http_response_set_body(&client->response, byte_slice_init_str("Hello, World!\n"));
}
http_client_respond(client);
} }
http_server_deinit(&server); http_server_deinit(&server);

View File

@ -1,22 +1,13 @@
#ifndef REQUEST_ #ifndef REQUEST_
#define REQUEST_ #define REQUEST_
#include <ctype.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include "byte_slice.c" #include "byte_slice.c"
#include "arena.c" #include "arena.c"
#include "utils.c" #include "headers.c"
#include "http_version.c"
struct http_version {
uint8_t major;
uint8_t minor;
};
struct http_header {
struct byte_slice name;
struct byte_slice value;
};
struct http_request { struct http_request {
struct arena arena; struct arena arena;
@ -27,85 +18,43 @@ struct http_request {
struct http_version version; struct http_version version;
// Headers: // Headers:
struct http_header *headers; struct http_headers headers;
size_t headers_len;
size_t headers_capacity;
// Body: // Body:
struct byte_slice body; struct byte_slice body;
}; };
int http_request_append_header(struct http_request *request, struct http_header header) { void http_request_deinit(struct http_request *request) {
if (request->headers_len >= request->headers_capacity) { arena_deinit(&request->arena);
size_t new_capacity = request->headers_capacity * 1.5; http_headers_deinit(&request->headers);
if (new_capacity == 0) {
new_capacity = 16;
}
struct http_header *new_headers = arena_alloc(&request->arena, new_capacity * sizeof(struct http_header)); memset(request, 0, sizeof(struct http_request));
if (!new_headers) {
return -1;
}
if (request->headers) {
memcpy(new_headers, request->headers, request->headers_capacity * sizeof(struct http_header));
}
request->headers = new_headers;
request->headers_capacity = new_capacity;
}
struct byte_slice name = arena_dupe_slice(&request->arena, header.name);
if (name.len == 0) {
return -1;
}
struct byte_slice value = arena_dupe_slice(&request->arena, header.value);
if (value.len == 0) {
return -1;
}
request->headers[request->headers_len] = (struct http_header){
.name = name,
.value = value,
};
request->headers_len++;
return 0;
} }
static bool case_insensitive_eql(struct byte_slice a, struct byte_slice b) { int http_request_append_header(struct http_request *request, struct http_header header) {
if (a.len != b.len) { return http_headers_append(&request->headers, &request->arena, header);
return false;
}
for (size_t i = 0; i < a.len; i++) {
uint8_t a_char = a.ptr[i];
uint8_t b_char = b.ptr[i];
if (is_alpha(a_char)) {
a_char = toupper(a_char);
}
if (is_alpha(b_char)) {
b_char = toupper(b_char);
}
if (a_char != b_char) {
return false;
}
}
return true;
} }
struct byte_slice *http_request_find_header(struct http_request *request, struct byte_slice name) { struct byte_slice *http_request_find_header(struct http_request *request, struct byte_slice name) {
for (size_t i = 0; i < request->headers_len; i++) { return http_headers_find(&request->headers, name);
if (case_insensitive_eql(request->headers[i].name, name)) { }
return &request->headers[i].value;
}
}
return NULL; struct byte_slice *http_request_find_header_str(struct http_request *request, const char *name) {
struct byte_slice str_slice = byte_slice_init_str((char*)name);
return http_request_find_header(request, str_slice);
}
void http_request_print(struct http_request *request) {
byte_slice_print(request->method);
printf(" ");
byte_slice_print(request->uri);
printf(" HTTP/%d.%d\n", request->version.major, request->version.minor);
http_headers_print(request->headers);
if (request->body.len > 0) {
printf("\n");
byte_slice_print(request->body);
printf("\n");
}
} }
#endif //REQUEST_ #endif //REQUEST_

43
src/response.c Normal file
View File

@ -0,0 +1,43 @@
#ifndef RESPONSE_
#define RESPONSE_
#include "arena.c"
#include "headers.c"
#include "http_version.c"
struct http_response {
struct arena arena;
uint16_t status_code;
struct http_version version;
struct http_headers headers;
struct byte_slice body;
};
void http_response_deinit(struct http_response *response) {
arena_deinit(&response->arena);
http_headers_deinit(&response->headers);
memset(response, 0, sizeof(struct http_response));
}
int http_response_append_header(struct http_response *response, struct http_header header) {
return http_headers_append(&response->headers, &response->arena, header);
}
int http_response_set_body(struct http_response *response, struct byte_slice body) {
if (body.len == 0) {
response->body = byte_slice_init_zero();
return 0;
}
struct byte_slice body_dupe = arena_dupe_slice(&response->arena, body);
if (body_dupe.len == 0) {
return -1;
}
response->body = body_dupe;
return 0;
}
#endif //RESPONSE_

View File

@ -38,11 +38,30 @@ int http_server_init(struct http_server *server) {
return 0; return 0;
} }
void http_server_remove_client(struct http_server *server, size_t index) {
assert(index < server->clients_len);
struct http_client *client = &server->clients[index];
// log_debug("Closing connection");
http_client_deinit(client);
// TODO: Maybe don't do swap remove? Returned request pointers can become invalidated
if (server->clients_len > 1) {
server->clients[index] = server->clients[server->clients_len-1];
}
server->clients_len--;
}
void http_server_deinit(struct http_server *server) { void http_server_deinit(struct http_server *server) {
if (!server) { if (!server) {
return; return;
} }
while (server->clients_len > 0) {
http_server_remove_client(server, 0);
}
free(server->clients); free(server->clients);
server->clients = NULL; server->clients = NULL;
@ -84,51 +103,58 @@ err:
return -1; return -1;
} }
struct http_client *http_server_get_unused_client(struct http_server *server) { struct http_client *http_server_append_client(struct http_server *server, int fd, struct sockaddr *addr, socklen_t addr_len) {
if (server->clients_len >= server->clients_capacity) { if (server->clients_len >= server->clients_capacity) {
return NULL; return NULL;
} }
struct http_client *client = &server->clients[server->clients_len++]; struct http_client *client = &server->clients[server->clients_len++];
memset(client, 0, sizeof(*client)); memset(client, 0, sizeof(*client));
client->fd = fd;
memcpy(&client->addr, addr, addr_len);
client->addr_len = addr_len;
return client; return client;
} }
enum poll_result http_server_poll_request(struct http_server *server, struct http_request *result_request, int timeout) { enum poll_result http_server_poll_request(struct http_server *server, struct http_client **result_client, int timeout) {
enum poll_result result = POLL_RESULT_ERROR; enum poll_result result = POLL_RESULT_ERROR;
if (server->fd == 0) { if (server->fd == 0) {
goto end; goto end;
} }
struct pollfd *pollfds = NULL; struct pollfd *pollfds = calloc(1 + server->clients_capacity, sizeof(struct pollfd));
if (!pollfds) {
goto end;
}
pollfds[0].fd = server->fd;
pollfds[0].events = POLLIN;
while (true) { while (true) {
for (size_t i = 0; i < server->clients_len; i++) { for (size_t i = 0; i < server->clients_len; i++) {
struct http_client *client = &server->clients[i]; struct http_client *client = &server->clients[i];
if (client->parse_state == PARSER_STATE_DONE) { if (client->state == CLIENT_STATE_REQUEST_DONE) {
result = POLL_RESULT_REQUEST; result = POLL_RESULT_REQUEST;
*result_request = client->parse_request; client->state = CLIENT_STATE_WAIT_FOR_RESPONSE;
(*result_client) = client;
return 0; return 0;
} }
} }
pollfds = calloc(1 + server->clients_capacity, sizeof(struct pollfd));
if (!pollfds) {
goto end;
}
size_t fds_count = 1 + server->clients_len; size_t fds_count = 1 + server->clients_len;
memset(pollfds, 0, sizeof(struct pollfd) * fds_count);
pollfds[0].fd = server->fd;
pollfds[0].events = POLLIN;
for (size_t i = 0; i < server->clients_len; i++) { for (size_t i = 0; i < server->clients_len; i++) {
struct http_client *client = &server->clients[i]; struct http_client *client = &server->clients[i];
struct pollfd *pollfd = &pollfds[i+1]; struct pollfd *pollfd = &pollfds[i+1];
pollfd->fd = server->clients[i].fd; pollfd->fd = server->clients[i].fd;
if (client->read_bufer_len < CLIENT_READ_BUFFER_SIZE) { pollfd->events = 0;
pollfd->events = POLLIN; if (http_client_can_recv(client)) {
pollfd->events |= POLLIN;
}
if (http_client_can_send(client)) {
pollfd->events |= POLLOUT;
} }
} }
@ -145,19 +171,20 @@ enum poll_result http_server_poll_request(struct http_server *server, struct htt
struct pollfd *server_pollfd = &pollfds[0]; struct pollfd *server_pollfd = &pollfds[0];
if (server_pollfd->revents & POLLIN) { if (server_pollfd->revents & POLLIN) {
struct sockaddr_storage client_addr = { 0 }; struct sockaddr_storage addr = { 0 };
socklen_t client_addr_len = sizeof(client_addr); socklen_t addr_len = sizeof(addr);
int client_fd = accept(server->fd, (struct sockaddr *)&client_addr, &client_addr_len); int client_fd = accept(server->fd, (struct sockaddr *)&addr, &addr_len);
if (client_fd == -1) { if (client_fd == -1) {
perror("accept"); perror("accept");
} else { } else {
log_debug("Accepted connection"); struct http_client *client = http_server_append_client(server, client_fd, (struct sockaddr *)&addr, addr_len);
struct http_client *client = http_server_get_unused_client(server); if (client) {
assert(client != NULL); // log_debug("Accepted connection");
client->fd = client_fd; } else {
client->addr = client_addr; log_debug("Failed to accept connection: Max clients reached");
client->addr_len = client_addr_len; close(client_fd);
}
} }
} }
@ -166,18 +193,29 @@ enum poll_result http_server_poll_request(struct http_server *server, struct htt
struct http_client *client = &server->clients[i - 1]; struct http_client *client = &server->clients[i - 1];
if (pollfd->revents & POLLIN) { if (pollfd->revents & POLLIN) {
http_client_recv(client); if (http_client_recv(client)) {
client->close = true;
}
}
if (pollfd->revents & POLLOUT) {
if (http_client_send(client)) {
client->close = true;
}
} }
if (pollfd->revents & (POLLHUP | POLLERR | POLLERR)) { if (pollfd->revents & (POLLHUP | POLLERR | POLLERR)) {
log_debug("Closing connection"); client->close = true;
} }
} }
free(pollfds); for (size_t i = 0; i < server->clients_len; i++) {
pollfds = NULL; struct http_client *client = &server->clients[i];
if (client->close) {
http_server_remove_client(server, i);
i--;
}
}
} }
end: end:
free(pollfds); free(pollfds);
return result; return result;