/
sawixy
/
Milk
Обзор
Документация
Войти
/
sawixy
/
Milk
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
src/http.c
392 строки
12 KB
sawixy
Added http parser/formatter
23 ноя 2025, 13:29
23 ноя 2025, 13:29
e32c48a
Код
Авторство
О чём код?
#include <http.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <milk/log.h> DEFINE_VEC(HTTPhead); #define check(pointer, size) \ if (pointer >= size) { \ free(parser->data); \ parser->data = buf; \ return NOT_PARSED; \ } HTTPparser new_HTTPparser(ParserType parser_type){ HTTPparser parser; parser.parsertype = parser_type; parser.data = malloc(1); parser.data[0] = '\0'; parser.state = UNDEFINED; parser.pointer = 0; if (parser_type == REQUEST){ parser.parsed.req.method = NULL; parser.parsed.req.uri = NULL; parser.parsed.req.version = NULL; parser.parsed.req.headers = vHTTPhead_new(0); parser.parsed.req.body = NULL; } else { parser.parsed.resp.status = 0; parser.parsed.resp.status_text = NULL; parser.parsed.resp.version = NULL; parser.parsed.resp.headers = vHTTPhead_new(0); parser.parsed.resp.body = NULL; } return parser; } void free_HTTPparser(HTTPparser *parser){ if (!parser) return; free(parser->data); parser->data = NULL; if (parser->parsertype == REQUEST){ free(parser->parsed.req.method); free(parser->parsed.req.uri); free(parser->parsed.req.version); free(parser->parsed.req.body); FOR_EACH(HTTPhead, parser->parsed.req.headers, { free(item->key); free(item->value); }); vHTTPhead_free(&parser->parsed.req.headers); } else { free(parser->parsed.resp.status_text); free(parser->parsed.resp.version); free(parser->parsed.resp.body); FOR_EACH(HTTPhead, parser->parsed.resp.headers, { free(item->key); free(item->value); }); vHTTPhead_free(&parser->parsed.resp.headers); } } ParseResult parseHTTP(HTTPparser* parser, char* data) { if (!data) return NOT_PARSED; int data_len = strlen(data); int current_len = strlen(parser->data); char* buf = malloc(current_len + data_len + 1); if (!buf) return NOT_PARSED; buf[0] = '\0'; strcat(buf, parser->data); strcat(buf, data); int size = strlen(buf); int len = 0; if (parser->state == UNDEFINED || parser->state == METHOD || parser->state == STATUS) { if (parser->parsertype == REQUEST) { // Method len = 0; while (parser->pointer + len < size && buf[parser->pointer + len] != ' ') { len++; } check(parser->pointer + len, size); parser->parsed.req.method = malloc(len + 1); strncpy(parser->parsed.req.method, buf + parser->pointer, len); parser->parsed.req.method[len] = '\0'; parser->state = URI; parser->pointer += len + 1; check(parser->pointer, size); // URI len = 0; while (parser->pointer + len < size && buf[parser->pointer + len] != ' ') { len++; } check(parser->pointer + len, size); parser->parsed.req.uri = malloc(len + 1); strncpy(parser->parsed.req.uri, buf + parser->pointer, len); parser->parsed.req.uri[len] = '\0'; parser->state = VERSION; parser->pointer += len + 1; check(parser->pointer, size); // Version len = 0; while (parser->pointer + len < size && buf[parser->pointer + len] != '\r') { len++; } check(parser->pointer + len, size); parser->parsed.req.version = malloc(len + 1); strncpy(parser->parsed.req.version, buf + parser->pointer, len); parser->parsed.req.version[len] = '\0'; parser->state = HEADERS; parser->pointer += len + 2; // \r\n check(parser->pointer, size); } else { // Version len = 0; while (parser->pointer + len < size && buf[parser->pointer + len] != ' ') { len++; } check(parser->pointer + len, size); parser->parsed.resp.version = malloc(len + 1); strncpy(parser->parsed.resp.version, buf + parser->pointer, len); parser->parsed.resp.version[len] = '\0'; parser->state = STATUS; parser->pointer += len + 1; check(parser->pointer, size); // Status code len = 0; while (parser->pointer + len < size && buf[parser->pointer + len] != ' ') { len++; } check(parser->pointer + len, size); char* status_str = malloc(len + 1); strncpy(status_str, buf + parser->pointer, len); status_str[len] = '\0'; parser->parsed.resp.status = atoi(status_str); free(status_str); parser->state = STATUS_TEXT; parser->pointer += len + 1; check(parser->pointer, size); // Status text len = 0; while (parser->pointer + len < size && buf[parser->pointer + len] != '\r') { len++; } check(parser->pointer + len, size); parser->parsed.resp.status_text = malloc(len + 1); strncpy(parser->parsed.resp.status_text, buf + parser->pointer, len); parser->parsed.resp.status_text[len] = '\0'; parser->state = HEADERS; parser->pointer += len + 2; check(parser->pointer, size); } } // Headers parsing while (parser->state == HEADERS){ // Check for end of headers if (parser->pointer + 1 < size && buf[parser->pointer] == '\r' && buf[parser->pointer + 1] == '\n') { parser->state = BODY; parser->pointer += 2; break; } // key len = 0; int key_start = parser->pointer; while (parser->pointer + len < size && buf[parser->pointer + len] != ':') { len++; } check(parser->pointer + len, size); HTTPhead header; header.key = malloc(len + 1); strncpy(header.key, buf + key_start, len); header.key[len] = '\0'; parser->pointer += len + 1; // skip key and ':' check(parser->pointer, size); // skip spaces after colon while (parser->pointer < size && buf[parser->pointer] == ' ') { parser->pointer++; } check(parser->pointer, size); // value len = 0; int value_start = parser->pointer; while (parser->pointer + len < size && buf[parser->pointer + len] != '\r') { len++; } check(parser->pointer + len, size); header.value = malloc(len + 1); strncpy(header.value, buf + value_start, len); header.value[len] = '\0'; parser->pointer += len + 2; // skip value and \r\n if (parser->parsertype == REQUEST) { vHTTPhead_push_back(&parser->parsed.req.headers, header); } else { vHTTPhead_push_back(&parser->parsed.resp.headers, header); } check(parser->pointer, size); } // Body parsing if (parser->state == BODY) { int content_length = 0; if (parser->parsertype == REQUEST) { FOR_EACH(HTTPhead, parser->parsed.req.headers, { if (strcasecmp(item->key, "Content-Length") == 0){ content_length = atoi(item->value); break; } }); } else { FOR_EACH(HTTPhead, parser->parsed.resp.headers, { if (strcasecmp(item->key, "Content-Length") == 0){ content_length = atoi(item->value); break; } }); } if (content_length > 0 && parser->pointer + content_length <= size) { if (parser->parsertype == REQUEST) { free(parser->parsed.req.body); // освобождаем предыдущее значение если есть parser->parsed.req.body = malloc(content_length + 1); strncpy(parser->parsed.req.body, buf + parser->pointer, content_length); parser->parsed.req.body[content_length] = '\0'; } else { free(parser->parsed.resp.body); parser->parsed.resp.body = malloc(content_length + 1); strncpy(parser->parsed.resp.body, buf + parser->pointer, content_length); parser->parsed.resp.body[content_length] = '\0'; } parser->pointer += content_length; parser->state = COMPLETE; } else if (content_length == 0) { parser->state = COMPLETE; } } int remaining = size - parser->pointer; if (remaining > 0) { free(parser->data); parser->data = malloc(remaining + 1); strncpy(parser->data, buf + parser->pointer, remaining); parser->data[remaining] = '\0'; parser->pointer = 0; } else { free(parser->data); parser->data = malloc(1); parser->data[0] = '\0'; parser->pointer = 0; } free(buf); return (parser->state == COMPLETE) ? PARSED : NOT_PARSED; } // formatters char* format_request(HTTPreq req) { if (!req.method || !req.uri || !req.version) return NULL; // Calculate total length needed size_t total_len = 0; // Start line total_len += strlen(req.method) + 1; // method + space total_len += strlen(req.uri) + 1; // uri + space total_len += strlen(req.version) + 2; // version + \r\n // Headers FOR_EACH(HTTPhead, req.headers, { total_len += strlen(item->key) + 2; // key + ": " total_len += strlen(item->value) + 2; // value + \r\n }); total_len += 2; // \r\n after headers // Body if (req.body) { total_len += strlen(req.body); } // Allocate and build the string char* result = malloc(total_len + 1); if (!result) return NULL; char* ptr = result; // Start line ptr += sprintf(ptr, "%s %s %s\r\n", req.method, req.uri, req.version); // Headers FOR_EACH(HTTPhead, req.headers, { ptr += sprintf(ptr, "%s: %s\r\n", item->key, item->value); }); // End of headers ptr += sprintf(ptr, "\r\n"); // Body if (req.body) { ptr += sprintf(ptr, "%s", req.body); } return result; } char* format_response(HTTPresp resp) { if (!resp.version || !resp.status_text) return NULL; // Calculate total length needed size_t total_len = 0; // Status line total_len += strlen(resp.version) + 1; // version + space total_len += 3 + 1; // status code (3 digits) + space total_len += strlen(resp.status_text) + 2; // status text + \r\n // Headers FOR_EACH(HTTPhead, resp.headers, { total_len += strlen(item->key) + 2; // key + ": " total_len += strlen(item->value) + 2; // value + \r\n }); total_len += 2; // \r\n after headers // Body if (resp.body) { total_len += strlen(resp.body); } // Allocate and build the string char* result = malloc(total_len + 1); if (!result) return NULL; char* ptr = result; // Status line ptr += sprintf(ptr, "%s %d %s\r\n", resp.version, resp.status, resp.status_text); // Headers FOR_EACH(HTTPhead, resp.headers, { ptr += sprintf(ptr, "%s: %s\r\n", item->key, item->value); }); // End of headers ptr += sprintf(ptr, "\r\n"); // Body if (resp.body) { ptr += sprintf(ptr, "%s", resp.body); } return result; }