commit 441c9a60a640da25e5c349da8db75dd6d0220a00
parent 6afe6873bc955a82da1a4b112c81b1acef06e0ab
Author: finwo <finwo@pm.me>
Date: Sun, 10 Nov 2019 18:46:42 +0100
Added response parsing
Diffstat:
| M | src/http-parser.c | | | 151 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
| M | src/http-parser.h | | | 8 | ++++---- |
| M | test.c | | | 54 | +++++++++++++++++++++++++++++++++++++++++++++++++++++- |
3 files changed, 203 insertions(+), 10 deletions(-)
diff --git a/src/http-parser.c b/src/http-parser.c
@@ -294,12 +294,30 @@ void http_parser_pair_request_data(struct http_parser_pair *pair, char *data, in
}
/**
+ * Pass data into the pair's request
+ *
+ * Triggers onRequest if set
+ */
+void http_parser_pair_response_data(struct http_parser_pair *pair, char *data, int size) {
+ struct http_parser_event *ev;
+ http_parser_response_data(pair->request, data, size);
+ if (pair->request->ready && pair->onResponse) {
+ ev = calloc(1,sizeof(struct http_parser_event));
+ ev->request = pair->request;
+ ev->response = pair->response;
+ ev->pair = pair;
+ ev->udata = pair->udata;
+ pair->onResponse(ev);
+ free(ev);
+ pair->onResponse = NULL;
+ }
+}
+
+/**
* Insert data into a http_message, acting as if it's a request
*/
void http_parser_request_data(struct http_parser_message *request, char *data, int size) {
char *index;
- char *colon;
- char *buf;
char *aContentLength;
int iContentLength;
char *aChunkSize;
@@ -318,7 +336,7 @@ void http_parser_request_data(struct http_parser_message *request, char *data, i
switch(request->_state) {
case HTTP_PARSER_STATE_PANIC:
return;
- case HTTP_PARSER_STATE_METHOD:
+ case HTTP_PARSER_STATE_INIT:
// Wait for more data if not line break found
index = strstr(request->body, "\r\n");
@@ -373,7 +391,6 @@ void http_parser_request_data(struct http_parser_message *request, char *data, i
break;
}
}
-
}
// Fetch the content length
@@ -425,7 +442,131 @@ void http_parser_request_data(struct http_parser_message *request, char *data, i
}
}
-// TODO: http_parser_response_data
+/**
+ * Insert data into a http_message, acting as if it's a request
+ */
+void http_parser_response_data(struct http_parser_message *response, char *data, int size) {
+ char *index;
+ char *aStatus;
+ int iContentLength;
+ char *aContentLength;
+ char *aChunkSize;
+ int res;
+
+ // Add event data to buffer
+ if (!response->body) response->body = malloc(1);
+ response->body = realloc(response->body, response->bodysize + size + 1);
+ memcpy(response->body + response->bodysize, data, size);
+ response->bodysize += size;
+
+ // Make string functions not segfault
+ *(response->body + response->bodysize) = '\0';
+
+ while(1) {
+ switch(response->_state) {
+ case HTTP_PARSER_STATE_PANIC:
+ return;
+ case HTTP_PARSER_STATE_INIT:
+
+ // Wait for more data if not line break found
+ index = strstr(response->body, "\r\n");
+ if (!index) return;
+ *(index) = '\0';
+
+ // Read version and status
+ response->version = calloc(1, 4);
+ response->statusMessage = calloc(1, 8192);
+ aStatus = calloc(1, 4);
+ if (sscanf(response->body, "HTTP/%3s %3s %8191c", response->version, aStatus, response->statusMessage) != 3) {
+ response->_state = HTTP_PARSER_STATE_PANIC;
+ return;
+ }
+
+ // Turn the text status into a number
+ response->status = atoi(aStatus);
+ free(aStatus);
+
+ // Remove status line
+ http_parser_message_remove_body_string(response);
+
+ // Signal we're now reading headers
+ response->_state = HTTP_PARSER_STATE_HEADER;
+ break;
+
+ case HTTP_PARSER_STATE_HEADER:
+ if (!http_parser_message_read_header(response)) {
+ if (
+ http_parser_header_get(response, "content-length") ||
+ http_parser_header_get(response, "transfer-encoding")
+ ) {
+ response->_state = HTTP_PARSER_STATE_BODY;
+ } else {
+ response->_state = HTTP_PARSER_STATE_DONE;
+ }
+ }
+ break;
+
+ case HTTP_PARSER_STATE_BODY:
+
+ // Detect chunked encoding
+ if (response->chunksize == -1) {
+ aChunkSize = http_parser_header_get(response, "transfer-encoding");
+ if (aChunkSize) {
+ if (!strcmp(aChunkSize, "chunked")) {
+ response->_state = HTTP_PARSER_STATE_BODY_CHUNKED;
+ break;
+ }
+ }
+ }
+
+ // Fetch the content length
+ aContentLength = http_parser_header_get(response, "content-length");
+ if (!aContentLength) {
+ response->_state = HTTP_PARSER_STATE_DONE;
+ break;
+ }
+ iContentLength = atoi(aContentLength);
+
+ // Not enough data = skip
+ if (response->bodysize < iContentLength) {
+ return;
+ }
+
+ // Change size to indicated size
+ response->bodysize = iContentLength;
+ response->_state = HTTP_PARSER_STATE_DONE;
+ break;
+
+ case HTTP_PARSER_STATE_BODY_CHUNKED:
+ res = http_parser_message_read_chunked(response);
+
+ if (res == 0) {
+ // Done
+ response->_state = HTTP_PARSER_STATE_DONE;
+ } else if (res == 1) {
+ // More data needed
+ return;
+ } else if (res == 2) {
+ // Still reading
+ }
+
+ break;
+
+ case HTTP_PARSER_STATE_DONE:
+
+ // Temporary buffer > direct buffer
+ if (response->buf) {
+ free(response->body);
+ response->body = response->buf;
+ response->buf = NULL;
+ }
+
+ // Mark the request as ready
+ response->ready = 1;
+ return;
+ }
+ }
+}
#ifdef __cplusplus
} // extern "C"
diff --git a/src/http-parser.h b/src/http-parser.h
@@ -5,12 +5,12 @@
extern "C" {
#endif
-#define HTTP_PARSER_STATE_METHOD 0
+#define HTTP_PARSER_STATE_INIT 0
#define HTTP_PARSER_STATE_HEADER 1
#define HTTP_PARSER_STATE_BODY 2
#define HTTP_PARSER_STATE_BODY_CHUNKED 3
#define HTTP_PARSER_STATE_DONE 4
-#define HTTP_PARSER_STATE_PANIC 666
+#define HTTP_PARSER_STATE_PANIC 666
struct http_parser_header {
void *next;
@@ -28,6 +28,7 @@ struct http_parser_event {
struct http_parser_message {
int ready;
int status;
+ char *statusMessage;
char *method;
char *path;
char *query;
@@ -59,8 +60,7 @@ struct http_parser_message * http_parser_request_init();
struct http_parser_message * http_parser_response_init();
void http_parser_request_data(struct http_parser_message *request, char *data, int size);
-
-void http_parser_response_data(struct http_parser_message *request, char *data, int size);
+void http_parser_response_data(struct http_parser_message *response, char *data, int size);
void http_parser_pair_request_data(struct http_parser_pair *pair, char *data, int size);
void http_parser_pair_response_data(struct http_parser_pair *pair, char *data, int size);
diff --git a/test.c b/test.c
@@ -49,6 +49,7 @@ char *getMessage =
"Host: localhost\r\n"
"\r\n"
;
+
char *postMessage =
"POST /foobar HTTP/1.1\r\n"
"Host: localhost\r\n"
@@ -56,6 +57,7 @@ char *postMessage =
"\r\n"
"Hello World\r\n"
;
+
char *postChunkedMessage =
"POST /foobar HTTP/1.1\r\n"
"Host: localhost\r\n"
@@ -67,13 +69,31 @@ char *postChunkedMessage =
"llo World\r\n"
"0\r\n"
;
+
char *responseMessage =
"HTTP/1.0 200 OK\r\n"
- "Content-Length: 11\r\n"
+ "Content-Length: 13\r\n"
"\r\n"
"Hello World\r\n"
;
+char *responseChunkedMessage =
+ "HTTP/1.0 200 OK\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "\r\n"
+ "2\r\n"
+ "He\r\n"
+ "B\r\n"
+ "llo World\r\n"
+ "0\r\n"
+;
+
+char *responseNotFoundMessage =
+ "HTTP/1.0 404 Not Found\r\n"
+ "Content-Length: 11\r\n"
+ "\r\n"
+ "Not Found\r\n"
+;
/* // Passing network data into it */
@@ -89,6 +109,8 @@ int main() {
ASSERT("request->body is null", request->body == NULL);
ASSERT("request->chunksize is -1", request->chunksize == -1);
+ http_parser_message_free(request);
+ request = http_parser_request_init();
http_parser_request_data(request, getMessage, strlen(getMessage));
printf("# GET request\n");
@@ -116,5 +138,35 @@ int main() {
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);
+ printf("# Pre-loaded response\n");
+ ASSERT("response->status = 200", response->status == 200);
+
+ http_parser_message_free(response);
+ response = http_parser_response_init();
+ http_parser_response_data(response, responseMessage, strlen(responseMessage));
+
+ printf("# 200 OK response\n");
+ 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);
+
+ http_parser_message_free(response);
+ response = http_parser_response_init();
+ http_parser_response_data(response, responseChunkedMessage, strlen(responseChunkedMessage));
+
+ printf("# 200 OK response (chunked)\n");
+ 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);
+
+ http_parser_message_free(response);
+ response = http_parser_response_init();
+ http_parser_response_data(response, responseNotFoundMessage, strlen(responseNotFoundMessage));
+
+ printf("# 404 Not Found response\n");
+ 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);
+
return err;
}