#include "context.h" #include #include #include #include #include #include #include // context_new creates a new context for a connection. context_t* context_new(conn_t *conn) { // Use connection's arena for all allocations arena_t *arena = conn->arena; context_t *ctx = arena_alloc(arena, sizeof(context_t)); if (!ctx) return NULL; ctx->conn = conn; ctx->arena = arena; ctx->data = NULL; // Create request ctx->request = request_new(arena); if (!ctx->request) return NULL; // Parse request from connection buffer if (request_parse(ctx->request, conn->rbuf, conn->rlen) < 0) { return NULL; } // Create response ctx->response = response_new(); if (!ctx->response) return NULL; return ctx; } // context_free frees the context. void context_free(context_t *ctx) { if (!ctx) return; // Response is malloced separately response_free(ctx->response); // Everything else is in arena and will be freed with connection } // context_set stores data in context. void context_set(context_t *ctx, const char *key, void *value) { context_data_t *d = arena_alloc(ctx->arena, sizeof(context_data_t)); if (!d) return; d->key = arena_strdup(ctx->arena, key, strlen(key)); d->value = value; d->next = ctx->data; ctx->data = d; } // context_get retrieves data from context. void* context_get(context_t *ctx, const char *key) { context_data_t *d = ctx->data; while (d) { if (strcmp(d->key, key) == 0) { return d->value; } d = d->next; } return NULL; } // Request helpers const char* ctx_param(context_t *ctx, const char *name) { return request_param(ctx->request, name); } const char* ctx_query(context_t *ctx, const char *name) { return request_query(ctx->request, name); } const char* ctx_header(context_t *ctx, const char *name) { return request_header(ctx->request, name); } const char* ctx_cookie(context_t *ctx, const char *name) { return request_cookie(ctx->request, name); } const char* ctx_body(context_t *ctx) { return request_body(ctx->request); } // Response helpers void ctx_status(context_t *ctx, int code) { response_status(ctx->response, code); } void ctx_header_set(context_t *ctx, const char *name, const char *value) { response_header(ctx->response, name, value); } // ctx_text sends plain text response. void ctx_text(context_t *ctx, int status, const char *text) { response_text(ctx->response, status, text); ctx_send(ctx); } // ctx_html sends HTML response. void ctx_html(context_t *ctx, int status, const char *html) { response_html(ctx->response, status, html); ctx_send(ctx); } // ctx_json sends JSON response. void ctx_json(context_t *ctx, int status, const char *json) { response_json(ctx->response, status, json); ctx_send(ctx); } // ctx_redirect sends redirect response. void ctx_redirect(context_t *ctx, const char *url) { response_redirect(ctx->response, url, 302); ctx_send(ctx); } // ctx_error sends error response. void ctx_error(context_t *ctx, int status, const char *message) { // Check if client expects JSON if (request_expects_json(ctx->request)) { char json[256]; snprintf(json, sizeof(json), "{\"error\":\"%s\"}", message ? message : "Unknown error"); response_json(ctx->response, status, json); } else { response_text(ctx->response, status, message ? message : "Error"); } ctx_send(ctx); } // ctx_send sends the response to client. int ctx_send(context_t *ctx) { conn_t *c = ctx->conn; // Serialize response to connection write buffer c->wlen = response_serialize(ctx->response, c->wbuf, c->wsize); c->wpos = 0; return 0; } // ctx_file sends a file as response. void ctx_file(context_t *ctx, const char *path) { int fd = open(path, O_RDONLY); if (fd < 0) { ctx_error(ctx, 404, "File not found"); return; } struct stat st; if (fstat(fd, &st) < 0) { close(fd); ctx_error(ctx, 500, "Cannot stat file"); return; } // Determine content type from extension const char *content_type = "application/octet-stream"; const char *ext = strrchr(path, '.'); if (ext) { if (strcmp(ext, ".html") == 0) content_type = "text/html"; else if (strcmp(ext, ".css") == 0) content_type = "text/css"; else if (strcmp(ext, ".js") == 0) content_type = "application/javascript"; else if (strcmp(ext, ".json") == 0) content_type = "application/json"; else if (strcmp(ext, ".png") == 0) content_type = "image/png"; else if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) content_type = "image/jpeg"; else if (strcmp(ext, ".gif") == 0) content_type = "image/gif"; else if (strcmp(ext, ".svg") == 0) content_type = "image/svg+xml"; else if (strcmp(ext, ".txt") == 0) content_type = "text/plain"; else if (strcmp(ext, ".xml") == 0) content_type = "application/xml"; } // For small files, read and send directly if (st.st_size < 1024 * 1024) { // 1MB threshold char *buf = malloc(st.st_size); if (buf) { if (read(fd, buf, st.st_size) == st.st_size) { ctx_status(ctx, 200); ctx_header_set(ctx, "Content-Type", content_type); ctx_write(ctx, buf, st.st_size); ctx_send(ctx); } free(buf); } } else { // File too large - send error for now ctx_error(ctx, 413, "File too large"); } close(fd); } // ctx_write writes data to response body. void ctx_write(context_t *ctx, const char *data, size_t len) { if (!ctx->response) return; // If no body yet, allocate if (!ctx->response->body) { ctx->response->body = malloc(len); if (ctx->response->body) { memcpy(ctx->response->body, data, len); ctx->response->body_len = len; } } else { // Append to existing body char *new_body = realloc(ctx->response->body, ctx->response->body_len + len); if (new_body) { memcpy(new_body + ctx->response->body_len, data, len); ctx->response->body = new_body; ctx->response->body_len += len; } } } // ctx_printf writes formatted data to response body. void ctx_printf(context_t *ctx, const char *fmt, ...) { char buf[4096]; va_list args; va_start(args, fmt); int len = vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); if (len > 0 && len < (int)sizeof(buf)) { ctx_write(ctx, buf, len); } } // ctx_send_chunk sends a chunk of data (for streaming). int ctx_send_chunk(context_t *ctx, const char *data, size_t len) { // For now, just accumulate and send ctx_write(ctx, data, len); return 0; }