http-parser.c

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

commit 08385ef17c8d8e2780a9af932190e0337f8a864a
parent 2502aff2da3632e7dd7d0832edc075070967a64c
Author: finwo <finwo@pm.me>
Date:   Sat,  9 Nov 2019 18:40:34 +0100

Copied base from crosync

Diffstat:
Asrc/http.c | 191+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/http.h | 44++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 235 insertions(+), 0 deletions(-)

diff --git a/src/http.c b/src/http.c @@ -0,0 +1,191 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> + +#include "http.h" + +#ifndef NULL +#define NULL ((void*)0) +#endif + +void http_header_free(struct http_header *header) { + if (header->next) http_header_free(header->next); + if (header->key) free(header->key); + if (header->value) free(header->value); + free(header); +} + +char *http_header_get(struct http_request *request, char *key) { + struct http_header *header = request->headers; + while(header) { + if (!strcasecmp(key, header->key)) { + return header->value; + } + header = header->next; + } + return NULL; +} + +void http_request_free(struct http_request *request) { + if (request->body) free(request->body); + if (request->method) free(request->method); + if (request->path) free(request->path); + if (request->headers) http_header_free(request->headers); + free(request); +} + +struct http_request * http_request_init() { + struct http_request *request = calloc(1, sizeof(struct http_request)); + return request; +} + +void http_request_data(struct http_request *request, char *data, int size) { + struct http_header *header; + struct http_event *ev; + char *index; + int newsize; + char *colon; + char *buf; + char *aContentLength; + int iContentLength; + + // Add event data to buffer + if (!request->body) request->body = malloc(1); + request->body = realloc(request->body, request->bodysize + size + 1); + memcpy(request->body + request->bodysize, data, size); + request->bodysize += size; + + // Make string functions not segfault + *(request->body + request->bodysize) = '\0'; + + int running = 1; + while(running) { + switch(request->state) { + case HTTP_STATE_PANIC: + return; + case HTTP_STATE_METHOD: + + // Wait for more data if not line break found + index = strstr(request->body, "\r\n"); + if (!index) return; + *(index) = '\0'; + + // Read method and path + request->method = calloc(1, 7); + request->path = calloc(1, 512); + if (sscanf(request->body, "%6s %511s", request->method, request->path) != 2) { + request->state = HTTP_STATE_PANIC; + return; + } + + // Remove the method line + newsize = request->bodysize - 2 - (index - request->body); + buf = calloc(1,newsize+1); + memcpy(buf, index + 2, newsize); + free(request->body); + request->body = buf; + request->bodysize = newsize; + + // Signal we're now reading headers + request->state = HTTP_STATE_HEADER; + break; + + case HTTP_STATE_HEADER: + + // Wait for more data if not line break found + index = strstr(request->body, "\r\n"); + if (!index) return; + *(index) = '\0'; + + // Detect end of headers + newsize = strlen(request->body); + if (!newsize) { + + // Remove the blank line + newsize = request->bodysize - 2; + buf = calloc(1,newsize+1); + memcpy(buf, index + 2, newsize); + free(request->body); + request->body = buf; + request->bodysize = newsize; + + // GET/DELETE = start responding + if ( + (!strcmp(request->method, "GET")) || + (!strcmp(request->method, "DELETE")) + ) { + request->state = HTTP_STATE_RESPONSE; + break; + } + + request->state = HTTP_STATE_BODY; + break; + } + + // Prepare new header + header = calloc(1,sizeof(header)); + header->key = calloc(1,strlen(request->body)); + header->value = calloc(1,strlen(request->body)); + + // Copy key & value + colon = strstr(request->body, ":"); + if (colon) { + *(colon) = '\0'; + strcpy(header->key, request->body); + strcpy(header->value, colon + 1); + } + + // Assign to the header list + header->next = request->headers; + request->headers = header; + + // Remove the header line + newsize = request->bodysize - 2 - (index - request->body); + buf = calloc(1,newsize+1); + memcpy(buf, index + 2, newsize); + free(request->body); + request->body = buf; + request->bodysize = newsize; + + break; + + case HTTP_STATE_BODY: + + // Fetch the content length + aContentLength = http_header_get(request, "content-length"); + iContentLength = atoi(aContentLength); + + // Not enough data = skip + if (request->bodysize < iContentLength) { + running = 0; + break; + } + + // Change size to indicated size + request->bodysize = iContentLength; + request->state = HTTP_STATE_RESPONSE; + break; + + case HTTP_STATE_RESPONSE: + + if (request->onRequest) { + ev = calloc(1,sizeof(struct http_event)); + ev->request = request; + request->onRequest(ev); + request->onRequest = NULL; + } + + running = 0; + break; + } + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/src/http.h b/src/http.h @@ -0,0 +1,44 @@ +#ifndef CROSYNC_HTTP_H +#define CROSYNC_HTTP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define HTTP_STATE_METHOD 0 +#define HTTP_STATE_HEADER 1 +#define HTTP_STATE_BODY 2 +#define HTTP_STATE_RESPONSE 3 +#define HTTP_STATE_PANIC 666 + +struct http_header { + void *next; + char *key; + char *value; +}; + +struct http_event { + struct http_request *request; +}; + +struct http_request { + int bodysize; + int state; + char *method; + char *path; + struct http_header *headers; + char *body; + void (*onRequest)(struct http_event*); + void *udata; +}; + +char *http_header_get(struct http_request *request, char *key); +void http_request_free(struct http_request *request); +struct http_request * http_request_init(); +void http_request_data(struct http_request *request, char *data, int size); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CROSYNC_HTTP_H