#include "response.h" #include #include #include // Status text lookup static const char* get_status_text(int code) { switch (code) { case 200: return "OK"; case 201: return "Created"; case 204: return "No Content"; case 301: return "Moved Permanently"; case 302: return "Found"; case 304: return "Not Modified"; case 400: return "Bad Request"; case 401: return "Unauthorized"; case 403: return "Forbidden"; case 404: return "Not Found"; case 405: return "Method Not Allowed"; case 500: return "Internal Server Error"; case 502: return "Bad Gateway"; case 503: return "Service Unavailable"; default: return "Unknown"; } } // response_new creates a new response. response_t* response_new(void) { response_t *res = calloc(1, sizeof(response_t)); if (!res) return NULL; res->status_code = 200; res->body_cap = 8192; res->body = malloc(res->body_cap); return res; } // response_free destroys a response. void response_free(response_t *res) { if (!res) return; response_header_t *h = res->headers; while (h) { response_header_t *next = h->next; free(h->name); free(h->value); free(h); h = next; } free(res->body); free(res); } // response_reset clears the response. void response_reset(response_t *res) { res->status_code = 200; res->status_text = NULL; // Clear headers response_header_t *h = res->headers; while (h) { response_header_t *next = h->next; free(h->name); free(h->value); free(h); h = next; } res->headers = NULL; res->body_len = 0; res->headers_sent = 0; } // response_status sets the status code. response_t* response_status(response_t *res, int code) { res->status_code = code; return res; } // response_header adds a header. response_t* response_header(response_t *res, const char *name, const char *value) { response_header_t *h = malloc(sizeof(response_header_t)); if (!h) return res; h->name = strdup(name); h->value = strdup(value); h->next = res->headers; res->headers = h; return res; } // response_content_type sets Content-Type header. response_t* response_content_type(response_t *res, const char *type) { return response_header(res, "Content-Type", type); } // response_content_length sets Content-Length header. response_t* response_content_length(response_t *res, size_t len) { char buf[32]; snprintf(buf, sizeof(buf), "%zu", len); return response_header(res, "Content-Length", buf); } // ensure_capacity ensures body buffer has enough space. static int ensure_capacity(response_t *res, size_t needed) { if (res->body_len + needed > res->body_cap) { size_t new_cap = res->body_cap * 2; while (new_cap < res->body_len + needed) new_cap *= 2; char *new_body = realloc(res->body, new_cap); if (!new_body) return -1; res->body = new_body; res->body_cap = new_cap; } return 0; } // response_body sets the body. response_t* response_body(response_t *res, const char *data, size_t len) { if (ensure_capacity(res, len) < 0) return res; memcpy(res->body, data, len); res->body_len = len; return res; } // response_write appends to body. response_t* response_write(response_t *res, const char *data, size_t len) { if (ensure_capacity(res, len) < 0) return res; memcpy(res->body + res->body_len, data, len); res->body_len += len; return res; } // response_text sends plain text. response_t* response_text(response_t *res, int status, const char *text) { response_status(res, status); response_content_type(res, "text/plain"); response_body(res, text, strlen(text)); return res; } // response_html sends HTML. response_t* response_html(response_t *res, int status, const char *html) { response_status(res, status); response_content_type(res, "text/html; charset=utf-8"); response_body(res, html, strlen(html)); return res; } // response_json sends JSON. response_t* response_json(response_t *res, int status, const char *json) { response_status(res, status); response_content_type(res, "application/json"); response_body(res, json, strlen(json)); return res; } // response_redirect sends a redirect. response_t* response_redirect(response_t *res, const char *url, int status) { if (status == 0) status = 302; response_status(res, status); response_header(res, "Location", url); return res; } // response_cookie sets a cookie. response_t* response_cookie(response_t *res, const char *name, const char *value, const char *path, int max_age, int http_only, int secure) { char cookie[1024]; int len = snprintf(cookie, sizeof(cookie), "%s=%s", name, value); if (path) { len += snprintf(cookie + len, sizeof(cookie) - len, "; Path=%s", path); } if (max_age > 0) { len += snprintf(cookie + len, sizeof(cookie) - len, "; Max-Age=%d", max_age); } if (http_only) { len += snprintf(cookie + len, sizeof(cookie) - len, "; HttpOnly"); } if (secure) { len += snprintf(cookie + len, sizeof(cookie) - len, "; Secure"); } len += snprintf(cookie + len, sizeof(cookie) - len, "; SameSite=Lax"); return response_header(res, "Set-Cookie", cookie); } // response_serialize writes response to buffer. int response_serialize(response_t *res, char *buf, size_t size) { int len = 0; // Status line len = snprintf(buf, size, "HTTP/1.1 %d %s\r\n", res->status_code, get_status_text(res->status_code)); // Headers response_header_t *h = res->headers; while (h) { len += snprintf(buf + len, size - len, "%s: %s\r\n", h->name, h->value); h = h->next; } // Content-Length if body exists if (res->body_len > 0) { len += snprintf(buf + len, size - len, "Content-Length: %zu\r\n", res->body_len); } // End headers len += snprintf(buf + len, size - len, "\r\n"); // Body if (res->body_len > 0 && len + res->body_len < size) { memcpy(buf + len, res->body, res->body_len); len += res->body_len; } return len; }