From 55c465fab94aaf3d9fb51a5c5f97180e0d0965c3 Mon Sep 17 00:00:00 2001 From: Arthur Lu Date: Wed, 4 Sep 2024 13:14:59 -0700 Subject: [PATCH] improve api response --- http_parser.h | 94 ++++++++++++++++++++++++++++++++++++++------------- http_serv.h | 90 +++++++++++++++++++++++++----------------------- 2 files changed, 119 insertions(+), 65 deletions(-) diff --git a/http_parser.h b/http_parser.h index 0f05d6f..2b6986c 100644 --- a/http_parser.h +++ b/http_parser.h @@ -3,17 +3,18 @@ #include "llhttp.h" +#define HTTP_RESPONSE_HEADER_TEMPLATE "HTTP/1.1 %d %s\nContent-Length: %d\nContent-Type: %s; charset=utf-8\nConnection: close\n\n" + typedef struct HTTP_REQUEST_DATA_T_ { - char protocol[8]; - char method[8]; + llhttp_method_t method; char url[128]; char body[128]; } HTTP_REQUEST_DATA_T; -typedef struct HTTP_REQUEST_PARSER_WRAPPER_T_ { +typedef struct HTTP_REQUEST_PARSER_T_ { llhttp_t parser; llhttp_settings_t settings; -} HTTP_REQUEST_PARSER_WRAPPER_T; +} HTTP_REQUEST_PARSER_T; int request_parser_on_url(llhttp_t * parser, const char * start, size_t length) { HTTP_REQUEST_DATA_T * http_request = parser->data; @@ -27,45 +28,36 @@ int request_parser_on_body(llhttp_t * parser, const char * start, size_t length) return 0; } -int parse_http_request (HTTP_REQUEST_PARSER_WRAPPER_T * wrapper, char * content, size_t len) { +int parse_http_request (HTTP_REQUEST_PARSER_T * wrapper, char * content, size_t len) { llhttp_t parser = wrapper->parser; llhttp_errno_t err = llhttp_execute(&parser, content, len); HTTP_REQUEST_DATA_T * http_request = parser.data; - uint8_t protocol_major = llhttp_get_http_major(&parser); - uint8_t protocol_minor = llhttp_get_http_minor(&parser); - sprintf(http_request->protocol, "%d.%d", protocol_major, protocol_minor); - - uint8_t method = llhttp_get_method(&parser); - const char * method_name = llhttp_method_name(method); - strcpy(http_request->method, method_name); + llhttp_method_t method = llhttp_get_method(&parser); + http_request->method = method; return 0; } -char * get_protocol (HTTP_REQUEST_PARSER_WRAPPER_T * wrapper) { - HTTP_REQUEST_DATA_T * http_request = wrapper->parser.data; - return http_request->protocol; -} - -char * get_method (HTTP_REQUEST_PARSER_WRAPPER_T * wrapper) { +llhttp_method_t get_method (HTTP_REQUEST_PARSER_T * wrapper) { HTTP_REQUEST_DATA_T * http_request = wrapper->parser.data; return http_request->method; } -char * get_url (HTTP_REQUEST_PARSER_WRAPPER_T * wrapper) { +char * get_url (HTTP_REQUEST_PARSER_T * wrapper) { HTTP_REQUEST_DATA_T * http_request = wrapper->parser.data; return http_request->url; } -char * get_body (HTTP_REQUEST_PARSER_WRAPPER_T * wrapper) { +char * get_body (HTTP_REQUEST_PARSER_T * wrapper) { HTTP_REQUEST_DATA_T * http_request = wrapper->parser.data; return http_request->body; } -HTTP_REQUEST_PARSER_WRAPPER_T * new_request_parser () { - HTTP_REQUEST_PARSER_WRAPPER_T * wrapper = calloc(1, sizeof(HTTP_REQUEST_PARSER_WRAPPER_T)); +HTTP_REQUEST_PARSER_T * new_request_parser () { + // create new response parser wrapper + HTTP_REQUEST_PARSER_T * wrapper = calloc(1, sizeof(HTTP_REQUEST_PARSER_T)); // create a new request data struct HTTP_REQUEST_DATA_T * http_request = calloc(1, sizeof(HTTP_REQUEST_DATA_T)); @@ -82,11 +74,67 @@ HTTP_REQUEST_PARSER_WRAPPER_T * new_request_parser () { return wrapper; } -void delete_request_parser (HTTP_REQUEST_PARSER_WRAPPER_T * wrapper) { +void delete_request_parser (HTTP_REQUEST_PARSER_T * wrapper) { free(wrapper->parser.data); free(wrapper); } +typedef struct HTTP_RESPONSE_DATA_T_ { + llhttp_status_t status; + char type[32]; + char body[128]; +} HTTP_RESPONSE_DATA_T; + +typedef struct HTTP_RESPONSE_COMPOSER_T_ { + HTTP_RESPONSE_DATA_T * response_data; + char header[1024]; + char body[128]; + uint32_t header_length; + uint32_t content_length; +} HTTP_RESPONSE_COMPOSER_T; + +void set_status (HTTP_RESPONSE_COMPOSER_T * composer, llhttp_status_t status) { + HTTP_RESPONSE_DATA_T * response_data = composer->response_data; + response_data->status = status; +} + +void set_type (HTTP_RESPONSE_COMPOSER_T * composer, char * type) { + HTTP_RESPONSE_DATA_T * response_data = composer->response_data; + strncpy(response_data->type, type, 128); +} + +void set_body (HTTP_RESPONSE_COMPOSER_T * composer, char * body) { + HTTP_RESPONSE_DATA_T * response_data = composer->response_data; + strncpy(response_data->body, body, 128); +} + +void compose_http_response (HTTP_RESPONSE_COMPOSER_T * composer) { + HTTP_RESPONSE_DATA_T * response_data = composer->response_data; + + composer->content_length = snprintf(composer->body, sizeof(composer->body), "%s", response_data->body); + + const char * status_name = llhttp_status_name(response_data->status); + + composer->header_length = snprintf(composer->header, sizeof(composer->header), HTTP_RESPONSE_HEADER_TEMPLATE, response_data->status, status_name, composer->content_length, response_data->type); +} + +HTTP_RESPONSE_COMPOSER_T * new_response_composer () { + // create new response composer wrapper + HTTP_RESPONSE_COMPOSER_T * wrapper = calloc(1, sizeof(HTTP_RESPONSE_COMPOSER_T)); + + // create a new response data struct + HTTP_RESPONSE_DATA_T * http_response = calloc(1, sizeof(HTTP_RESPONSE_DATA_T)); + + wrapper->response_data = http_response; + + return wrapper; +} + +void delete_response_composer (HTTP_RESPONSE_COMPOSER_T * composer) { + free(composer->response_data); + free(composer); +} + int http_parser_init () { return 0; } diff --git a/http_serv.h b/http_serv.h index d371ae8..f408e35 100644 --- a/http_serv.h +++ b/http_serv.h @@ -7,9 +7,6 @@ #define HTTP_PORT 80 #define POLL_TIME_S 5 -#define HTTP_GET "GET" -#define HTTP_POST "POST" -#define HTTP_RESPONSE_HEADER "HTTP/1.1 %d OK\nContent-Length: %d\nContent-Type: application/json; charset=utf-8\nConnection: close\n\n" extern CURRENT_STATE_T current_state; @@ -22,11 +19,8 @@ typedef struct TCP_SERVER_T_ { typedef struct TCP_CONNECT_STATE_T_ { struct tcp_pcb * pcb; int sent_len; - char headers[1024]; - char result[128]; - HTTP_REQUEST_PARSER_WRAPPER_T * request_parser; - int header_len; - int result_len; + HTTP_REQUEST_PARSER_T * request_parser; + HTTP_RESPONSE_COMPOSER_T * response_composer; ip_addr_t * gw; } TCP_CONNECT_STATE_T; @@ -48,6 +42,7 @@ static err_t tcp_close_client_connection(TCP_CONNECT_STATE_T * con_state, struct } if (con_state) { delete_request_parser(con_state->request_parser); + delete_response_composer(con_state->response_composer); free(con_state); } DEBUG_printf("[HTTP] [OK ] Finished and closed connection to client\n"); @@ -67,8 +62,8 @@ static err_t tcp_server_sent(void * arg, struct tcp_pcb * pcb, u16_t len) { TCP_CONNECT_STATE_T * con_state = (TCP_CONNECT_STATE_T *)arg; DEBUG_printf("[HTTP] [OK ] Sent %u\n", len); con_state->sent_len += len; - if (con_state->sent_len >= con_state->header_len + con_state->result_len) { - DEBUG_printf("[HTTP] [OK ] Send done\n"); + if (con_state->sent_len >= con_state->response_composer->header_length + con_state->response_composer->content_length) { + DEBUG_printf("[HTTP] [OK ] Sent done\n"); return tcp_close_client_connection(con_state, pcb, ERR_OK); } return ERR_OK; @@ -76,26 +71,26 @@ static err_t tcp_server_sent(void * arg, struct tcp_pcb * pcb, u16_t len) { int send_response (TCP_CONNECT_STATE_T * con_state, struct tcp_pcb * pcb) { // Check result buffer size - if (con_state->result_len > sizeof(con_state->result) - 1) { - DEBUG_printf("[HTTP] [ERR] Too much result data %d\n", con_state->result_len); + if (con_state->response_composer->content_length > sizeof(con_state->response_composer->body) - 1) { + DEBUG_printf("[HTTP] [ERR] Too much result data %d\n", con_state->response_composer->content_length); return tcp_close_client_connection(con_state, pcb, ERR_CLSD); } // Check header buffer size - if (con_state->header_len > sizeof(con_state->headers) - 1) { - DEBUG_printf("[HTTP] [ERR] Too much header data %d\n", con_state->header_len); + if (con_state->response_composer->header_length > sizeof(con_state->response_composer->header) - 1) { + DEBUG_printf("[HTTP] [ERR] Too much header data %d\n", con_state->response_composer->header_length); return tcp_close_client_connection(con_state, pcb, ERR_CLSD); } // Send the headers to the client con_state->sent_len = 0; - err_t err = tcp_write(pcb, con_state->headers, con_state->header_len, 0); + err_t err = tcp_write(pcb, con_state->response_composer->header, con_state->response_composer->header_length, 0); if (err != ERR_OK) { DEBUG_printf("[HTTP] [ERR] Failed to write header data %d\n", err); return tcp_close_client_connection(con_state, pcb, err); } // Send the body to the client - if (con_state->result_len) { - err = tcp_write(pcb, con_state->result, con_state->result_len, 0); + if (con_state->response_composer->content_length) { + err = tcp_write(pcb, con_state->response_composer->body, con_state->response_composer->content_length, 0); if (err != ERR_OK) { DEBUG_printf("[HTTP] [ERR] Failed to write result data %d\n", err); return tcp_close_client_connection(con_state, pcb, err); @@ -104,32 +99,47 @@ int send_response (TCP_CONNECT_STATE_T * con_state, struct tcp_pcb * pcb) { return 0; } -static int handle_status_get (const char * request, const char * params, const char * body, char * result, size_t max_result_len) { - return snprintf(result, max_result_len, "{volt: %f, temp: %f, power: %d}", current_state.voltage, current_state.tempC, current_state.power_state); +void handle_status_get (const char * request, const char * params, char * body, HTTP_RESPONSE_COMPOSER_T * response) { + set_status(response, 200); + set_type(response, "application/json"); + char response_body[128]; + snprintf(response_body, 128, "{volt: %f, temp: %f, power: %d}", current_state.voltage, current_state.tempC, current_state.power_state); + set_body(response, response_body); } -static int handle_power_post (const char * request, const char * params, const char * body, char * result, size_t max_result_len) { +void handle_power_post (const char * request, const char * params, char * body, HTTP_RESPONSE_COMPOSER_T * response) { + set_type(response, "application/json"); if (strstr(body, "requested_state")) { int requested_power_state_int; int led_param = sscanf(body, "requested_state=%d", &requested_power_state_int); if (led_param) { if (requested_power_state_int == 0 || requested_power_state_int == 1) { + set_status(response, 200); bmc_power_handler((bool) requested_power_state_int); - return snprintf(result, max_result_len, "{}"); + set_body(response, "{}"); } else { - return snprintf(result, max_result_len, "{error: true, description: \"invalid requested state, must be 0 or 1\"}"); + set_status(response, 400); + set_body(response, "{error: true, description: \"invalid requested state, must be 0 or 1\"}"); } } else { - return snprintf(result, max_result_len, "{error: true, description: \"invalid requested state, must be 0 or 1\"}"); + set_status(response, 400); + set_body(response, "{error: true, description: \"invalid requested state, must be 0 or 1\"}"); } } else { - return snprintf(result, max_result_len, "{error: true, description: \"missing required parameter requested_state\"}"); + set_status(response, 400); + set_body(response, "{error: true, description: \"missing required parameter requested_state\"}"); } } +void handle_404_not_found (const char * request, const char * params, char * body, HTTP_RESPONSE_COMPOSER_T * response) { + set_status(response, 404); + set_type(response, "application/json"); + set_body(response, ""); +} + err_t tcp_server_recv(void * arg, struct tcp_pcb * pcb, struct pbuf * p, err_t err) { TCP_CONNECT_STATE_T * con_state = (TCP_CONNECT_STATE_T *)arg; if (!p) { @@ -138,42 +148,38 @@ err_t tcp_server_recv(void * arg, struct tcp_pcb * pcb, struct pbuf * p, err_t e } assert(con_state && con_state->pcb == pcb); if (p->tot_len > 0) { - DEBUG_printf("[HTTP] [OK ] Server recieved %d err %d\n", p->tot_len, err); + DEBUG_printf("[HTTP] [OK ] Recieved %d err: %d\n", p->tot_len, err); char content[2048] = {0}; pbuf_copy_partial(p, &content, 2048 - 1, 0); con_state->request_parser = new_request_parser(); parse_http_request(con_state->request_parser, content, strlen(content)); - char * protocol = get_protocol(con_state->request_parser); - char * method = get_method(con_state->request_parser); + llhttp_method_t method = get_method(con_state->request_parser); char * url = get_url(con_state->request_parser); char * body = get_body(con_state->request_parser); - // print request - DEBUG_printf("[HTTP] [OK ] Request: %s %s %s\n", method, url, body); + // debug print request + const char * method_name = llhttp_method_name(method); + DEBUG_printf("[HTTP] [OK ] Request: %s %s %s\n", method_name, url, body); - int response_code; + con_state->response_composer = new_response_composer(); // parse request depending on method and request - if (strncmp(method, HTTP_GET, sizeof(HTTP_GET) - 1) == 0 && strncmp(url, "/status", sizeof("/status") - 1) == 0) { - con_state->result_len = handle_status_get(url, NULL, body, con_state->result, sizeof(con_state->result)); - response_code = 200; - con_state->header_len = snprintf(con_state->headers, sizeof(con_state->headers), HTTP_RESPONSE_HEADER, response_code, con_state->result_len); + if (method == HTTP_GET && strncmp(url, "/status", sizeof("/status") - 1) == 0) { + handle_status_get(url, NULL, body, con_state->response_composer); } - else if (strncmp(method, HTTP_POST, sizeof(HTTP_POST) - 1) == 0 && strncmp(url, "/power", sizeof("/power") - 1) == 0) { - con_state->result_len = handle_power_post(url, NULL, body, con_state->result, sizeof(con_state->result)); - response_code = 200; - con_state->header_len = snprintf(con_state->headers, sizeof(con_state->headers), HTTP_RESPONSE_HEADER, response_code, con_state->result_len); + else if (method == HTTP_POST && strncmp(url, "/power", sizeof("/power") - 1) == 0) { + handle_power_post(url, NULL, body, con_state->response_composer); } else { // if not a registered path, return HTTP 404 - con_state->result_len = 0; - response_code = 404; - con_state->header_len = snprintf(con_state->headers, sizeof(con_state->headers), HTTP_RESPONSE_HEADER, response_code, con_state->result_len); + handle_404_not_found(url, NULL, body, con_state->response_composer); } + compose_http_response(con_state->response_composer); + // print result - DEBUG_printf("[HTTP] [OK ] Result: %d %s\n", response_code, con_state->result); + DEBUG_printf("[HTTP] [OK ] Result: %s %s\n", con_state->response_composer->header, con_state->response_composer->body); int err; if (err = send_response(con_state, pcb)) {