#include "worker.h" #include "client.h" #include "server.h" #include #include 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); }