http-parser.c

Small C library to parse HTTP requests
Log | Files | Refs | README | LICENSE

commit 9354097fa8faf95da3677003bad0599a67166b4c
parent 441c9a60a640da25e5c349da8db75dd6d0220a00
Author: finwo <finwo@pm.me>
Date:   Sun, 10 Nov 2019 21:45:46 +0100

Added request/response serialization

Diffstat:
Msrc/http-parser.c | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/http-parser.h | 6++++++
Mtest.c | 6+++++-
3 files changed, 155 insertions(+), 1 deletion(-)

diff --git a/src/http-parser.c b/src/http-parser.c @@ -123,6 +123,8 @@ struct http_parser_message * http_parser_request_init() { struct http_parser_message * http_parser_response_init() { struct http_parser_message *message = http_parser_request_init(); message->status = 200; + message->version = calloc(1,4); + strcpy(message->version, "1.1"); return message; } @@ -236,6 +238,9 @@ static int http_parser_message_read_chunked(struct http_parser_message *message) message->chunksize = xtoi(aChunkSize); free(aChunkSize); + // Add it to the total + message->chunktotal += message->chunksize; + // Remove chunksize line http_parser_message_remove_body_string(message); @@ -273,6 +278,141 @@ static int http_parser_message_read_chunked(struct http_parser_message *message) return 2; } +char * http_parser_print_pair_response(struct http_parser_pair *pair) { + return http_parser_print_response(pair->response); +} + +char * http_parser_print_pair_quest(struct http_parser_pair *pair) { + return http_parser_print_response(pair->response); +} + +char * http_parser_print_response(struct http_parser_message *response) { + char *result = calloc(1,256); + struct http_parser_header *header; + int index, length; + + // Status + sprintf(result, + "HTTP/%s %d %s\r\n" + , response->version + , response->status + , response->statusMessage + ); + + // Headers + header = response->headers; + while(header) { + index = strlen(result); + length = index + + strlen(header->key) + + 2 + + strlen(header->value) + + 3 + + 3; + + // Asign memory + result = realloc(result,length); + *(result + length) = '\0'; + + // Write header + sprintf(result + index, + "%s: %s\r\n", + header->key, + header->value + ); + + // Next header + header = header->next; + } + + strcpy( result + strlen(result), "\r\n" ); + + // Write body + index = strlen(result); + length = index + response->bodysize; + result = realloc(result, length + 1); + *(result + length) = '\0'; + memcpy( result + index + 2, response->body, response->bodysize ); + + return result;; +} + +char * http_parser_print_request(struct http_parser_message *request) { + char *result = calloc(1,8192); + struct http_parser_header *header; + int index, length; + + // Status + sprintf(result, + "%s %s" + , request->method + , request->path + ); + + // Query + if (request->query) { + *(result + strlen(result)) = '?'; + strcpy( result + strlen(result), request->query ); + } + + // HTTP version + strcpy( result + strlen(result), " HTTP/" ); + strcpy( result + strlen(result), request->version ); + strcpy( result + strlen(result), "\r\n" ); + + // Headers + header = request->headers; + while(header) { + index = strlen(result); + length = index + + strlen(header->key) + + 2 + + strlen(header->value) + + 3; + + // Asign memory + result = realloc(result,length); + *(result + length) = '\0'; + + // Write header + sprintf(result + index, + "%s: %s\r\n", + header->key, + header->value + ); + + // Next header + header = header->next; + } + + strcpy( result + strlen(result), "\r\n" ); + + // Handle chunked header + if (http_parser_header_get(request, "transfer-encoding")) { + result = realloc(result, strlen(result) + 20); + sprintf(result+strlen(result), "%x\r\n", request->bodysize); + } + + // Write body + index = strlen(result); + length = index + request->bodysize; + result = realloc(result, length + 1); + *(result + length) = '\0'; + memcpy( result + index, request->body, request->bodysize ); + + if (http_parser_header_get(request, "transfer-encoding")) { + result = realloc(result, length + 6); + *(result+length+0) = '0'; + *(result+length+1) = '\r'; + *(result+length+2) = '\n'; + *(result+length+3) = '\r'; + *(result+length+4) = '\n'; + *(result+length+5) = '\0'; + } + + return result;; +} + /** * Pass data into the pair's request * @@ -387,6 +527,7 @@ void http_parser_request_data(struct http_parser_message *request, char *data, i aChunkSize = http_parser_header_get(request, "transfer-encoding"); if (aChunkSize) { if (!strcmp(aChunkSize, "chunked")) { + request->chunktotal = 0; request->_state = HTTP_PARSER_STATE_BODY_CHUNKED; break; } @@ -433,6 +574,7 @@ void http_parser_request_data(struct http_parser_message *request, char *data, i free(request->body); request->body = request->buf; request->buf = NULL; + request->bodysize = request->chunktotal; } // Mark the request as ready @@ -513,6 +655,7 @@ void http_parser_response_data(struct http_parser_message *response, char *data, aChunkSize = http_parser_header_get(response, "transfer-encoding"); if (aChunkSize) { if (!strcmp(aChunkSize, "chunked")) { + response->chunktotal = 0; response->_state = HTTP_PARSER_STATE_BODY_CHUNKED; break; } @@ -559,6 +702,7 @@ void http_parser_response_data(struct http_parser_message *response, char *data, free(response->body); response->body = response->buf; response->buf = NULL; + response->bodysize = response->chunktotal; } // Mark the request as ready diff --git a/src/http-parser.h b/src/http-parser.h @@ -39,6 +39,7 @@ struct http_parser_message { int bufsize; int chunksize; int bodysize; + int chunktotal; int _state; }; @@ -68,6 +69,11 @@ void http_parser_pair_response_data(struct http_parser_pair *pair, char *data, i void http_parser_pair_free(struct http_parser_pair *pair); void http_parser_message_free(struct http_parser_message *subject); +char * http_parser_print_pair_response(struct http_parser_pair *pair); +char * http_parser_print_pair_request(struct http_parser_pair *pair); +char * http_parser_print_response(struct http_parser_message *response); +char * http_parser_print_request(struct http_parser_message *request); + #ifdef __cplusplus } // extern "C" #endif diff --git a/test.c b/test.c @@ -59,7 +59,7 @@ char *postMessage = ; char *postChunkedMessage = - "POST /foobar HTTP/1.1\r\n" + "POST /foobar?token=pizza HTTP/1.1\r\n" "Host: localhost\r\n" "Transfer-Encoding: chunked\r\n" "\r\n" @@ -117,6 +117,7 @@ int main() { ASSERT("request->version is 1.1", strcmp(request->version, "1.1") == 0); ASSERT("request->method is GET", strcmp(request->method, "GET") == 0); ASSERT("request->path is /foobar", strcmp(request->path, "/foobar") == 0); + ASSERT("request->toString matches", strcmp(getMessage, http_parser_print_request(request))); http_parser_message_free(request); request = http_parser_request_init(); @@ -127,6 +128,7 @@ int main() { ASSERT("request->method is POST", strcmp(request->method, "POST") == 0); ASSERT("request->path is /foobar", strcmp(request->path, "/foobar") == 0); ASSERT("request->body is \"Helo World\\r\\n\"", strcmp(request->body, "Hello World\r\n") == 0); + ASSERT("request->toString matches", strcmp(postMessage, http_parser_print_request(request))); http_parser_message_free(request); request = http_parser_request_init(); @@ -149,6 +151,7 @@ int main() { ASSERT("response->status = 200", response->status == 200); ASSERT("response->statusmessage = \"OK\"", strcmp(response->statusMessage, "OK") == 0); ASSERT("response->body = \"Hello World\\r\\n\"", strcmp(response->body, "Hello World\r\n") == 0); + ASSERT("response->toString matches", strcmp(responseMessage, http_parser_print_response(response))); http_parser_message_free(response); response = http_parser_response_init(); @@ -167,6 +170,7 @@ int main() { ASSERT("response->status = 404", response->status == 404); ASSERT("response->statusmessage = \"Not Found\"", strcmp(response->statusMessage, "Not Found") == 0); ASSERT("response->body = \"Not Found\\r\\n\"", strcmp(response->body, "Not Found\r\n") == 0); + ASSERT("response->toString matches", strcmp(responseNotFoundMessage, http_parser_print_response(response))); return err; }