diff --git a/Sming/Components/Network/src/Network/FtpServer.cpp b/Sming/Components/Network/src/Network/Ftp/FtpServer.cpp similarity index 97% rename from Sming/Components/Network/src/Network/FtpServer.cpp rename to Sming/Components/Network/src/Network/Ftp/FtpServer.cpp index 985fa77aeb..0471a6749c 100644 --- a/Sming/Components/Network/src/Network/FtpServer.cpp +++ b/Sming/Components/Network/src/Network/Ftp/FtpServer.cpp @@ -9,7 +9,7 @@ ****/ #include "FtpServer.h" -#include "Ftp/FtpServerConnection.h" +#include "FtpServerConnection.h" TcpConnection* CustomFtpServer::createClient(tcp_pcb* clientTcp) { diff --git a/Sming/Components/Network/src/Network/Ftp/FtpServer.h b/Sming/Components/Network/src/Network/Ftp/FtpServer.h new file mode 100644 index 0000000000..a1563eb8c6 --- /dev/null +++ b/Sming/Components/Network/src/Network/Ftp/FtpServer.h @@ -0,0 +1,85 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * FtpServer.h + * + ****/ + +#pragma once + +#include "../TcpServer.h" +#include +#include + +class FtpServerConnection; + +/** @defgroup ftpserver FTP server + * @ingroup tcpserver + * @brief Base implementation for FTP server + */ +class CustomFtpServer : public TcpServer +{ + friend class FtpServerConnection; + +public: + CustomFtpServer(IFS::FileSystem* fileSystem = nullptr) : fileSystem(fileSystem) + { + setTimeOut(900); + } + + /** + * @brief Validate user + * @param login User name + * @param pass User password + * @retval IFS::UserRole Returns assigned user role, None if user not validated + */ + virtual IFS::UserRole validateUser(const char* login, const char* pass) = 0; + +protected: + TcpConnection* createClient(tcp_pcb* clientTcp) override; + + /** + * @brief Handle an incoming command + * @param cmd The command identifier, e.g. LIST + * @param data Any command arguments + * @param connection The associated TCP connection to receive any response + * @retval bool true if command handled and response sent + */ + virtual bool onCommand(String cmd, String data, FtpServerConnection& connection) + { + return false; + } + + IFS::FileSystem* getFileSystem() const + { + return fileSystem ?: ::getFileSystem(); + } + +private: + IFS::FileSystem* fileSystem; +}; + +/** + * @ingroup ftpserver + * @brief Provides FTP server + */ +class FtpServer : public CustomFtpServer +{ +public: + void addUser(const String& login, const String& pass, IFS::UserRole userRole = IFS::UserRole::Admin); + IFS::UserRole validateUser(const char* login, const char* pass) override; + +protected: + bool onCommand(String cmd, String data, FtpServerConnection& connection) override; + +private: + struct User { + String password; + IFS::UserRole role; + }; + using UserList = HashMap; + UserList users; +}; diff --git a/Sming/Components/Network/src/Network/FtpServer.h b/Sming/Components/Network/src/Network/FtpServer.h index 7d0e9492a0..8fd5ddc955 100644 --- a/Sming/Components/Network/src/Network/FtpServer.h +++ b/Sming/Components/Network/src/Network/FtpServer.h @@ -10,76 +10,4 @@ #pragma once -#include "TcpServer.h" -#include "WHashMap.h" -#include - -class FtpServerConnection; - -/** @defgroup ftpserver FTP server - * @ingroup tcpserver - * @brief Base implementation for FTP server - */ -class CustomFtpServer : public TcpServer -{ - friend class FtpServerConnection; - -public: - CustomFtpServer(IFS::FileSystem* fileSystem = nullptr) : fileSystem(fileSystem) - { - setTimeOut(900); - } - - /** - * @brief Validate user - * @param login User name - * @param pass User password - * @retval IFS::UserRole Returns assigned user role, None if user not validated - */ - virtual IFS::UserRole validateUser(const char* login, const char* pass) = 0; - -protected: - TcpConnection* createClient(tcp_pcb* clientTcp) override; - - /** - * @brief Handle an incoming command - * @param cmd The command identifier, e.g. LIST - * @param data Any command arguments - * @param connection The associated TCP connection to receive any response - * @retval bool true if command handled and response sent - */ - virtual bool onCommand(String cmd, String data, FtpServerConnection& connection) - { - return false; - } - - IFS::FileSystem* getFileSystem() const - { - return fileSystem ?: ::getFileSystem(); - } - -private: - IFS::FileSystem* fileSystem; -}; - -/** - * @ingroup ftpserver - * @brief Provides FTP server - */ -class FtpServer : public CustomFtpServer -{ -public: - void addUser(const String& login, const String& pass, IFS::UserRole userRole = IFS::UserRole::Admin); - IFS::UserRole validateUser(const char* login, const char* pass) override; - -protected: - bool onCommand(String cmd, String data, FtpServerConnection& connection) override; - -private: - struct User { - String password; - IFS::UserRole role; - }; - using UserList = HashMap; - UserList users; -}; +#include "Ftp/FtpServer.h" diff --git a/Sming/Components/Network/src/Network/HttpClient.cpp b/Sming/Components/Network/src/Network/Http/HttpClient.cpp similarity index 98% rename from Sming/Components/Network/src/Network/HttpClient.cpp rename to Sming/Components/Network/src/Network/Http/HttpClient.cpp index 7c97be2d6e..88d5ff7b4d 100644 --- a/Sming/Components/Network/src/Network/HttpClient.cpp +++ b/Sming/Components/Network/src/Network/Http/HttpClient.cpp @@ -11,7 +11,7 @@ ****/ #include "HttpClient.h" -#include "Data/Stream/FileStream.h" +#include HttpClient::HttpConnectionPool HttpClient::httpConnectionPool; SimpleTimer HttpClient::cleanUpTimer; diff --git a/Sming/Components/Network/src/Network/Http/HttpClient.h b/Sming/Components/Network/src/Network/Http/HttpClient.h new file mode 100644 index 0000000000..73d0ff9cdc --- /dev/null +++ b/Sming/Components/Network/src/Network/Http/HttpClient.h @@ -0,0 +1,146 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * HttpClient.h + * + * Modified: 2017 - Slavey Karadzhov + * + ****/ + +/** @defgroup httpclient HTTP client + * @brief Provides HTTP/S client + * @ingroup tcpclient + * @{ + */ + +#pragma once + +#include "../TcpClient.h" +#include "HttpCommon.h" +#include "HttpRequest.h" +#include "HttpClientConnection.h" +#include +#include + +class HttpClient +{ +public: + /** + * @brief HttpClient destructor + * @note DON'T call cleanup. + * If you want to free all resources from HttpClients the correct sequence will be to + * 1. Delete all instances of HttpClient + * 2. Call the static method HttpClient::cleanup(); + */ + virtual ~HttpClient() + { + } + + /* High-Level Methods */ + + bool sendRequest(const Url& url, RequestCompletedDelegate requestComplete) + { + return send(createRequest(url)->setMethod(HTTP_GET)->onRequestComplete(requestComplete)); + } + + bool sendRequest(const HttpMethod method, const Url& url, const HttpHeaders& headers, + RequestCompletedDelegate requestComplete) + { + return send(createRequest(url)->setMethod(method)->setHeaders(headers)->onRequestComplete(requestComplete)); + } + + bool sendRequest(const HttpMethod method, const Url& url, const HttpHeaders& headers, const String& body, + RequestCompletedDelegate requestComplete) + { + return send(createRequest(url)->setMethod(method)->setHeaders(headers)->setBody(body)->onRequestComplete( + requestComplete)); + } + + bool sendRequest(const HttpMethod method, const Url& url, const HttpHeaders& headers, String&& body, + RequestCompletedDelegate requestComplete) noexcept + { + return send(createRequest(url) + ->setMethod(method) + ->setHeaders(headers) + ->setBody(std::move(body)) + ->onRequestComplete(requestComplete)); + } + + /** + * @brief Queue request to download content as string (in memory) + * @param url URL from which the content will be fetched + * @param requestComplete Completion callback + * @param maxLength maximum bytes to store in memory. If the response is bigger than `maxLength` then the rest bytes will be discarded. + * Use this parameter wisely as setting the value too high may consume all available RAM resulting in + * device restart and Denial-Of-Service + */ + bool downloadString(const Url& url, RequestCompletedDelegate requestComplete, + size_t maxLength = NETWORK_SEND_BUFFER_SIZE) + { + return send(createRequest(url) + ->setMethod(HTTP_GET) + ->setResponseStream(new LimitedMemoryStream(maxLength)) + ->onRequestComplete(requestComplete)); + } + + bool downloadFile(const Url& url, RequestCompletedDelegate requestComplete = nullptr) + { + return downloadFile(url, nullptr, requestComplete); + } + + /** + * @brief Queue request to download a file + * @param url Source of file data + * @param saveFileName Path to save file to. Optional: specify nullptr to use name from url + * @param requestComplete Completion callback + */ + bool downloadFile(const Url& url, const String& saveFileName, RequestCompletedDelegate requestComplete = nullptr); + + /* Low Level Methods */ + + /* + * @brief This method queues a request and sends it, once it is connected to the remote server. + * @param HttpRequest* request The request object will be freed inside of the method. + * Do not try to reuse it outside of the send method as it will lead to unpredicted results + * + * @retval bool true if the request was queued, false otherwise. + * + */ + bool send(HttpRequest* request); + + /** @brief Helper function to create a new request on a URL + * @param url + * @retval HttpRequest* + */ + HttpRequest* createRequest(const Url& url) + { + return new HttpRequest(url); + } + + /** + * @brief Use this method to clean all object pools + */ + static void cleanup() + { + httpConnectionPool.clear(); + } + +protected: + String getCacheKey(const Url& url) + { + return url.Host + ':' + url.getPort(); + } + +protected: + using HttpConnectionPool = ObjectMap; + static HttpConnectionPool httpConnectionPool; + +private: + static SimpleTimer cleanUpTimer; + static void cleanInactive(); +}; + +/** @} */ diff --git a/Sming/Components/Network/src/Network/HttpServer.cpp b/Sming/Components/Network/src/Network/Http/HttpServer.cpp similarity index 95% rename from Sming/Components/Network/src/Network/HttpServer.cpp rename to Sming/Components/Network/src/Network/Http/HttpServer.cpp index 9b0844a4bf..702ba135cb 100644 --- a/Sming/Components/Network/src/Network/HttpServer.cpp +++ b/Sming/Components/Network/src/Network/Http/HttpServer.cpp @@ -11,8 +11,6 @@ ****/ #include "HttpServer.h" -#include "TcpClient.h" -#include "WString.h" void HttpServer::configure(const HttpServerSettings& settings) { diff --git a/Sming/Components/Network/src/Network/Http/HttpServer.h b/Sming/Components/Network/src/Network/Http/HttpServer.h new file mode 100644 index 0000000000..db5ec08f55 --- /dev/null +++ b/Sming/Components/Network/src/Network/Http/HttpServer.h @@ -0,0 +1,91 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * HttpServer.h + * + * Modified: 2017 - Slavey Karadzhov + * + ****/ + +/** @defgroup httpserver HTTP server + * @brief Provides powerful HTTP/S + Websocket server + * @ingroup tcpserver + * @{ + */ + +#pragma once + +#include "../TcpServer.h" +#include +#include "HttpResourceTree.h" +#include "HttpServerConnection.h" +#include "HttpBodyParser.h" + +struct HttpServerSettings { + uint16_t maxActiveConnections = 10; ///< maximum number of concurrent requests.. + uint16_t keepAliveSeconds = 0; ///< default seconds to keep the connection alive before closing it + int minHeapSize = -1; ///< min heap size that is required to accept connection, -1 means use server default + bool useDefaultBodyParsers = 1; ///< if the default body parsers, as form-url-encoded, should be used + bool closeOnContentError = + true; ///< close the connection if a body parser or resource fails to parse the body content. +}; + +class HttpServer : public TcpServer +{ +public: + HttpServer() + { + settings.keepAliveSeconds = 2; + configure(settings); + } + + HttpServer(const HttpServerSettings& settings) + { + configure(settings); + } + + /** + * @brief Allows changing the server configuration + */ + void configure(const HttpServerSettings& settings); + + /** + * @brief Allows content-type specific parsing of the body based on content-type. + * + * @param contentType Can be full content-type like 'application/json', or 'application/*' or '*'. + * If there is exact match for the content-type wildcard content-types will not be used. + * There can be only one catch-all '*' body parser and that will be the last registered + * + * @param parser + */ + void setBodyParser(const String& contentType, HttpBodyParserDelegate parser) + { + bodyParsers[contentType] = parser; + } + + /** + * @brief Allows content-type specific parsing of the body based on content-type. + * @param mimeType + * @param parser + */ + void setBodyParser(MimeType mimeType, HttpBodyParserDelegate parser) + { + bodyParsers[toString(mimeType)] = parser; + } + +public: + /** @brief Maps paths to resources which deal with incoming requests */ + HttpResourceTree paths; + +protected: + TcpConnection* createClient(tcp_pcb* clientTcp) override; + +private: + HttpServerSettings settings; + BodyParsers bodyParsers; +}; + +/** @} */ diff --git a/Sming/Components/Network/src/Network/WebsocketClient.cpp b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketClient.cpp similarity index 99% rename from Sming/Components/Network/src/Network/WebsocketClient.cpp rename to Sming/Components/Network/src/Network/Http/Websocket/WebsocketClient.cpp index fd464252eb..1f275a3098 100644 --- a/Sming/Components/Network/src/Network/WebsocketClient.cpp +++ b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketClient.cpp @@ -14,7 +14,7 @@ ****/ #include "WebsocketClient.h" -#include "Http/HttpHeaders.h" +#include "../HttpHeaders.h" #include class WebsocketClientConnection : public HttpClientConnection diff --git a/Sming/Components/Network/src/Network/Http/Websocket/WebsocketClient.h b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketClient.h new file mode 100644 index 0000000000..575c68e8a7 --- /dev/null +++ b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketClient.h @@ -0,0 +1,98 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * WebsocketClient.h + * + * @authors: + * Originally - hrsavla + * Refactored - Alexander V, Ribchansky + * Refactored - Slavey Karadzhov + * + ****/ + +//TODO: Add stream support for sending big chunks of data via websockets. + +#pragma once + +#include "../HttpClientConnection.h" +#include "WebsocketConnection.h" + +/** @defgroup wsclient Websocket client + * @brief Provides Websocket client + * @ingroup tcpclient + * @{ + */ + +/** @brief Websocket Client + */ +class WebsocketClient : protected WebsocketConnection +{ +public: + WebsocketClient(HttpConnection* connection = nullptr) : WebsocketConnection(connection) + { + } + + using WebsocketConnection::setBinaryHandler; + using WebsocketConnection::setConnectionHandler; + using WebsocketConnection::setDisconnectionHandler; + using WebsocketConnection::setMessageHandler; + + HttpConnection* getHttpConnection(); + + /** @brief Connects websocket client to server + * @param url Url address of websocket server + */ + bool connect(const Url& url); + + using WebsocketConnection::send; + using WebsocketConnection::sendBinary; + using WebsocketConnection::sendString; + + /** @brief Send websocket ping to server + * + * @param payload Maximum 255 bytes + * + * @retval bool true if the data can be send, false otherwise + */ + void sendPing(const String& payload = nullptr) + { + debug_d("Sending PING"); + WebsocketConnection::send(payload.c_str(), payload.length(), WS_FRAME_PING); + } + + /** @brief Send websocket ping to server + * @param payload Maximum 255 bytes + * + * @retval bool true if the data can be send, false otherwise + */ + void sendPong(const String& payload = nullptr) + { + debug_d("Sending PONG"); + WebsocketConnection::send(payload.c_str(), payload.length(), WS_FRAME_PONG); + } + + using WebsocketConnection::close; + using WebsocketConnection::getState; + + /** + * @brief Set the SSL session initialisation callback + * @param handler + */ + void setSslInitHandler(Ssl::Session::InitDelegate handler) + { + sslInitHandler = handler; + } + +protected: + int verifyKey(HttpConnection& connection, HttpResponse& response); + +private: + Url uri; + String key; + Ssl::Session::InitDelegate sslInitHandler; +}; + +/** @} */ diff --git a/Sming/Components/Network/src/Network/HttpClient.h b/Sming/Components/Network/src/Network/HttpClient.h index 9d1e86687b..53620b5632 100644 --- a/Sming/Components/Network/src/Network/HttpClient.h +++ b/Sming/Components/Network/src/Network/HttpClient.h @@ -6,141 +6,8 @@ * * HttpClient.h * - * Modified: 2017 - Slavey Karadzhov - * ****/ -/** @defgroup httpclient HTTP client - * @brief Provides HTTP/S client - * @ingroup tcpclient - * @{ - */ - #pragma once -#include "TcpClient.h" -#include "Http/HttpCommon.h" -#include "Http/HttpRequest.h" -#include "Http/HttpClientConnection.h" -#include "Data/Stream/LimitedMemoryStream.h" -#include - -class HttpClient -{ -public: - /** - * @brief HttpClient destructor - * @note DON'T call cleanup. - * If you want to free all resources from HttpClients the correct sequence will be to - * 1. Delete all instances of HttpClient - * 2. Call the static method HttpClient::cleanup(); - */ - virtual ~HttpClient() - { - } - - /* High-Level Methods */ - - bool sendRequest(const Url& url, RequestCompletedDelegate requestComplete) - { - return send(createRequest(url)->setMethod(HTTP_GET)->onRequestComplete(requestComplete)); - } - - bool sendRequest(const HttpMethod method, const Url& url, const HttpHeaders& headers, - RequestCompletedDelegate requestComplete) - { - return send(createRequest(url)->setMethod(method)->setHeaders(headers)->onRequestComplete(requestComplete)); - } - - bool sendRequest(const HttpMethod method, const Url& url, const HttpHeaders& headers, const String& body, - RequestCompletedDelegate requestComplete) - { - return send(createRequest(url)->setMethod(method)->setHeaders(headers)->setBody(body)->onRequestComplete( - requestComplete)); - } - - bool sendRequest(const HttpMethod method, const Url& url, const HttpHeaders& headers, String&& body, - RequestCompletedDelegate requestComplete) noexcept - { - return send(createRequest(url) - ->setMethod(method) - ->setHeaders(headers) - ->setBody(std::move(body)) - ->onRequestComplete(requestComplete)); - } - - /** - * @brief Queue request to download content as string (in memory) - * @param url URL from which the content will be fetched - * @param requestComplete Completion callback - * @param maxLength maximum bytes to store in memory. If the response is bigger than `maxLength` then the rest bytes will be discarded. - * Use this parameter wisely as setting the value too high may consume all available RAM resulting in - * device restart and Denial-Of-Service - */ - bool downloadString(const Url& url, RequestCompletedDelegate requestComplete, - size_t maxLength = NETWORK_SEND_BUFFER_SIZE) - { - return send(createRequest(url) - ->setMethod(HTTP_GET) - ->setResponseStream(new LimitedMemoryStream(maxLength)) - ->onRequestComplete(requestComplete)); - } - - bool downloadFile(const Url& url, RequestCompletedDelegate requestComplete = nullptr) - { - return downloadFile(url, nullptr, requestComplete); - } - - /** - * @brief Queue request to download a file - * @param url Source of file data - * @param saveFileName Path to save file to. Optional: specify nullptr to use name from url - * @param requestComplete Completion callback - */ - bool downloadFile(const Url& url, const String& saveFileName, RequestCompletedDelegate requestComplete = nullptr); - - /* Low Level Methods */ - - /* - * @brief This method queues a request and sends it, once it is connected to the remote server. - * @param HttpRequest* request The request object will be freed inside of the method. - * Do not try to reuse it outside of the send method as it will lead to unpredicted results - * - * @retval bool true if the request was queued, false otherwise. - * - */ - bool send(HttpRequest* request); - - /** @brief Helper function to create a new request on a URL - * @param url - * @retval HttpRequest* - */ - HttpRequest* createRequest(const Url& url) - { - return new HttpRequest(url); - } - - /** - * @brief Use this method to clean all object pools - */ - static void cleanup() - { - httpConnectionPool.clear(); - } - -protected: - String getCacheKey(const Url& url) - { - return url.Host + ':' + url.getPort(); - } - -protected: - using HttpConnectionPool = ObjectMap; - static HttpConnectionPool httpConnectionPool; - -private: - static SimpleTimer cleanUpTimer; - static void cleanInactive(); -}; - -/** @} */ +#include "Http/HttpClient.h" diff --git a/Sming/Components/Network/src/Network/HttpServer.h b/Sming/Components/Network/src/Network/HttpServer.h index 7ca2b0b26b..3cc00f193f 100644 --- a/Sming/Components/Network/src/Network/HttpServer.h +++ b/Sming/Components/Network/src/Network/HttpServer.h @@ -6,86 +6,8 @@ * * HttpServer.h * - * Modified: 2017 - Slavey Karadzhov - * ****/ -/** @defgroup httpserver HTTP server - * @brief Provides powerful HTTP/S + Websocket server - * @ingroup tcpserver - * @{ - */ - #pragma once -#include "TcpServer.h" -#include "WString.h" -#include "Http/HttpResourceTree.h" -#include "Http/HttpServerConnection.h" -#include "Http/HttpBodyParser.h" - -struct HttpServerSettings { - uint16_t maxActiveConnections = 10; ///< maximum number of concurrent requests.. - uint16_t keepAliveSeconds = 0; ///< default seconds to keep the connection alive before closing it - int minHeapSize = -1; ///< min heap size that is required to accept connection, -1 means use server default - bool useDefaultBodyParsers = 1; ///< if the default body parsers, as form-url-encoded, should be used - bool closeOnContentError = - true; ///< close the connection if a body parser or resource fails to parse the body content. -}; - -class HttpServer : public TcpServer -{ -public: - HttpServer() - { - settings.keepAliveSeconds = 2; - configure(settings); - } - - HttpServer(const HttpServerSettings& settings) - { - configure(settings); - } - - /** - * @brief Allows changing the server configuration - */ - void configure(const HttpServerSettings& settings); - - /** - * @brief Allows content-type specific parsing of the body based on content-type. - * - * @param contentType Can be full content-type like 'application/json', or 'application/*' or '*'. - * If there is exact match for the content-type wildcard content-types will not be used. - * There can be only one catch-all '*' body parser and that will be the last registered - * - * @param parser - */ - void setBodyParser(const String& contentType, HttpBodyParserDelegate parser) - { - bodyParsers[contentType] = parser; - } - - /** - * @brief Allows content-type specific parsing of the body based on content-type. - * @param mimeType - * @param parser - */ - void setBodyParser(MimeType mimeType, HttpBodyParserDelegate parser) - { - bodyParsers[toString(mimeType)] = parser; - } - -public: - /** @brief Maps paths to resources which deal with incoming requests */ - HttpResourceTree paths; - -protected: - TcpConnection* createClient(tcp_pcb* clientTcp) override; - -private: - HttpServerSettings settings; - BodyParsers bodyParsers; -}; - -/** @} */ +#include "Http/HttpServer.h" diff --git a/Sming/Components/Network/src/Network/MqttClient.cpp b/Sming/Components/Network/src/Network/Mqtt/MqttClient.cpp similarity index 99% rename from Sming/Components/Network/src/Network/MqttClient.cpp rename to Sming/Components/Network/src/Network/Mqtt/MqttClient.cpp index 2e37419af8..52cd5be3d5 100644 --- a/Sming/Components/Network/src/Network/MqttClient.cpp +++ b/Sming/Components/Network/src/Network/Mqtt/MqttClient.cpp @@ -9,8 +9,7 @@ ****/ #include "MqttClient.h" - -#include "Data/Stream/DataSourceStream.h" +#include const mqtt_parser_callbacks_t MqttClient::callbacks PROGMEM = { .on_message_begin = staticOnMessageBegin, diff --git a/Sming/Components/Network/src/Network/Mqtt/MqttClient.h b/Sming/Components/Network/src/Network/Mqtt/MqttClient.h new file mode 100644 index 0000000000..907783b967 --- /dev/null +++ b/Sming/Components/Network/src/Network/Mqtt/MqttClient.h @@ -0,0 +1,270 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * MqttClient.h + * + ****/ + +#pragma once + +#include "../TcpClient.h" +#include "../Url.h" +#include +#include +#include +#include +#include +#include "MqttPayloadParser.h" +#include +#include +#include + +/** @defgroup mqttclient MQTT client + * @brief Provides MQTT client + * @ingroup tcpclient + * @{ + */ + +enum MqttClientState { eMCS_Ready = 0, eMCS_SendingData }; + +#ifndef MQTT_REQUEST_POOL_SIZE +#define MQTT_REQUEST_POOL_SIZE 10 +#endif + +#define MQTT_CLIENT_CONNECTED bit(1) + +#define MQTT_FLAG_RETAINED 1 + +class MqttClient; + +using MqttDelegate = Delegate; +using MqttRequestQueue = ObjectQueue; + +class MqttClient : protected TcpClient +{ +public: + MqttClient(bool withDefaultPayloadParser = true, bool autoDestruct = false); + + ~MqttClient(); + + /** + * @brief Sets keep-alive time. That information is sent during connection to the server + * @param seconds + */ + void setKeepAlive(uint16_t seconds) //send to broker + { + keepAlive = seconds; + if(seconds < pingRepeatTime) { + setPingRepeatTime(seconds); + } + } + + /** + * Sets the interval in which to ping the remote server if there was no activity + * @param seconds + */ + void setPingRepeatTime(uint16_t seconds) + { + seconds = std::min(keepAlive, seconds); + if(seconds != pingRepeatTime) { + pingRepeatTime = seconds; + pingTimer.reset(seconds); + } + } + + /** + * Sets last will and testament + * @param topic + * @param message + * @param flags QoS, retain, etc flags + * @retval bool + */ + bool setWill(const String& topic, const String& message, uint8_t flags = 0); + + /** @brief Connect to a MQTT server + * @param url URL in the form "mqtt://user:password@server:port" or "mqtts://user:password@server:port" + * @param uniqueClientName + * @retval bool + */ + bool connect(const Url& url, const String& uniqueClientName); + + /** + * @brief Publish a message + * @param topic + * @param message Message content as String + * @param flags Optional flags + * @retval bool + */ + bool publish(const String& topic, const String& message, uint8_t flags = 0); + + /** + * @brief Publish a message + * @param topic + * @param message Message content as read-only stream + * @param flags Optional flags + * @retval bool + */ + bool publish(const String& topic, IDataSourceStream* stream, uint8_t flags = 0); + + /** + * @brief Subscribe to a topic + * @param topic + * @retval bool + */ + bool subscribe(const String& topic); + + /** + * @brief Unsubscribe from a topic + * @param topic + * @retval bool + */ + bool unsubscribe(const String& topic); + + /** + * @brief Register a callback function to be invoked on incoming event notification + * @param type Type of event to be notified of + * @param handler The callback. Pass nullptr to cancel callback. + */ + void setEventHandler(mqtt_type_t type, MqttDelegate handler) + { + eventHandlers[type] = handler; + } + + /** + * @brief Sets or clears a payload parser (for PUBLISH messages from the server to us) + * @note We no longer have size limitation for incoming or outgoing messages + * but in order to prevent running out of memory we have a "sane" payload parser + * that will read up to 1K of payload + */ + void setPayloadParser(MqttPayloadParser payloadParser = nullptr) + { + this->payloadParser = payloadParser; + } + + /* [ Convenience methods ] */ + + /** + * @brief Compute the flags value + * @param QoS - Quality of Service + * @param retain - Retain flag + * @param dup - Duplicate delivery + * + * @retval uint8_t calculated flags value + */ + static uint8_t getFlags(mqtt_qos_t QoS, mqtt_retain_t retain = MQTT_RETAIN_FALSE, mqtt_dup_t dup = MQTT_DUP_FALSE) + { + return (retain + (QoS << 1) + (dup << 3)); + } + + /** + * @brief Sets a handler to be called after successful MQTT connection + * + * @param handler + */ + void setConnectedHandler(MqttDelegate handler) + { + eventHandlers[MQTT_TYPE_CONNACK] = handler; + } + + /** + * @brief Sets a handler to be called after receiving confirmation from the server + * for a published message from the client + * + * @param handler + */ + void setPublishedHandler(MqttDelegate handler) + { + eventHandlers[MQTT_TYPE_PUBACK] = handler; + eventHandlers[MQTT_TYPE_PUBREC] = handler; + } + + /** + * @brief Sets a handler to be called after receiving a PUBLISH message from the server + * + * @param handler + */ + void setMessageHandler(MqttDelegate handler) + { + eventHandlers[MQTT_TYPE_PUBLISH] = handler; + } + + /** + * @brief Sets a handler to be called on disconnect from the server + * + * @param handler + */ + void setDisconnectHandler(TcpClientCompleteDelegate handler) + { + TcpClient::setCompleteDelegate(handler); + } + + using TcpClient::getSsl; + using TcpClient::setSslInitHandler; + + using TcpClient::setCompleteDelegate; + + using TcpClient::getConnectionState; + using TcpClient::isProcessing; + + using TcpClient::getRemoteIp; + using TcpClient::getRemotePort; + +protected: + void onReadyToSendData(TcpConnectionEvent sourceEvent) override; + void onFinished(TcpClientState finishState) override; + +private: + using TcpClient::connect; // Keep compiler happy but prevent access to base method by clients + + // TCP methods + virtual bool onTcpReceive(TcpClient& client, char* data, int size); + + // MQTT parser methods + static int staticOnMessageBegin(void* user_data, mqtt_message_t* message); + static int staticOnDataBegin(void* user_data, mqtt_message_t* message); + static int staticOnDataPayload(void* user_data, mqtt_message_t* message, const char* data, size_t length); + static int staticOnDataEnd(void* user_data, mqtt_message_t* message); + static int staticOnMessageEnd(void* user_data, mqtt_message_t* message); + int onMessageEnd(mqtt_message_t* message); + +private: + Url url; + + // callbacks + using HandlerMap = HashMap; + HandlerMap eventHandlers; + MqttPayloadParser payloadParser = nullptr; + + // states + MqttClientState state = eMCS_Ready; + MqttPayloadParserState payloadState = {}; + + // keep-alive and ping + uint16_t keepAlive = 60; + uint16_t pingRepeatTime = 20; ///< pingRepeatTime should be <= keepAlive + OneShotElapseTimer pingTimer; + + // messages + MqttRequestQueue requestQueue; + mqtt_message_t connectMessage; + bool connectQueued = false; ///< True if our connect message needs to be sent + mqtt_message_t* outgoingMessage = nullptr; + mqtt_message_t incomingMessage; + + // parsers and serializers + mqtt_serialiser_t serialiser; + static const mqtt_parser_callbacks_t callbacks; + mqtt_parser_t parser; + + // client flags + uint8_t flags = 0; + /* 7 8 6 5 4 3 2 1 0 + * | + * --- set when connected ... + */ +}; + +/** @} */ diff --git a/Sming/Components/Network/src/Network/MqttClient.h b/Sming/Components/Network/src/Network/MqttClient.h index 0f82ea19ee..6178958d18 100644 --- a/Sming/Components/Network/src/Network/MqttClient.h +++ b/Sming/Components/Network/src/Network/MqttClient.h @@ -10,261 +10,4 @@ #pragma once -#include "TcpClient.h" -#include "Url.h" -#include -#include -#include -#include -#include -#include "Mqtt/MqttPayloadParser.h" -#include "mqtt-codec/src/message.h" -#include "mqtt-codec/src/serialiser.h" -#include "mqtt-codec/src/parser.h" - -/** @defgroup mqttclient MQTT client - * @brief Provides MQTT client - * @ingroup tcpclient - * @{ - */ - -enum MqttClientState { eMCS_Ready = 0, eMCS_SendingData }; - -#ifndef MQTT_REQUEST_POOL_SIZE -#define MQTT_REQUEST_POOL_SIZE 10 -#endif - -#define MQTT_CLIENT_CONNECTED bit(1) - -#define MQTT_FLAG_RETAINED 1 - -class MqttClient; - -using MqttDelegate = Delegate; -using MqttRequestQueue = ObjectQueue; - -class MqttClient : protected TcpClient -{ -public: - MqttClient(bool withDefaultPayloadParser = true, bool autoDestruct = false); - - ~MqttClient(); - - /** - * @brief Sets keep-alive time. That information is sent during connection to the server - * @param seconds - */ - void setKeepAlive(uint16_t seconds) //send to broker - { - keepAlive = seconds; - if(seconds < pingRepeatTime) { - setPingRepeatTime(seconds); - } - } - - /** - * Sets the interval in which to ping the remote server if there was no activity - * @param seconds - */ - void setPingRepeatTime(uint16_t seconds) - { - seconds = std::min(keepAlive, seconds); - if(seconds != pingRepeatTime) { - pingRepeatTime = seconds; - pingTimer.reset(seconds); - } - } - - /** - * Sets last will and testament - * @param topic - * @param message - * @param flags QoS, retain, etc flags - * @retval bool - */ - bool setWill(const String& topic, const String& message, uint8_t flags = 0); - - /** @brief Connect to a MQTT server - * @param url URL in the form "mqtt://user:password@server:port" or "mqtts://user:password@server:port" - * @param uniqueClientName - * @retval bool - */ - bool connect(const Url& url, const String& uniqueClientName); - - /** - * @brief Publish a message - * @param topic - * @param message Message content as String - * @param flags Optional flags - * @retval bool - */ - bool publish(const String& topic, const String& message, uint8_t flags = 0); - - /** - * @brief Publish a message - * @param topic - * @param message Message content as read-only stream - * @param flags Optional flags - * @retval bool - */ - bool publish(const String& topic, IDataSourceStream* stream, uint8_t flags = 0); - - /** - * @brief Subscribe to a topic - * @param topic - * @retval bool - */ - bool subscribe(const String& topic); - - /** - * @brief Unsubscribe from a topic - * @param topic - * @retval bool - */ - bool unsubscribe(const String& topic); - - /** - * @brief Register a callback function to be invoked on incoming event notification - * @param type Type of event to be notified of - * @param handler The callback. Pass nullptr to cancel callback. - */ - void setEventHandler(mqtt_type_t type, MqttDelegate handler) - { - eventHandlers[type] = handler; - } - - /** - * @brief Sets or clears a payload parser (for PUBLISH messages from the server to us) - * @note We no longer have size limitation for incoming or outgoing messages - * but in order to prevent running out of memory we have a "sane" payload parser - * that will read up to 1K of payload - */ - void setPayloadParser(MqttPayloadParser payloadParser = nullptr) - { - this->payloadParser = payloadParser; - } - - /* [ Convenience methods ] */ - - /** - * @brief Compute the flags value - * @param QoS - Quality of Service - * @param retain - Retain flag - * @param dup - Duplicate delivery - * - * @retval uint8_t calculated flags value - */ - static uint8_t getFlags(mqtt_qos_t QoS, mqtt_retain_t retain = MQTT_RETAIN_FALSE, mqtt_dup_t dup = MQTT_DUP_FALSE) - { - return (retain + (QoS << 1) + (dup << 3)); - } - - /** - * @brief Sets a handler to be called after successful MQTT connection - * - * @param handler - */ - void setConnectedHandler(MqttDelegate handler) - { - eventHandlers[MQTT_TYPE_CONNACK] = handler; - } - - /** - * @brief Sets a handler to be called after receiving confirmation from the server - * for a published message from the client - * - * @param handler - */ - void setPublishedHandler(MqttDelegate handler) - { - eventHandlers[MQTT_TYPE_PUBACK] = handler; - eventHandlers[MQTT_TYPE_PUBREC] = handler; - } - - /** - * @brief Sets a handler to be called after receiving a PUBLISH message from the server - * - * @param handler - */ - void setMessageHandler(MqttDelegate handler) - { - eventHandlers[MQTT_TYPE_PUBLISH] = handler; - } - - /** - * @brief Sets a handler to be called on disconnect from the server - * - * @param handler - */ - void setDisconnectHandler(TcpClientCompleteDelegate handler) - { - TcpClient::setCompleteDelegate(handler); - } - - using TcpClient::getSsl; - using TcpClient::setSslInitHandler; - - using TcpClient::setCompleteDelegate; - - using TcpClient::getConnectionState; - using TcpClient::isProcessing; - - using TcpClient::getRemoteIp; - using TcpClient::getRemotePort; - -protected: - void onReadyToSendData(TcpConnectionEvent sourceEvent) override; - void onFinished(TcpClientState finishState) override; - -private: - using TcpClient::connect; // Keep compiler happy but prevent access to base method by clients - - // TCP methods - virtual bool onTcpReceive(TcpClient& client, char* data, int size); - - // MQTT parser methods - static int staticOnMessageBegin(void* user_data, mqtt_message_t* message); - static int staticOnDataBegin(void* user_data, mqtt_message_t* message); - static int staticOnDataPayload(void* user_data, mqtt_message_t* message, const char* data, size_t length); - static int staticOnDataEnd(void* user_data, mqtt_message_t* message); - static int staticOnMessageEnd(void* user_data, mqtt_message_t* message); - int onMessageEnd(mqtt_message_t* message); - -private: - Url url; - - // callbacks - using HandlerMap = HashMap; - HandlerMap eventHandlers; - MqttPayloadParser payloadParser = nullptr; - - // states - MqttClientState state = eMCS_Ready; - MqttPayloadParserState payloadState = {}; - - // keep-alive and ping - uint16_t keepAlive = 60; - uint16_t pingRepeatTime = 20; ///< pingRepeatTime should be <= keepAlive - OneShotElapseTimer pingTimer; - - // messages - MqttRequestQueue requestQueue; - mqtt_message_t connectMessage; - bool connectQueued = false; ///< True if our connect message needs to be sent - mqtt_message_t* outgoingMessage = nullptr; - mqtt_message_t incomingMessage; - - // parsers and serializers - mqtt_serialiser_t serialiser; - static const mqtt_parser_callbacks_t callbacks; - mqtt_parser_t parser; - - // client flags - uint8_t flags = 0; - /* 7 8 6 5 4 3 2 1 0 - * | - * --- set when connected ... - */ -}; - -/** @} */ +#include "Mqtt/MqttClient.h" diff --git a/Sming/Components/Network/src/Network/WebsocketClient.h b/Sming/Components/Network/src/Network/WebsocketClient.h index db39285fd0..276a59ee50 100644 --- a/Sming/Components/Network/src/Network/WebsocketClient.h +++ b/Sming/Components/Network/src/Network/WebsocketClient.h @@ -6,93 +6,8 @@ * * WebsocketClient.h * - * @authors: - * Originally - hrsavla - * Refactored - Alexander V, Ribchansky - * Refactored - Slavey Karadzhov - * ****/ -//TODO: Add stream support for sending big chunks of data via websockets. - #pragma once -#include "Http/HttpClientConnection.h" -#include "Http/Websocket/WebsocketConnection.h" - -/** @defgroup wsclient Websocket client - * @brief Provides Websocket client - * @ingroup tcpclient - * @{ - */ - -/** @brief Websocket Client - */ -class WebsocketClient : protected WebsocketConnection -{ -public: - WebsocketClient(HttpConnection* connection = nullptr) : WebsocketConnection(connection) - { - } - - using WebsocketConnection::setBinaryHandler; - using WebsocketConnection::setConnectionHandler; - using WebsocketConnection::setDisconnectionHandler; - using WebsocketConnection::setMessageHandler; - - HttpConnection* getHttpConnection(); - - /** @brief Connects websocket client to server - * @param url Url address of websocket server - */ - bool connect(const Url& url); - - using WebsocketConnection::send; - using WebsocketConnection::sendBinary; - using WebsocketConnection::sendString; - - /** @brief Send websocket ping to server - * - * @param payload Maximum 255 bytes - * - * @retval bool true if the data can be send, false otherwise - */ - void sendPing(const String& payload = nullptr) - { - debug_d("Sending PING"); - WebsocketConnection::send(payload.c_str(), payload.length(), WS_FRAME_PING); - } - - /** @brief Send websocket ping to server - * @param payload Maximum 255 bytes - * - * @retval bool true if the data can be send, false otherwise - */ - void sendPong(const String& payload = nullptr) - { - debug_d("Sending PONG"); - WebsocketConnection::send(payload.c_str(), payload.length(), WS_FRAME_PONG); - } - - using WebsocketConnection::close; - using WebsocketConnection::getState; - - /** - * @brief Set the SSL session initialisation callback - * @param handler - */ - void setSslInitHandler(Ssl::Session::InitDelegate handler) - { - sslInitHandler = handler; - } - -protected: - int verifyKey(HttpConnection& connection, HttpResponse& response); - -private: - Url uri; - String key; - Ssl::Session::InitDelegate sslInitHandler; -}; - -/** @} */ +#include "Http/Websocket/WebsocketClient.h"