From 169f255eab6a1d9a419839efc7c85471ba64c47d Mon Sep 17 00:00:00 2001 From: Mateusz Lubecki Date: Sat, 19 Mar 2022 12:47:38 +0100 Subject: [PATCH] http client rx done callback --- STM32L476_ParaMETEO/makefile | 1 + STM32L476_ParaMETEO/sources.mk | 1 + system/include/gsm/http_client.h | 24 --- system/include/http_client/http_client.h | 55 ++++++ .../http_client/http_client_rx_callback.h | 15 ++ system/src/gsm/http_client.c | 107 ---------- system/src/http_client/http_client.c | 49 +++++ .../src/http_client/http_client_rx_callback.c | 186 ++++++++++++++++++ 8 files changed, 307 insertions(+), 131 deletions(-) delete mode 100644 system/include/gsm/http_client.h create mode 100644 system/include/http_client/http_client.h create mode 100644 system/include/http_client/http_client_rx_callback.h delete mode 100644 system/src/gsm/http_client.c create mode 100644 system/src/http_client/http_client.c create mode 100644 system/src/http_client/http_client_rx_callback.c diff --git a/STM32L476_ParaMETEO/makefile b/STM32L476_ParaMETEO/makefile index 7d20dcc..3cc1012 100644 --- a/STM32L476_ParaMETEO/makefile +++ b/STM32L476_ParaMETEO/makefile @@ -12,6 +12,7 @@ RM := rm -rf -include system/src/umb_master/subdir.mk -include system/src/stm32l4-hal-driver/subdir.mk -include system/src/modbus_rtu/subdir.mk +-include system/src/http_client/subdir.mk -include system/src/gsm/subdir.mk -include system/src/drivers/l4/subdir.mk -include system/src/drivers/subdir.mk diff --git a/STM32L476_ParaMETEO/sources.mk b/STM32L476_ParaMETEO/sources.mk index f63b31e..d689a50 100644 --- a/STM32L476_ParaMETEO/sources.mk +++ b/STM32L476_ParaMETEO/sources.mk @@ -35,6 +35,7 @@ system/src/diag \ system/src/drivers \ system/src/drivers/l4 \ system/src/gsm \ +system/src/http_client \ system/src/modbus_rtu \ system/src/stm32l4-hal-driver \ system/src/umb_master \ diff --git a/system/include/gsm/http_client.h b/system/include/gsm/http_client.h deleted file mode 100644 index 1289869..0000000 --- a/system/include/gsm/http_client.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * http_client.h - * - * Created on: Mar 10, 2022 - * Author: mateusz - */ - -#ifndef INCLUDE_GSM_HTTP_CLIENT_H_ -#define INCLUDE_GSM_HTTP_CLIENT_H_ - -#include "gsm/sim800c.h" - -#define HTTP_CLIENT_OK 0 -#define HTTP_CLIENT_TIMEOUT -100 -#define HTTP_CLIENT_TOO_LONG_RESPONSE -110 - -void http_client_init(gsm_sim800_state_t * state, srl_context_t * serial_context); -uint8_t http_client_async_get(char * url, uint8_t url_ln, uint16_t response_ln_limit); -uint8_t http_client_async_post(char * url, uint8_t url_ln, char * data_to_post, uint8_t data_ln); - -char * http_client_get_server_response(); -uint16_t http_client_get_latest_http_code(); - -#endif /* INCLUDE_GSM_HTTP_CLIENT_H_ */ diff --git a/system/include/http_client/http_client.h b/system/include/http_client/http_client.h new file mode 100644 index 0000000..794b7fa --- /dev/null +++ b/system/include/http_client/http_client.h @@ -0,0 +1,55 @@ +/* + * http_client.h + * + * Created on: Mar 10, 2022 + * Author: mateusz + */ + +#ifndef INCLUDE_GSM_HTTP_CLIENT_H_ +#define INCLUDE_GSM_HTTP_CLIENT_H_ + +/** + * This library supports only HTTP/1.1 and chunked Trasfer-Encoding as described here: + * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding + */ + +#include "gsm/sim800c.h" + +#define HTTP_CLIENT_OK 0 +#define HTTP_CLIENT_TIMEOUT -100 +#define HTTP_CLIENT_TOO_LONG_RESPONSE -110 +#define HTTP_CLIENT_HEADERS_OVERFLOW -120 +#define HTTP_CLIENT_DISCONNECTED -130 // this is set if remote server disconnected in unexpected moment + +#define HEADER_BUFFER_LN 64 + +/** + * HTTP code returned by the latest query. It is zeroed after each successful call to async + * function. This indicate that a request is currently in progress. Negative values means some + * non HTTP error, like communication timeout or response longer than expected + */ +extern int16_t http_client_http_code; + +/** + * Content lenght received from HTTP response headers or chunked encoding + */ +extern uint16_t http_client_content_lenght; + +/** + * Temporary buffer for processing + */ +extern char http_client_header_buffer[HEADER_BUFFER_LN]; + +/** + * Index used to walk through 'http_client_header_buffer' + */ +extern uint8_t http_client_header_index; + +void http_client_init(gsm_sim800_state_t * state, srl_context_t * serial_context); +uint8_t http_client_async_get(char * url, uint8_t url_ln, uint16_t response_ln_limit); +uint8_t http_client_async_post(char * url, uint8_t url_ln, char * data_to_post, uint8_t data_ln); + +char * http_client_get_server_response(); +uint16_t http_client_get_latest_http_code(); + +#endif /* INCLUDE_GSM_HTTP_CLIENT_H_ */ diff --git a/system/include/http_client/http_client_rx_callback.h b/system/include/http_client/http_client_rx_callback.h new file mode 100644 index 0000000..3e260f9 --- /dev/null +++ b/system/include/http_client/http_client_rx_callback.h @@ -0,0 +1,15 @@ +/* + * http_client_rx_callback.h + * + * Created on: Mar 19, 2022 + * Author: mateusz + */ + +#ifndef INCLUDE_HTTP_CLIENT_HTTP_CLIENT_RX_CALLBACK_H_ +#define INCLUDE_HTTP_CLIENT_HTTP_CLIENT_RX_CALLBACK_H_ + +#include + +uint8_t http_client_rx_done_callback(uint8_t current_data, const uint8_t * const rx_buffer, uint16_t rx_bytes_counter); + +#endif /* INCLUDE_HTTP_CLIENT_HTTP_CLIENT_RX_CALLBACK_H_ */ diff --git a/system/src/gsm/http_client.c b/system/src/gsm/http_client.c deleted file mode 100644 index 1f903e2..0000000 --- a/system/src/gsm/http_client.c +++ /dev/null @@ -1,107 +0,0 @@ -#include "gsm/http_client.h" - -#include - -static const char * DISCONNECTED = "CLOSED\0"; -static const char * CONTENT_LN = "Content-Length: \0"; - -#define WAIT_FOR_CONTENT_LENGHT 0xFFFFu - -/** - * Content lenght received from HTTP response headers - */ -uint16_t http_client_content_lenght = 0; - -/** - * HTTP code returned by the latest query. It is zeroed after each successful call to async - * function. This indicate that a request is currently in progress. Negative values means some - * non HTTP error, like communication timeout or response longer than expected - */ -int16_t http_client_http_code = 0; - -/** - * Temporary buffer for processing - */ -static char http_client_header_buffer[32]; - -/** - * Index used to walk through 'http_client_header_buffer' - */ -static uint8_t http_client_header_index = 0; - -/** - * This static function is used as a termination callback for serial I/O with GPRS module. - * It ends transmission in one of three cases - * 1. All data is received according to size specified by Content-Lenght field in response header - * 2. If user set 'response_ln_limit' to non zero value and 'response_ln_limit' characters have been received (it also set HTTP_CLIENT_TOO_LONG_RESPONSE) - * 3. If connection has been closed by remote server - * - */ -static uint8_t http_client_rx_done_callback(uint8_t current_data, const uint8_t * const rx_buffer, uint16_t rx_bytes_counter) { - - uint8_t out = 0; - - int compare_result = 0; - - // check if we wait for content lenght - if (http_client_content_lenght == WAIT_FOR_CONTENT_LENGHT) { - // copy current character to temporary buffer - http_client_header_buffer[http_client_header_index++] = (char)current_data; - - // check if - if (current_data == '\r') { - - } - } - - // if this is maybe the last character of 'CLOSED' - if ((char)current_data == 'D') { - // check 6 previous characters - compare_result = strncmp(DISCONNECTED, (const char *) (rx_buffer + rx_bytes_counter - 6), 6); - - // terminate reception if 'CLOSED' has been found. - if (compare_result == 0) { - out = 1; - } - } - - // if this is maybe an end of content lenght - else if (http_client_content_lenght == 0 && (char)current_data == 'h') { - // check 14 previous characters - compare_result = strncmp(CONTENT_LN, rx_buffer + rx_bytes_counter - 14, 14); - - // terminate reception if 'CLOSED' has been found. - if (compare_result == 0) { - // set waiting for - http_client_content_lenght = WAIT_FOR_CONTENT_LENGHT; - - // clear the buffer where header value will be stored - memset (http_client_header_buffer, 0x00, sizeof(http_client_header_buffer)); - } - } - - return out; -} - - -void http_client_init(gsm_sim800_state_t * state, srl_context_t * serial_context) { - -} - - -uint8_t http_client_async_get(char * url, uint8_t url_ln, uint16_t response_ln_limit) { - return 0; -} - -uint8_t http_client_async_post(char * url, uint8_t url_ln, char * data_to_post, uint8_t data_ln) { - return 0; -} - - -char * http_client_get_server_response() { - return 0; -} - -uint16_t http_client_get_latest_http_code() { - return http_client_http_code; -} diff --git a/system/src/http_client/http_client.c b/system/src/http_client/http_client.c new file mode 100644 index 0000000..af59552 --- /dev/null +++ b/system/src/http_client/http_client.c @@ -0,0 +1,49 @@ +#include "http_client/http_client.h" +#include "http_client/http_client_rx_callback.h" + +#include + +/** + * Content lenght received from HTTP response headers or chunked encoding + */ +uint16_t http_client_content_lenght = 0; + +/** + * HTTP code returned by the latest query. It is zeroed after each successful call to async + * function. This indicate that a request is currently in progress. Negative values means some + * non HTTP error, like communication timeout or response longer than expected + */ +int16_t http_client_http_code = 0; + +/** + * Temporary buffer for processing + */ +char http_client_header_buffer[HEADER_BUFFER_LN]; + +/** + * Index used to walk through 'http_client_header_buffer' + */ +uint8_t http_client_header_index = 0; + + +void http_client_init(gsm_sim800_state_t * state, srl_context_t * serial_context) { + +} + + +uint8_t http_client_async_get(char * url, uint8_t url_ln, uint16_t response_ln_limit) { + return 0; +} + +uint8_t http_client_async_post(char * url, uint8_t url_ln, char * data_to_post, uint8_t data_ln) { + return 0; +} + + +char * http_client_get_server_response() { + return 0; +} + +uint16_t http_client_get_latest_http_code() { + return http_client_http_code; +} diff --git a/system/src/http_client/http_client_rx_callback.c b/system/src/http_client/http_client_rx_callback.c new file mode 100644 index 0000000..aeb3e92 --- /dev/null +++ b/system/src/http_client/http_client_rx_callback.c @@ -0,0 +1,186 @@ +/* + * http_client_rx_callback.c + * + * Created on: Mar 19, 2022 + * Author: mateusz + */ + +#include "http_client/http_client_rx_callback.h" +#include "http_client/http_client.h" + +#include +#include + +static const char * DISCONNECTED = "CLOSED\0"; + +static const char * CONTENT_LN = "Content-Length: \0"; +#define CONTENT_LN_LN 16 +static const char * TRANSFER_ENCODING_CHUNKED = "Transfer-Encoding: chunked\0"; +#define TRANSFER_ENCODING_CHUNKED_LN 26 +static const char * HTTP_HEADER = "HTTP/\0"; +#define HTTP_HEADER_LN 5 +static const char * BLANK_NEWLINE = "\r\n\0"; + +#define WAIT_FOR_CONTENT_LENGHT 0xFFFFu +#define CONTENT_IN_CHUNKS 0xFFFEu + +typedef enum http_client_header_field { + + HEADER_HTTP, + HEADER_CONTENT_LN, + HEADER_TRANSFER_ENCODING_CHUNKED, + HEADER_END, + HEADER_UNKNOWN +} http_client_header_field_t; + +// set to one if we are still parsing HTTP response header +static uint8_t http_client_response_header_processing = 1; + +/** + * This function is responsible for checking what HTTP header has been received + */ +static http_client_header_field_t http_client_check_what_field_it_is(char * buffer, uint16_t buffer_ln) { + + http_client_header_field_t out = HEADER_UNKNOWN; + + if (strncmp(CONTENT_LN, buffer, CONTENT_LN_LN) == 0) { + out = HEADER_CONTENT_LN; + } + else if (strncmp(TRANSFER_ENCODING_CHUNKED, buffer, TRANSFER_ENCODING_CHUNKED_LN) == 0) { + out = HEADER_TRANSFER_ENCODING_CHUNKED; + } + else if (strncmp(HTTP_HEADER, buffer, HTTP_HEADER_LN) == 0) { + out = HEADER_HTTP; + } + else if (strncmp(BLANK_NEWLINE, buffer, 2) == 0) { + out = HEADER_END; + } + + return out; +} + +/** + * This function is used as a termination callback for serial I/O with GPRS module. + * It ends transmission in one of three cases + * 1. All data is received according to size specified by Content-Lenght field in response header + * 2. If user set 'response_ln_limit' to non zero value and 'response_ln_limit' characters have been received (it also set HTTP_CLIENT_TOO_LONG_RESPONSE) + * 3. If connection has been closed by remote server + * + * It assumes that 'http_client_header_buffer' is zeroed + * + */ +uint8_t http_client_rx_done_callback(uint8_t current_data, const uint8_t * const rx_buffer, uint16_t rx_bytes_counter) { + + uint8_t out = 0; + + int compare_result = 0; + + // local buffer used to fetch HTTP error code or similar short things + char local_buffer[9]; + + // if this is maybe the last character of 'CLOSED' + if ((char)current_data == 'D') { + // check 6 previous characters + compare_result = strncmp(DISCONNECTED, (const char *) (rx_buffer + rx_bytes_counter - 6), 6); + + // terminate reception if 'CLOSED' has been found. + if (compare_result == 0) { + out = 1; + + http_client_http_code = HTTP_CLIENT_DISCONNECTED; + } + } + + // check if we wait for content length + if (http_client_response_header_processing == 1) { + // copy current character to temporary buffer + http_client_header_buffer[http_client_header_index++] = (char)current_data; + + // we have an overflow during processing HTTP headers + if (http_client_header_index >= HEADER_BUFFER_LN) { + out = 1; + + // set an error code also + http_client_http_code = HTTP_CLIENT_HEADERS_OVERFLOW; + } + else { + // check if this is newline + if (current_data == '\n') { + // if yes parse the content of HTTP header buffer + http_client_header_field_t field = http_client_check_what_field_it_is(http_client_header_buffer, HEADER_BUFFER_LN); + + memset(local_buffer, 0x0, 0x9); + + switch (field) { + + // fetch the HTTP return code + case HEADER_HTTP: { + // copy last 6 characters of header >> 0000 48 54 54 50 2f 31 2e 31 20 32 30 30 20 0d 0a HTTP/1.1 200 .. << + memcpy(local_buffer, http_client_header_buffer + http_client_header_index - 6, 6); + + // convert to int + http_client_http_code = atoi(local_buffer); + break; + } + + case HEADER_CONTENT_LN: { + // content ln may vary, so we need to copy data starting from the begining not the end + strncpy(local_buffer, http_client_header_buffer + http_client_header_index + 16, 6); + + // convert to integer value + http_client_content_lenght = atoi(local_buffer); + + break; + } + + case HEADER_TRANSFER_ENCODING_CHUNKED: { + /** + * 0000 54 72 61 6e 73 66 65 72 2d 45 6e 63 6f 64 69 6e Transfer-Encodin + * 0010 67 3a 20 63 68 75 6e 6b 65 64 0d 0a g: chunked.. + * + */ + + // set that the content will be sent in chunks + http_client_content_lenght = CONTENT_IN_CHUNKS; + + // this is not the end of the header as such as neighter 'Transfer-Encoding' nor + // 'Content-Lenght' need to occur as the last thing in response + break; + } + + case HEADER_END: { + http_client_response_header_processing = 0; + + break; + } + + // not all headers needs to be processed + default: + break; + } + + // clear temporary buffer + memset (http_client_header_buffer, 0x00, HEADER_BUFFER_LN); + http_client_header_index = 0; + } + } + + } + else { + // we know the lenght of response or we are waiting for first chunk + if (http_client_content_lenght == CONTENT_IN_CHUNKS) { + + // save content data until we get chunk size + http_client_header_buffer[http_client_header_index++] = (char)current_data; + + // if this is a first newline + if ((char)current_data == '\n') { + // everything what is now in 'http_client_header_buffer' is the size of chunk in hex!! + http_client_content_lenght = strtol(http_client_header_buffer, 0, 16); + } + } + } + + + return out; +}