diff options
-rw-r--r-- | minion/src/gymnasiearbete.cpp | 42 | ||||
-rw-r--r-- | minion/src/http/request.cpp | 107 | ||||
-rw-r--r-- | minion/src/http/request.hpp | 65 | ||||
-rw-r--r-- | minion/src/util.cpp | 9 | ||||
-rw-r--r-- | minion/src/util.hpp | 16 | ||||
-rw-r--r-- | minion/src/wifi_module.cpp | 162 | ||||
-rw-r--r-- | minion/src/wifi_module.hpp | 30 |
7 files changed, 365 insertions, 66 deletions
diff --git a/minion/src/gymnasiearbete.cpp b/minion/src/gymnasiearbete.cpp index 927cd1c..30e5605 100644 --- a/minion/src/gymnasiearbete.cpp +++ b/minion/src/gymnasiearbete.cpp @@ -1,3 +1,4 @@ +#include "http/request.hpp" #include "secrets.hpp" #include "wifi_module.hpp" @@ -9,13 +10,11 @@ constexpr auto HTTP_PORT = 80U; constexpr auto BAUDRATE = 9600U; -constexpr auto NETWORK_MODULE_RX_PIN = 3; -constexpr auto NETWORK_MODULE_TX_PIN = 2; +constexpr auto NETWORK_MODULE_RX_PIN = 13U; +constexpr auto NETWORK_MODULE_TX_PIN = 11U; constexpr auto SECOND_IN_MILLIS = 1000U; -constexpr auto MAX_HTTP_REQUEST_SIZE = 200U; - auto wifi_module = WiFiModule({ NETWORK_MODULE_RX_PIN, NETWORK_MODULE_TX_PIN }); void setup() @@ -74,22 +73,35 @@ void setup() void loop() { - if (wifi_module.has_incoming_request()) + const auto *request = wifi_module.read_incoming_request(); + + if (request == nullptr) { - char raw_request[MAX_HTTP_REQUEST_SIZE] = ""; + return; + } - const auto connection_id = wifi_module.read_incoming_request(raw_request); + const auto connection_id = request->connection_id(); - Serial.print("Connection ID: "); - Serial.println(static_cast<unsigned int>(connection_id)); + Serial.print("Connection ID: "); + Serial.println(static_cast<unsigned int>(connection_id)); - Serial.print("\nRaw request: "); - Serial.println(raw_request); + Serial.print("Request method: "); + Serial.println(http_request_method_strs[static_cast<size_t>(request->method())]); - wifi_module.send(connection_id, "lmao!"); + Serial.print("Request HTTP version: "); + Serial.println(request->http_version()); - wifi_module.close_connection(connection_id); + Serial.print("Request path: "); + Serial.println(request->path()); - delay(SECOND_IN_MILLIS); - } + Serial.print("\nData: "); + Serial.println(request->data()); + + wifi_module.send(connection_id, "lmao!"); + + wifi_module.close_connection(connection_id); + + delete request; + + delay(SECOND_IN_MILLIS); } diff --git a/minion/src/http/request.cpp b/minion/src/http/request.cpp new file mode 100644 index 0000000..9a35d37 --- /dev/null +++ b/minion/src/http/request.cpp @@ -0,0 +1,107 @@ +#include "request.hpp" + +#include "util.hpp" + +#include <string.h> + +auto str_to_http_request_method(const char *http_request_method_str) -> HTTPRequestMethod +{ + if (util::streq(http_request_method_str, "GET")) + { + return HTTPRequestMethod::GET; + } + + if (util::streq(http_request_method_str, "POST")) + { + return HTTPRequestMethod::POST; + } + + if (util::streq(http_request_method_str, "PUT")) + { + return HTTPRequestMethod::PUT; + } + + if (util::streq(http_request_method_str, "HEAD")) + { + return HTTPRequestMethod::HEAD; + } + + if (util::streq(http_request_method_str, "DELETE")) + { + return HTTPRequestMethod::DELETE; + } + + if (util::streq(http_request_method_str, "CONNECT")) + { + return HTTPRequestMethod::CONNECT; + } + + if (util::streq(http_request_method_str, "OPTIONS")) + { + return HTTPRequestMethod::OPTIONS; + } + + if (util::streq(http_request_method_str, "TRACE")) + { + return HTTPRequestMethod::TRACE; + } + + if (util::streq(http_request_method_str, "PATCH")) + { + return HTTPRequestMethod::PATCH; + } + + return HTTPRequestMethod::GET; +} + +HTTPRequest::HTTPRequest( + size_t connection_id, + HTTPRequestMethod method, + char *http_version, // NOLINT(bugprone-easily-swappable-parameters) + char *path, + int data_length, + char *data +) noexcept + : _connection_id(connection_id), + _method(method), + _http_version(http_version), + _path(path), + _data_length(data_length), + _data(data) +{ +} + +HTTPRequest::~HTTPRequest() noexcept +{ + free(_data); +} + +auto HTTPRequest::connection_id() const noexcept -> size_t +{ + return _connection_id; +} + +auto HTTPRequest::method() const noexcept -> HTTPRequestMethod +{ + return _method; +} + +auto HTTPRequest::http_version() const noexcept -> const char * +{ + return _http_version; +} + +auto HTTPRequest::path() const noexcept -> const char * +{ + return _path; +} + +auto HTTPRequest::data_length() const noexcept -> int +{ + return _data_length; +} + +auto HTTPRequest::data() const noexcept -> const char * +{ + return _data; +} diff --git a/minion/src/http/request.hpp b/minion/src/http/request.hpp new file mode 100644 index 0000000..85af722 --- /dev/null +++ b/minion/src/http/request.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include <stddef.h> + +enum HTTPRequestMethod +{ + GET, + POST, + PUT, + HEAD, + DELETE, + CONNECT, + OPTIONS, + TRACE, + PATCH +}; + +constexpr const char *http_request_method_strs[] = { "GET", "POST", "PUT", + "HEAD", "DELETE", "CONNECT", + "OPTIONS", "TRACE", "PATCH" }; + +auto str_to_http_request_method(const char *http_request_method_str) -> HTTPRequestMethod; + +class HTTPRequest +{ +public: + explicit HTTPRequest( + size_t connection_id, + HTTPRequestMethod method, + char *http_version, + char *path, + int data_length, + char *data + ) noexcept; + + HTTPRequest(const HTTPRequest &other) noexcept = delete; + + HTTPRequest(HTTPRequest &&other) noexcept = delete; + + ~HTTPRequest() noexcept; + + auto connection_id() const noexcept -> size_t; + + auto method() const noexcept -> HTTPRequestMethod; + + auto http_version() const noexcept -> const char *; + + auto path() const noexcept -> const char *; + + auto data_length() const noexcept -> int; + + auto data() const noexcept -> const char *; + + auto operator=(const HTTPRequest &other) noexcept -> HTTPRequest & = delete; + + auto operator=(HTTPRequest &&other) noexcept -> HTTPRequest & = delete; + +private: + const size_t _connection_id; + const HTTPRequestMethod _method; + char *_http_version; + char *_path; + const int _data_length; + char *_data; +}; diff --git a/minion/src/util.cpp b/minion/src/util.cpp index f587781..540a20c 100644 --- a/minion/src/util.cpp +++ b/minion/src/util.cpp @@ -3,7 +3,7 @@ namespace util { -bool str_ends_with(const char *str, const char *other_str) noexcept +auto str_ends_with(const char *str, const char *other_str) noexcept -> bool { if (str == nullptr || other_str == nullptr) { @@ -25,11 +25,16 @@ void substr(const char *str, const char *end, char *dest) noexcept { auto *dest_head = dest; - for (const char *char_ptr = str; char_ptr + 1 != end; ++char_ptr) + for (const char *char_ptr = str; char_ptr != end; ++char_ptr) { *dest_head = *char_ptr; dest_head++; } } +auto streq(const char *str_one, const char *str_two) noexcept -> bool +{ + return strcmp(str_one, str_two) == 0; +} + } // namespace util diff --git a/minion/src/util.hpp b/minion/src/util.hpp index d6f7f1c..4cc3967 100644 --- a/minion/src/util.hpp +++ b/minion/src/util.hpp @@ -8,12 +8,12 @@ namespace util { template <typename Type> -Type *malloc(size_t size) noexcept +auto malloc(size_t size) noexcept -> Type * { return static_cast<Type *>(::malloc(size)); } -bool str_ends_with(const char *str, const char *other_str) noexcept; +auto str_ends_with(const char *str, const char *other_str) noexcept -> bool; /** * Extracts a portion of a string. @@ -24,4 +24,16 @@ bool str_ends_with(const char *str, const char *other_str) noexcept; */ void substr(const char *str, const char *end, char *dest) noexcept; +/** + * Compares two strings. + * + * Wrapper function for strcmp. + * + * @param str_one The first string. + * @param str_two The second string. + * + * @returns Whether or not the two string are the same. + */ +auto streq(const char *str_one, const char *str_two) noexcept -> bool; + } // namespace util diff --git a/minion/src/wifi_module.cpp b/minion/src/wifi_module.cpp index 8b04b70..065bcdf 100644 --- a/minion/src/wifi_module.cpp +++ b/minion/src/wifi_module.cpp @@ -146,7 +146,7 @@ void WiFiModule::create_tcp_server(size_t port) noexcept char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = ""; - _read(TIMEOUT_LONG, response); + _read(TIMEOUT_MEDIUM, response); // Serial.println(response); } @@ -191,6 +191,8 @@ auto WiFiModule::get_local_ip(char *local_ip_out) noexcept -> const char * return local_ip_out; } + strcpy(buf, ""); + const auto response_status = _read(10000U, buf); if (response_status != ResponseStatus::OK) @@ -206,7 +208,7 @@ auto WiFiModule::get_local_ip(char *local_ip_out) noexcept -> const char * return local_ip_out; } - const auto *local_ip_end = strstr(buf, "\r\n+CIFSR:STAMAC"); + const auto local_ip_end = strstr(buf, "\r\n+CIFSR:STAMAC"); if (local_ip_end == nullptr) { @@ -224,15 +226,15 @@ auto WiFiModule::get_local_ip(char *local_ip_out) noexcept -> const char * return local_ip_out; } -auto WiFiModule::has_incoming_request() noexcept -> bool +auto WiFiModule::read_incoming_request() noexcept -> HTTPRequest * { char request_prefix[] = "+IPD,"; - return get_available() != 0 && _serial.find(request_prefix); -} + if (get_available() == 0 || !_serial.find(request_prefix)) + { + return nullptr; + } -auto WiFiModule::read_incoming_request(char *raw_request_out) noexcept -> size_t -{ const auto min_available_bytes = 5; // Wait for the data buffer a bit @@ -240,41 +242,50 @@ auto WiFiModule::read_incoming_request(char *raw_request_out) noexcept -> size_t { } - const auto connection_id = _serial.read() - ASCII_TO_CHAR; + const auto connection_id = _read_connection_id(); - // Skip the comma - _serial.read(); + auto request_data_length = _read_request_data_length(); - char data_length_buf[16] = { '\0' }; + const auto request_method = _read_request_method(request_data_length); - _read_buffer(&data_length_buf[0], sizeof(data_length_buf) - 1, ':'); + // Read request path + char request_path[REQUEST_PATH_MAX_LENGTH] = ""; + _read_to(&request_path[0], sizeof(request_path) - 1U, ' ', TIMEOUT_MEDIUM); - auto data_length = atoi(data_length_buf); + request_data_length -= strlen(request_path) + 1U; - Serial.print("Data length: "); - Serial.println(data_length); + // Read request HTTP version + char http_version[REQUEST_HTTP_VERSION_MAX_LENGTH] = ""; + _read_to(&http_version[0], sizeof(http_version) - 1U, '\r', TIMEOUT_MEDIUM); - auto read_bytes = 0; - const auto start_time = millis(); + request_data_length -= strlen(http_version) + 1U; - while (read_bytes != data_length && (start_time + 10000) > millis()) + // Skip the newline + while (get_available() == 0) { - if (get_available() == 0) - { - continue; - } - - char character = _read_byte(); + } + _serial.read(); + request_data_length -= 1U; - strncat(raw_request_out, &character, 1U); + auto request_data = util::malloc<char>(request_data_length + 1U); - read_bytes++; + if (request_data == nullptr) + { + return nullptr; } - // Skip the comma at the beginning - raw_request_out++; + strcpy(request_data, ""); - return connection_id; + _read_bytes(request_data, request_data_length, TIMEOUT_LONG); + + return new HTTPRequest( + connection_id, + request_method, + http_version, + request_path, + request_data_length, + request_data + ); } auto WiFiModule::close_connection(size_t connection_id) noexcept -> bool @@ -340,13 +351,13 @@ auto WiFiModule::send(size_t connection_id, const char *data) noexcept -> bool char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = ""; - _read(TIMEOUT_LONG, response); + _read(TIMEOUT_MEDIUM, response); _serial.print(data); strcpy(response, ""); - _read(TIMEOUT_LONG, response); + _read(TIMEOUT_MEDIUM, response); return true; } @@ -374,6 +385,43 @@ auto WiFiModule::_send_serial(const char *command) noexcept -> bool return true; } +size_t WiFiModule::_read_connection_id() noexcept +{ + const auto connection_id = _serial.read() - ASCII_TO_CHAR; + + // Skip the comma + _serial.read(); + + return connection_id; +} + +size_t WiFiModule::_read_request_data_length() noexcept +{ + char data_length_buf[REQUEST_DATA_LENGTH_BUF_SIZE] = ""; + + _read_to(&data_length_buf[0], sizeof(data_length_buf) - 1U, ':', TIMEOUT_SHORT); + + return static_cast<size_t>(strtoul(data_length_buf, nullptr, 10)); +} + +HTTPRequestMethod WiFiModule::_read_request_method(size_t &request_data_length) noexcept +{ + char request_method_buf[REQUEST_METHOD_STR_MAX_LENGTH] = ""; + + _read_to( + &request_method_buf[0], + sizeof(request_method_buf) - 1U, + ' ', + TIMEOUT_MEDIUM + ); + + const auto request_method = str_to_http_request_method(request_method_buf); + + request_data_length -= strlen(request_method_buf) + 1U; + + return request_method; +} + auto WiFiModule::_read(uint64_t timeout, char *response_out) noexcept -> ResponseStatus { const auto start_time = millis(); @@ -381,8 +429,6 @@ auto WiFiModule::_read(uint64_t timeout, char *response_out) noexcept -> Respons bool has_end = false; auto status = ResponseStatus::TIMEOUT; - // auto *response_out_head = response_out; - while (!has_end && (start_time + timeout) > millis()) { while (_serial.available() != 0) @@ -391,9 +437,6 @@ auto WiFiModule::_read(uint64_t timeout, char *response_out) noexcept -> Respons strncat(response_out, &character, 1U); - // *response_out_head = character; - // response_out_head++; - if (util::str_ends_with(response_out, "OK")) { status = ResponseStatus::OK; @@ -417,16 +460,29 @@ auto WiFiModule::_read(uint64_t timeout, char *response_out) noexcept -> Respons return status; } -void WiFiModule::_read_buffer(char *buffer_out, size_t length, char stop_char) noexcept +void WiFiModule::_read_to( + char *buffer_out, + size_t length, // NOLINT(bugprone-easily-swappable-parameters) + char stop_char, + uint64_t timeout +) noexcept { - byte position = 0; + auto position = 0U; + const auto start_time = millis(); - while (get_available() != 0 && position < length) + while (position < length && (start_time + timeout) > millis()) { + if (get_available() == 0) + { + continue; + } + auto character = _read_byte(); if (character == stop_char) + { break; + } buffer_out[position++] = character; } @@ -434,6 +490,34 @@ void WiFiModule::_read_buffer(char *buffer_out, size_t length, char stop_char) n buffer_out[position] = '\0'; } +// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) +void WiFiModule::_read_bytes(char *buffer_out, size_t length, uint64_t timeout) noexcept +{ + const auto original_length = length; + + auto position = 0U; + const auto start_time = millis(); + + while (position != length && (start_time + timeout) > millis()) + { + if (get_available() == 0) + { + continue; + } + + char character = _read_byte(); + + buffer_out[position++] = character; + + if (character == '\n' && position != original_length) + { + length -= 2U; + } + } + + buffer_out[position] = '\0'; +} + auto WiFiModule::_read_byte() noexcept -> char { return static_cast<char>(_serial.read()); diff --git a/minion/src/wifi_module.hpp b/minion/src/wifi_module.hpp index 43999d6..62ea4f4 100644 --- a/minion/src/wifi_module.hpp +++ b/minion/src/wifi_module.hpp @@ -1,15 +1,23 @@ #pragma once +#include "http/request.hpp" + #include <SoftwareSerial.h> #include <stddef.h> #include <stdint.h> constexpr auto MAX_NETWORK_MODULE_RESPONSE_LENGTH = 128U; +constexpr auto REQUEST_DATA_LENGTH_BUF_SIZE = 16U; +constexpr auto REQUEST_METHOD_STR_MAX_LENGTH = 10U; +constexpr auto REQUEST_PATH_MAX_LENGTH = 64U; +constexpr auto REQUEST_HTTP_VERSION_MAX_LENGTH = 10U; + constexpr auto ASCII_TO_CHAR = 48U; constexpr auto TIMEOUT_SHORT = 1500U; -constexpr auto TIMEOUT_LONG = 4000U; +constexpr auto TIMEOUT_MEDIUM = 4000U; +constexpr auto TIMEOUT_LONG = 10000U; enum WifiMode { @@ -77,16 +85,13 @@ public: */ auto get_local_ip(char *local_ip_out) noexcept -> const char *; - auto has_incoming_request() noexcept -> bool; - /** * Reads a incoming HTTP request. * - * @param raw_request_out Raw request output buffer. - * - * @returns The connection ID. + * @returns A pointer to the request. Has to be deleted after use. Is nullptr if + * reading the request failed. */ - auto read_incoming_request(char *raw_request_out) noexcept -> size_t; + auto read_incoming_request() noexcept -> HTTPRequest *; auto close_connection(size_t connection_id) noexcept -> bool; @@ -104,6 +109,12 @@ private: */ auto _send_serial(const char *command) noexcept -> bool; + size_t _read_connection_id() noexcept; + + size_t _read_request_data_length() noexcept; + + HTTPRequestMethod _read_request_method(size_t &request_data_length) noexcept; + /** * Reads from the wifi module until it responds with a status. * @@ -115,7 +126,10 @@ private: */ auto _read(uint64_t timeout, char *response_out) noexcept -> ResponseStatus; - void _read_buffer(char *buffer_out, size_t length, char stop_char) noexcept; + void + _read_to(char *buffer_out, size_t length, char stop_char, uint64_t timeout) noexcept; + + void _read_bytes(char *buffer_out, size_t length, uint64_t timeout) noexcept; auto _read_byte() noexcept -> char; }; |