mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-27 16:24:07 +02:00
130 lines
3.4 KiB
C
130 lines
3.4 KiB
C
#include "worker.h"
|
|
#include "client.h"
|
|
#include "server.h"
|
|
#include <pthread.h>
|
|
#include <string.h>
|
|
|
|
void http_worker_ctx_construct(WorkerCtx* ctx, const HttpServer* server)
|
|
{
|
|
ctx->server = server;
|
|
pthread_mutex_init(&ctx->mutex, NULL);
|
|
pthread_cond_init(&ctx->cond, NULL);
|
|
client_queue_construct(&ctx->req_queue, 8192);
|
|
}
|
|
|
|
void http_worker_ctx_destroy(WorkerCtx* ctx)
|
|
{
|
|
pthread_mutex_destroy(&ctx->mutex);
|
|
pthread_cond_destroy(&ctx->cond);
|
|
client_queue_destroy(&ctx->req_queue);
|
|
}
|
|
|
|
void http_worker_construct(Worker* worker, WorkerCtx* ctx)
|
|
{
|
|
*worker = (Worker) {
|
|
.thread = (pthread_t) { 0 },
|
|
.ctx = ctx,
|
|
};
|
|
|
|
pthread_create(&worker->thread, NULL, http_worker_thread_fn, worker);
|
|
}
|
|
|
|
void http_worker_destroy(Worker* worker)
|
|
{
|
|
if (worker->thread != 0) {
|
|
pthread_cancel(worker->thread);
|
|
|
|
// a bit ugly, but who cares?
|
|
pthread_cond_broadcast(&worker->ctx->cond);
|
|
|
|
pthread_join(worker->thread, NULL);
|
|
}
|
|
}
|
|
|
|
void* http_worker_thread_fn(void* data)
|
|
{
|
|
Worker* worker = data;
|
|
http_worker_listen(worker);
|
|
return NULL;
|
|
}
|
|
|
|
void http_worker_listen(Worker* worker)
|
|
{
|
|
WorkerCtx* ctx = worker->ctx;
|
|
while (true) {
|
|
pthread_testcancel();
|
|
|
|
pthread_mutex_lock(&ctx->mutex);
|
|
|
|
if (client_queue_size(&ctx->req_queue) > 0) {
|
|
ClientConnection connection;
|
|
client_queue_pop(&ctx->req_queue, &connection);
|
|
pthread_mutex_unlock(&ctx->mutex);
|
|
|
|
http_worker_handle_connection(worker, connection);
|
|
continue;
|
|
}
|
|
|
|
pthread_cond_wait(&ctx->cond, &ctx->mutex);
|
|
|
|
pthread_mutex_unlock(&ctx->mutex);
|
|
}
|
|
}
|
|
|
|
void http_worker_handle_connection(Worker* worker, ClientConnection connection)
|
|
{
|
|
(void)worker;
|
|
|
|
Client* client = http_client_new(connection);
|
|
Request request;
|
|
|
|
int res = http_client_next(client, &request);
|
|
if (res != 0) {
|
|
fprintf(stderr,
|
|
"warning: failed to parse request. sending 400 Bad Request "
|
|
"response\n");
|
|
const char* response
|
|
= "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n";
|
|
ssize_t bytes_written
|
|
= write(connection.file, response, strlen(response));
|
|
if (bytes_written != (ssize_t)strlen(response)) {
|
|
fprintf(stderr, "error: could not send 400 Bad Request response\n");
|
|
}
|
|
goto l0_return;
|
|
}
|
|
|
|
HttpCtx handler_ctx = {
|
|
.client = &client->connection,
|
|
.req = &request,
|
|
.req_body = request.body,
|
|
.req_body_size = request.body_size,
|
|
.res_headers = { 0 },
|
|
.user_ctx = worker->ctx->server->user_ctx,
|
|
};
|
|
|
|
header_vec_construct(&handler_ctx.res_headers);
|
|
|
|
bool been_handled = false;
|
|
|
|
for (size_t i = 0; i < worker->ctx->server->handlers.size; ++i) {
|
|
Handler* handler = &worker->ctx->server->handlers.data[i];
|
|
if (handler->method != request.method)
|
|
continue;
|
|
if (strcmp(handler->path, request.path) != 0)
|
|
continue;
|
|
handler->handler(&handler_ctx);
|
|
been_handled = true;
|
|
break;
|
|
}
|
|
|
|
if (!been_handled && worker->ctx->server->not_found_handler != NULL) {
|
|
worker->ctx->server->not_found_handler(&handler_ctx);
|
|
}
|
|
|
|
header_vec_destroy(&handler_ctx.res_headers);
|
|
http_request_destroy(&request);
|
|
l0_return:
|
|
close(client->connection.file);
|
|
http_client_free(client);
|
|
}
|