Add initial webserver sources
This commit is contained in:
1 parent
2721be2d22
commit
7b93dfa6cf
26 files changed
+1018
No files matched your search
@@ -0,0 +1,114 @@
|
||||
#include "debugresponsehandler.h"
|
||||
|
||||
// esp-idf includes
|
||||
#include <asio.hpp>
|
||||
#include <esp_log.h>
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <fmt/core.h>
|
||||
#include <asio_webserver/clientconnection.h>
|
||||
|
||||
namespace {
|
||||
constexpr const char * const TAG = "ASIO_WEBSERVER";
|
||||
} // namespace
|
||||
|
||||
DebugResponseHandler::DebugResponseHandler(ClientConnection &clientConnection, std::string_view method, std::string_view path, std::string_view protocol) :
|
||||
m_clientConnection{clientConnection}, m_method{method}, m_path{path}, m_protocol{protocol}
|
||||
{
|
||||
ESP_LOGI(TAG, "constructed for %.*s %.*s (%s:%hi)", m_method.size(), m_method.data(), path.size(), path.data(),
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
}
|
||||
|
||||
DebugResponseHandler::~DebugResponseHandler()
|
||||
{
|
||||
ESP_LOGI(TAG, "destructed for %.*s %.*s (%s:%hi)", m_method.size(), m_method.data(), m_path.size(), m_path.data(),
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
}
|
||||
|
||||
void DebugResponseHandler::requestHeaderReceived(std::string_view key, std::string_view value)
|
||||
{
|
||||
m_requestHeaders.emplace_back(std::make_pair(std::string{key}, std::string{value}));
|
||||
}
|
||||
|
||||
void DebugResponseHandler::sendResponse()
|
||||
{
|
||||
ESP_LOGI(TAG, "sending response for %.*s %.*s (%s:%hi)", m_method.size(), m_method.data(), m_path.size(), m_path.data(),
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
|
||||
m_response = fmt::format("<html>"
|
||||
"<head>"
|
||||
"<title>Test</title>"
|
||||
"</head>"
|
||||
"<body>"
|
||||
"<h1>Request details:</h1>"
|
||||
"<table>"
|
||||
"<tbody>"
|
||||
"<tr><th>Method:</th><td>{}</td></tr>"
|
||||
"<tr><th>Path:</th><td>{}</td></tr>"
|
||||
"<tr><th>Protocol:</th><td>{}</td></tr>"
|
||||
"<tr>"
|
||||
"<th>Request headers:</th>"
|
||||
"<td>"
|
||||
"<table border=\"1\">",
|
||||
m_method, m_path, m_protocol);
|
||||
|
||||
for (const auto &pair : m_requestHeaders)
|
||||
m_response += fmt::format( "<tr><th>{}</th><td>{}</td></tr>",
|
||||
pair.first, pair.second);
|
||||
|
||||
m_response += fmt::format( "</table>"
|
||||
"</td>"
|
||||
"</tr>"
|
||||
"</tbody>"
|
||||
"</table>"
|
||||
"<form method=\"GET\">"
|
||||
"<fieldset>"
|
||||
"<legend>GET form with line edit</legend>"
|
||||
"<label>Text-Input: <input type=\"text\" name=\"inputName\" /></label>"
|
||||
"<button type=\"submit\">Go</button>"
|
||||
"</fieldset>"
|
||||
"</form>"
|
||||
"<form method=\"POST\">"
|
||||
"<fieldset>"
|
||||
"<legend>POST form with line edit</legend>"
|
||||
"<label>Text-Input: <input type=\"text\" name=\"inputName\" /></label>"
|
||||
"<button type=\"submit\">Go</button>"
|
||||
"</fieldset>"
|
||||
"</form>"
|
||||
"<form method=\"POST\">"
|
||||
"<fieldset>"
|
||||
"<legend>POST form with multiline edit</legend>"
|
||||
"<label>Text-Input: <textarea name=\"inputName\"></textarea></label>"
|
||||
"<button type=\"submit\">Go</button>"
|
||||
"</fieldset>"
|
||||
"</form>"
|
||||
"<form method=\"POST\" enctype=\"multipart/form-data\">"
|
||||
"<fieldset>"
|
||||
"<legend>POST form with multipart form-data and line edit</legend>"
|
||||
"<label>Text-Input: <input type=\"text\" name=\"inputName\" /></label>"
|
||||
"<button type=\"submit\">Go</button>"
|
||||
"</fieldset>"
|
||||
"</form>"
|
||||
"</body>"
|
||||
"</html>");
|
||||
|
||||
m_response = fmt::format("HTTP/1.1 200 Ok\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Content-Length: {}\r\n"
|
||||
"\r\n"
|
||||
"{}",
|
||||
m_response.size(), m_response);
|
||||
|
||||
asio::async_write(m_clientConnection.socket(),
|
||||
asio::buffer(m_response.data(), m_response.size()),
|
||||
[this, self=m_clientConnection.shared_from_this()](std::error_code ec, std::size_t length)
|
||||
{ written(ec, length); });
|
||||
}
|
||||
|
||||
void DebugResponseHandler::written(std::error_code ec, std::size_t length)
|
||||
{
|
||||
ESP_LOGI(TAG, "expected=%zd actual=%zd for %.*s %.*s (%s:%hi)", m_response.size(), length,
|
||||
m_method.size(), m_method.data(), m_path.size(), m_path.data(),
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
m_clientConnection.responseFinished(ec);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <system_error>
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <asio_webserver/responsehandler.h>
|
||||
|
||||
// forward declarations
|
||||
class ClientConnection;
|
||||
|
||||
class DebugResponseHandler : public ResponseHandler
|
||||
{
|
||||
public:
|
||||
DebugResponseHandler(ClientConnection &clientConnection, std::string_view method, std::string_view path, std::string_view protocol);
|
||||
~DebugResponseHandler() override;
|
||||
|
||||
void requestHeaderReceived(std::string_view key, std::string_view value) final;
|
||||
void sendResponse() final;
|
||||
|
||||
private:
|
||||
void written(std::error_code ec, std::size_t length);
|
||||
|
||||
ClientConnection &m_clientConnection;
|
||||
std::string m_method;
|
||||
std::string m_path;
|
||||
std::string m_protocol;
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> m_requestHeaders;
|
||||
|
||||
std::string m_response;
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "errorresponsehandler.h"
|
||||
|
||||
// esp-idf includes
|
||||
#include <asio.hpp>
|
||||
#include <esp_log.h>
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <fmt/core.h>
|
||||
#include <asio_webserver/clientconnection.h>
|
||||
|
||||
namespace {
|
||||
constexpr const char * const TAG = "ASIO_WEBSERVER";
|
||||
} // namespace
|
||||
|
||||
ErrorResponseHandler::ErrorResponseHandler(ClientConnection &clientConnection, std::string_view path) :
|
||||
m_clientConnection{clientConnection},
|
||||
m_path{path}
|
||||
{
|
||||
ESP_LOGI(TAG, "constructed for %.*s (%s:%hi)", path.size(), path.data(),
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
}
|
||||
|
||||
ErrorResponseHandler::~ErrorResponseHandler()
|
||||
{
|
||||
ESP_LOGI(TAG, "destructed for %.*s (%s:%hi)", m_path.size(), m_path.data(),
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
}
|
||||
|
||||
void ErrorResponseHandler::requestHeaderReceived(std::string_view key, std::string_view value)
|
||||
{
|
||||
}
|
||||
|
||||
void ErrorResponseHandler::sendResponse()
|
||||
{
|
||||
ESP_LOGI(TAG, "sending response for %.*s (%s:%hi)", m_path.size(), m_path.data(),
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
|
||||
m_response = fmt::format("Error 404 Not Found: {}", m_path);
|
||||
|
||||
m_response = fmt::format("HTTP/1.1 404 Not Found\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: {}\r\n"
|
||||
"\r\n"
|
||||
"{}", m_response.size(), m_response);
|
||||
|
||||
asio::async_write(m_clientConnection.socket(),
|
||||
asio::buffer(m_response.data(), m_response.size()),
|
||||
[this, self=m_clientConnection.shared_from_this()](std::error_code ec, std::size_t length)
|
||||
{ written(ec, length); });
|
||||
}
|
||||
|
||||
void ErrorResponseHandler::written(std::error_code ec, std::size_t length)
|
||||
{
|
||||
ESP_LOGI(TAG, "expected=%zd actual=%zd for %.*s (%s:%hi)", m_response.size(), length, m_path.size(), m_path.data(),
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
m_clientConnection.responseFinished(ec);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <asio_webserver/responsehandler.h>
|
||||
|
||||
// forward declarations
|
||||
class ClientConnection;
|
||||
|
||||
class ErrorResponseHandler : public ResponseHandler
|
||||
{
|
||||
public:
|
||||
ErrorResponseHandler(ClientConnection &clientConnection, std::string_view path);
|
||||
~ErrorResponseHandler() override;
|
||||
|
||||
void requestHeaderReceived(std::string_view key, std::string_view value) final;
|
||||
void sendResponse() final;
|
||||
|
||||
private:
|
||||
void written(std::error_code ec, std::size_t length);
|
||||
|
||||
ClientConnection &m_clientConnection;
|
||||
std::string m_path;
|
||||
|
||||
std::string m_response;
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "examplewebserver.h"
|
||||
|
||||
#include "rootresponsehandler.h"
|
||||
#include "debugresponsehandler.h"
|
||||
#include "errorresponsehandler.h"
|
||||
|
||||
std::unique_ptr<ResponseHandler> ExampleWebserver::makeResponseHandler(ClientConnection &clientConnection, std::string_view method, std::string_view path, std::string_view protocol)
|
||||
{
|
||||
const std::string_view processedPath{[&](){
|
||||
const auto index = path.find('?');
|
||||
return index == std::string_view::npos ?
|
||||
path : path.substr(0, index);
|
||||
}()};
|
||||
|
||||
if (processedPath.empty() || processedPath == "/")
|
||||
return std::make_unique<RootResponseHandler>(clientConnection);
|
||||
else if (processedPath == "/debug" || processedPath.starts_with("/debug/") )
|
||||
return std::make_unique<DebugResponseHandler>(clientConnection, method, path, protocol);
|
||||
else
|
||||
return std::make_unique<ErrorResponseHandler>(clientConnection, path);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "asio_webserver/webserver.h"
|
||||
|
||||
class ExampleWebserver : public Webserver
|
||||
{
|
||||
public:
|
||||
using Webserver::Webserver;
|
||||
|
||||
std::unique_ptr<ResponseHandler> makeResponseHandler(ClientConnection &clientConnection, std::string_view method, std::string_view path, std::string_view protocol) final;
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
#include <QLoggingCategory>
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "examplewebserver.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
qSetMessagePattern(QStringLiteral("%{time dd.MM.yyyy HH:mm:ss.zzz} "
|
||||
"["
|
||||
"%{if-debug}D%{endif}"
|
||||
"%{if-info}I%{endif}"
|
||||
"%{if-warning}W%{endif}"
|
||||
"%{if-critical}C%{endif}"
|
||||
"%{if-fatal}F%{endif}"
|
||||
"] "
|
||||
"%{function}(): "
|
||||
"%{message}"));
|
||||
|
||||
asio::io_context io_context;
|
||||
ExampleWebserver server{io_context, (short int)8080};
|
||||
|
||||
ESP_LOGI("running mainloop");
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#include "rootresponsehandler.h"
|
||||
|
||||
// esp-idf includes
|
||||
#include <asio.hpp>
|
||||
#include <esp_log.h>
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <fmt/core.h>
|
||||
#include <asio_webserver/clientconnection.h>
|
||||
|
||||
namespace {
|
||||
constexpr const char * const TAG = "ASIO_WEBSERVER";
|
||||
} // namespace
|
||||
|
||||
RootResponseHandler::RootResponseHandler(ClientConnection &clientConnection) :
|
||||
m_clientConnection{clientConnection}
|
||||
{
|
||||
ESP_LOGI(TAG, "constructed for (%s:%hi)",
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
}
|
||||
|
||||
RootResponseHandler::~RootResponseHandler()
|
||||
{
|
||||
ESP_LOGI(TAG, "destructed for (%s:%hi)",
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
}
|
||||
|
||||
void RootResponseHandler::requestHeaderReceived(std::string_view key, std::string_view value)
|
||||
{
|
||||
}
|
||||
|
||||
void RootResponseHandler::sendResponse()
|
||||
{
|
||||
ESP_LOGI(TAG, "sending response for (%s:%hi)",
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
|
||||
m_response = fmt::format("<html>"
|
||||
"<head>"
|
||||
"<title>asio test webserver</title>"
|
||||
"</head>"
|
||||
"<body>"
|
||||
"<h1>asio test webserver</h1>"
|
||||
"<a href=\"/debug\">Debug</a>"
|
||||
"</body>"
|
||||
"</html>");
|
||||
|
||||
m_response = fmt::format("HTTP/1.1 200 Ok\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Content-Length: {}\r\n"
|
||||
"\r\n"
|
||||
"{}", m_response.size(), m_response);
|
||||
|
||||
asio::async_write(m_clientConnection.socket(),
|
||||
asio::buffer(m_response.data(), m_response.size()),
|
||||
[this, self=m_clientConnection.shared_from_this()](std::error_code ec, std::size_t length)
|
||||
{ written(ec, length); });
|
||||
}
|
||||
|
||||
void RootResponseHandler::written(std::error_code ec, std::size_t length)
|
||||
{
|
||||
ESP_LOGI(TAG, "expected=%zd actual=%zd for (%s:%hi)", m_response.size(), length,
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
m_clientConnection.responseFinished(ec);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <asio_webserver/responsehandler.h>
|
||||
|
||||
// forward declarations
|
||||
class ClientConnection;
|
||||
|
||||
class RootResponseHandler : public ResponseHandler
|
||||
{
|
||||
public:
|
||||
RootResponseHandler(ClientConnection &clientConnection);
|
||||
~RootResponseHandler() override;
|
||||
|
||||
void requestHeaderReceived(std::string_view key, std::string_view value) final;
|
||||
void sendResponse() final;
|
||||
|
||||
private:
|
||||
void written(std::error_code ec, std::size_t length);
|
||||
|
||||
ClientConnection &m_clientConnection;
|
||||
|
||||
std::string m_response;
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += core
|
||||
|
||||
CONFIG += c++latest
|
||||
|
||||
HEADERS += \
|
||||
debugresponsehandler.h \
|
||||
errorresponsehandler.h \
|
||||
examplewebserver.h \
|
||||
rootresponsehandler.h
|
||||
|
||||
SOURCES += \
|
||||
debugresponsehandler.cpp \
|
||||
errorresponsehandler.cpp \
|
||||
examplewebserver.cpp \
|
||||
main.cpp \
|
||||
rootresponsehandler.cpp
|
||||
|
||||
unix: TARGET=webserver_example.bin
|
||||
DESTDIR=$${OUT_PWD}/..
|
||||
INCLUDEPATH += $$PWD/..
|
||||
|
||||
include(../paths.pri)
|
||||
|
||||
include(../dependencies.pri)
|
||||
|
||||
unix: {
|
||||
LIBS += -Wl,-rpath=\\\$$ORIGIN
|
||||
}
|
||||
LIBS += -L$${OUT_PWD}/..
|
||||
LIBS += -lasio_webserver
|
||||
Reference in new issue
Block a user