Preperations for websocket
This commit is contained in:
@ -64,6 +64,14 @@ void ClientConnection::responseFinished(std::error_code ec)
|
||||
m_socket.close();
|
||||
}
|
||||
|
||||
void ClientConnection::upgradeWebsocket()
|
||||
{
|
||||
// ESP_LOGD(TAG, "state changed to RequestLine");
|
||||
m_state = State::WebSocket;
|
||||
|
||||
doReadWebSocket();
|
||||
}
|
||||
|
||||
void ClientConnection::doRead()
|
||||
{
|
||||
m_socket.async_read_some(asio::buffer(m_receiveBuffer, max_length),
|
||||
@ -71,6 +79,13 @@ void ClientConnection::doRead()
|
||||
{ readyRead(ec, length); });
|
||||
}
|
||||
|
||||
void ClientConnection::doReadWebSocket()
|
||||
{
|
||||
m_socket.async_read_some(asio::buffer(m_receiveBuffer, max_length),
|
||||
[this, self=shared_from_this()](std::error_code ec, std::size_t length)
|
||||
{ readyReadWebSocket(ec, length); });
|
||||
}
|
||||
|
||||
void ClientConnection::readyRead(std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (ec)
|
||||
@ -119,7 +134,7 @@ requestFinished:
|
||||
}
|
||||
}
|
||||
|
||||
// ESP_LOGV(TAG, "received: %zd \"%.*s\"", length, length, m_data);
|
||||
// ESP_LOGV(TAG, "received: %zd \"%.*s\"", length, length, m_receiveBuffer);
|
||||
m_parsingBuffer.append(m_receiveBuffer, length);
|
||||
|
||||
bool shouldDoRead{true};
|
||||
@ -145,6 +160,20 @@ requestFinished:
|
||||
doRead();
|
||||
}
|
||||
|
||||
void ClientConnection::readyReadWebSocket(std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (ec)
|
||||
{
|
||||
ESP_LOGI(TAG, "error: %i %s (%s:%hi)", ec.value(), ec.message().c_str(),
|
||||
m_remote_endpoint.address().to_string().c_str(), m_remote_endpoint.port());
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "received: %zd \"%.*s\"", length, length, m_receiveBuffer);
|
||||
|
||||
doReadWebSocket();
|
||||
}
|
||||
|
||||
bool ClientConnection::readyReadLine(std::string_view line)
|
||||
{
|
||||
switch (m_state)
|
||||
|
@ -27,10 +27,13 @@ public:
|
||||
|
||||
void start();
|
||||
void responseFinished(std::error_code ec);
|
||||
void upgradeWebsocket();
|
||||
|
||||
private:
|
||||
void doRead();
|
||||
void doReadWebSocket();
|
||||
void readyRead(std::error_code ec, std::size_t length);
|
||||
void readyReadWebSocket(std::error_code ec, std::size_t length);
|
||||
bool parseRequestLine(std::string_view line);
|
||||
bool readyReadLine(std::string_view line);
|
||||
bool parseRequestHeader(std::string_view line);
|
||||
@ -39,12 +42,12 @@ private:
|
||||
asio::ip::tcp::socket m_socket;
|
||||
const asio::ip::tcp::endpoint m_remote_endpoint;
|
||||
|
||||
static constexpr const std::size_t max_length = 128;
|
||||
static constexpr const std::size_t max_length = 1024;
|
||||
char m_receiveBuffer[max_length];
|
||||
|
||||
std::string m_parsingBuffer;
|
||||
|
||||
enum class State { RequestLine, RequestHeaders, RequestBody, Response };
|
||||
enum class State { RequestLine, RequestHeaders, RequestBody, Response, WebSocket };
|
||||
State m_state { State::RequestLine };
|
||||
|
||||
std::size_t m_requestBodySize{};
|
||||
|
@ -39,7 +39,7 @@ void ChunkedResponseHandler::sendResponse()
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
|
||||
m_response = fmt::format("HTTP/1.1 200 Ok\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Connection: keep-alive\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n");
|
||||
|
@ -103,6 +103,13 @@ void DebugResponseHandler::sendResponse()
|
||||
"<button type=\"submit\">Go</button>"
|
||||
"</fieldset>"
|
||||
"</form>"
|
||||
"<form method=\"POST\" enctype=\"multipart/form-data\">"
|
||||
"<fieldset>"
|
||||
"<legend>POST form with multipart form-data and file upload</legend>"
|
||||
"<label>File-Upload: <input type=\"file\" name=\"inputName\" /></label>"
|
||||
"<button type=\"submit\">Go</button>"
|
||||
"</fieldset>"
|
||||
"</form>"
|
||||
"</body>"
|
||||
"</html>";
|
||||
|
||||
|
@ -42,7 +42,7 @@ void ErrorResponseHandler::sendResponse()
|
||||
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"
|
||||
"Connection: keep-alive\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: {}\r\n"
|
||||
"\r\n"
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "debugresponsehandler.h"
|
||||
#include "chunkedresponsehandler.h"
|
||||
#include "errorresponsehandler.h"
|
||||
#include "websocketfrontendresponsehandler.h"
|
||||
#include "websocketbackendresponsehandler.h"
|
||||
|
||||
namespace {
|
||||
constexpr const char * const TAG = "ASIO_WEBSERVER";
|
||||
@ -15,6 +17,8 @@ constexpr const char * const TAG = "ASIO_WEBSERVER";
|
||||
|
||||
std::unique_ptr<ResponseHandler> ExampleWebserver::makeResponseHandler(ClientConnection &clientConnection, std::string_view method, std::string_view path, std::string_view protocol)
|
||||
{
|
||||
ESP_LOGI(TAG, "method=\"%.*s\" path=\"%.*s\" protocol=\"%.*s\"",
|
||||
method.size(), method.data(), path.size(), path.data(), protocol.size(), protocol.data());
|
||||
const std::string_view processedPath{[&](){
|
||||
const auto index = path.find('?');
|
||||
return index == std::string_view::npos ?
|
||||
@ -27,6 +31,10 @@ std::unique_ptr<ResponseHandler> ExampleWebserver::makeResponseHandler(ClientCon
|
||||
return std::make_unique<DebugResponseHandler>(clientConnection, method, path, protocol);
|
||||
else if (processedPath == "/chunked")
|
||||
return std::make_unique<ChunkedResponseHandler>(clientConnection);
|
||||
else if (processedPath == "/websocket")
|
||||
return std::make_unique<WebsocketFrontendResponseHandler>(clientConnection);
|
||||
else if (processedPath == "/ws")
|
||||
return std::make_unique<WebsocketBackendResponseHandler>(clientConnection);
|
||||
else
|
||||
return std::make_unique<ErrorResponseHandler>(clientConnection, path);
|
||||
}
|
||||
|
@ -38,21 +38,22 @@ 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>"
|
||||
"<ul>"
|
||||
"<li><a href=\"/debug\">Debug</a></li>"
|
||||
"<li><a href=\"/chunked\">Chunked</a></li>"
|
||||
"</ul>"
|
||||
"</body>"
|
||||
"</html>");
|
||||
m_response = "<html>"
|
||||
"<head>"
|
||||
"<title>asio test webserver</title>"
|
||||
"</head>"
|
||||
"<body>"
|
||||
"<h1>asio test webserver</h1>"
|
||||
"<ul>"
|
||||
"<li><a href=\"/debug\">Debug</a></li>"
|
||||
"<li><a href=\"/chunked\">Chunked</a></li>"
|
||||
"<li><a href=\"/websocket\">Websocket</a></li>"
|
||||
"</ul>"
|
||||
"</body>"
|
||||
"</html>";
|
||||
|
||||
m_response = fmt::format("HTTP/1.1 200 Ok\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Connection: keep-alive\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Content-Length: {}\r\n"
|
||||
"\r\n"
|
||||
|
@ -9,7 +9,9 @@ HEADERS += \
|
||||
debugresponsehandler.h \
|
||||
errorresponsehandler.h \
|
||||
examplewebserver.h \
|
||||
rootresponsehandler.h
|
||||
rootresponsehandler.h \
|
||||
websocketbackendresponsehandler.h \
|
||||
websocketfrontendresponsehandler.h
|
||||
|
||||
SOURCES += \
|
||||
chunkedresponsehandler.cpp \
|
||||
@ -17,7 +19,9 @@ SOURCES += \
|
||||
errorresponsehandler.cpp \
|
||||
examplewebserver.cpp \
|
||||
main.cpp \
|
||||
rootresponsehandler.cpp
|
||||
rootresponsehandler.cpp \
|
||||
websocketbackendresponsehandler.cpp \
|
||||
websocketfrontendresponsehandler.cpp
|
||||
|
||||
unix: TARGET=webserver_example.bin
|
||||
DESTDIR=$${OUT_PWD}/..
|
||||
@ -32,3 +36,5 @@ unix: {
|
||||
}
|
||||
LIBS += -L$${OUT_PWD}/..
|
||||
LIBS += -lasio_webserver
|
||||
|
||||
LIBS += -lssl -lcrypto
|
||||
|
153
test/webserver_example/websocketbackendresponsehandler.cpp
Normal file
153
test/webserver_example/websocketbackendresponsehandler.cpp
Normal file
@ -0,0 +1,153 @@
|
||||
#include "websocketbackendresponsehandler.h"
|
||||
|
||||
// system includes
|
||||
#include <openssl/sha.h>
|
||||
|
||||
// esp-idf includes
|
||||
#include <asio.hpp>
|
||||
#include <esp_log.h>
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <fmt/core.h>
|
||||
#include <asio_webserver/clientconnection.h>
|
||||
#include <strutils.h>
|
||||
|
||||
namespace {
|
||||
constexpr const char * const TAG = "ASIO_WEBSERVER";
|
||||
} // namespace
|
||||
|
||||
WebsocketBackendResponseHandler::WebsocketBackendResponseHandler(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());
|
||||
}
|
||||
|
||||
WebsocketBackendResponseHandler::~WebsocketBackendResponseHandler()
|
||||
{
|
||||
ESP_LOGI(TAG, "destructed for (%s:%hi)",
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
}
|
||||
|
||||
void WebsocketBackendResponseHandler::requestHeaderReceived(std::string_view key, std::string_view value)
|
||||
{
|
||||
// ESP_LOGV(TAG, "key=\"%.*s\" value=\"%.*s\"", key.size(), key.data(), value.size(), value.data());
|
||||
|
||||
if (cpputils::stringEqualsIgnoreCase(key, "Connection"))
|
||||
{
|
||||
m_connectionUpgrade = cpputils::stringEqualsIgnoreCase(value, "Upgrade") ||
|
||||
value.contains("Upgrade");
|
||||
}
|
||||
else if (cpputils::stringEqualsIgnoreCase(key, "Upgrade"))
|
||||
{
|
||||
m_upgradeWebsocket = cpputils::stringEqualsIgnoreCase(value, "websocket");
|
||||
}
|
||||
else if (cpputils::stringEqualsIgnoreCase(key, "Sec-WebSocket-Version"))
|
||||
{
|
||||
m_secWebsocketVersion = value;
|
||||
}
|
||||
else if (cpputils::stringEqualsIgnoreCase(key, "Sec-WebSocket-Key"))
|
||||
{
|
||||
m_secWebsocketKey = value;
|
||||
}
|
||||
else if (cpputils::stringEqualsIgnoreCase(key, "Sec-WebSocket-Extensions"))
|
||||
{
|
||||
m_secWebsocketExtensions = value;
|
||||
}
|
||||
}
|
||||
|
||||
void WebsocketBackendResponseHandler::requestBodyReceived(std::string_view body)
|
||||
{
|
||||
}
|
||||
|
||||
void WebsocketBackendResponseHandler::sendResponse()
|
||||
{
|
||||
ESP_LOGI(TAG, "sending response for (%s:%hi)",
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
|
||||
const auto sendErrorResponse = [&](std::string_view message){
|
||||
ESP_LOGW(TAG, "%.*s", message.size(), message.data());
|
||||
|
||||
m_response = fmt::format("HTTP/1.1 400 Bad Request\r\n"
|
||||
"Connection: keep-alive\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: {}\r\n"
|
||||
"\r\n"
|
||||
"{}", message.size(), message);
|
||||
|
||||
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)
|
||||
{ writtenError(ec, length); });
|
||||
};
|
||||
|
||||
if (!m_connectionUpgrade)
|
||||
{
|
||||
sendErrorResponse("Only websocket clients are allowed on this endpoint (header missing Connection: Upgrade)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_upgradeWebsocket)
|
||||
{
|
||||
sendErrorResponse("Only websocket clients are allowed on this endpoint (header missing Upgrade: websocket)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_secWebsocketKey.empty())
|
||||
{
|
||||
sendErrorResponse("Only websocket clients are allowed on this endpoint (header missing Sec-WebSocket-Key)");
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr std::string_view magic_uuid{"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"};
|
||||
m_secWebsocketKey.append(magic_uuid);
|
||||
|
||||
unsigned char hash[SHA_DIGEST_LENGTH]; // == 20
|
||||
SHA1((const unsigned char *)m_secWebsocketKey.data(), m_secWebsocketKey.size(), hash);
|
||||
|
||||
const auto base64Hash = cpputils::toBase64String({hash, SHA_DIGEST_LENGTH});
|
||||
|
||||
m_response = fmt::format("HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Accept: {}\r\n"
|
||||
"Sec-WebSocket-Protocol: chat\r\n"
|
||||
"\r\n", base64Hash);
|
||||
|
||||
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 WebsocketBackendResponseHandler::writtenError(std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (ec)
|
||||
{
|
||||
ESP_LOGW(TAG, "error: %i (%s:%hi)", ec.value(),
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
m_clientConnection.responseFinished(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void WebsocketBackendResponseHandler::written(std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (ec)
|
||||
{
|
||||
ESP_LOGW(TAG, "error: %i (%s:%hi)", ec.value(),
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
m_clientConnection.responseFinished(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
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.upgradeWebsocket();
|
||||
}
|
39
test/webserver_example/websocketbackendresponsehandler.h
Normal file
39
test/webserver_example/websocketbackendresponsehandler.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#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 WebsocketBackendResponseHandler final : public ResponseHandler
|
||||
{
|
||||
public:
|
||||
WebsocketBackendResponseHandler(ClientConnection &clientConnection);
|
||||
~WebsocketBackendResponseHandler() override;
|
||||
|
||||
void requestHeaderReceived(std::string_view key, std::string_view value) final;
|
||||
void requestBodyReceived(std::string_view body) final;
|
||||
void sendResponse() final;
|
||||
|
||||
private:
|
||||
void writtenError(std::error_code ec, std::size_t length);
|
||||
void written(std::error_code ec, std::size_t length);
|
||||
|
||||
ClientConnection &m_clientConnection;
|
||||
|
||||
std::string m_response;
|
||||
|
||||
bool m_connectionUpgrade{};
|
||||
bool m_upgradeWebsocket{};
|
||||
std::string m_secWebsocketVersion;
|
||||
std::string m_secWebsocketKey;
|
||||
std::string m_secWebsocketExtensions;
|
||||
};
|
170
test/webserver_example/websocketfrontendresponsehandler.cpp
Normal file
170
test/webserver_example/websocketfrontendresponsehandler.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
#include "websocketfrontendresponsehandler.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
|
||||
|
||||
WebsocketFrontendResponseHandler::WebsocketFrontendResponseHandler(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());
|
||||
}
|
||||
|
||||
WebsocketFrontendResponseHandler::~WebsocketFrontendResponseHandler()
|
||||
{
|
||||
ESP_LOGI(TAG, "destructed for (%s:%hi)",
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
}
|
||||
|
||||
void WebsocketFrontendResponseHandler::requestHeaderReceived(std::string_view key, std::string_view value)
|
||||
{
|
||||
}
|
||||
|
||||
void WebsocketFrontendResponseHandler::requestBodyReceived(std::string_view body)
|
||||
{
|
||||
}
|
||||
|
||||
void WebsocketFrontendResponseHandler::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 = R"END(
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Websocket test</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Websocket test</h1>
|
||||
|
||||
<div style="border: 1px solid black;">
|
||||
<button id="connectButton">Connect</button>
|
||||
<span id="statusSpan">Not connected</span>
|
||||
</div>
|
||||
|
||||
<form id="sendForm">
|
||||
<fieldset>
|
||||
<legend>Send msg</legend>
|
||||
<input type="text" id="sendInput" />
|
||||
<button type="submit">Send</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<pre id="logOutput"></pre>
|
||||
|
||||
<script>
|
||||
var websocket = null;
|
||||
|
||||
const connectButton = document.getElementById('connectButton');
|
||||
const statusSpan = document.getElementById('statusSpan');
|
||||
const sendForm = document.getElementById('sendForm');
|
||||
const sendInput = document.getElementById('sendInput');
|
||||
const logOutput = document.getElementById('logOutput');
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
console.log('loaded');
|
||||
connectButton.addEventListener('click', connectButtonClicked);
|
||||
sendForm.addEventListener('submit', sendMsg);
|
||||
});
|
||||
|
||||
function logLine(msg) {
|
||||
logOutput.appendChild(document.createTextNode(msg + "\n"));
|
||||
}
|
||||
|
||||
function connectButtonClicked() {
|
||||
console.log('clicked');
|
||||
if (websocket === null) {
|
||||
connectButton.textContent = 'Disconnect';
|
||||
|
||||
const url = (window.location.protocol === 'https:' ? 'wss://' : 'ws://') + window.location.host + '/ws';
|
||||
|
||||
logLine('Connecting to ' + url);
|
||||
|
||||
statusSpan.textContent = "Connecting...";
|
||||
|
||||
websocket = new WebSocket(url);
|
||||
websocket.onopen = function (event) {
|
||||
statusSpan.textContent = "Connected";
|
||||
logLine('Connected');
|
||||
};
|
||||
websocket.onclose = function(event) {
|
||||
statusSpan.textContent = "Lost connection";
|
||||
logLine('Lost connection');
|
||||
};
|
||||
websocket.onerror = function(event) {
|
||||
statusSpan.textContent = "Error occured";
|
||||
logLine('Error occured');
|
||||
};
|
||||
websocket.onmessage = function(event) {
|
||||
if (typeof event.data === 'string' || event.data instanceof String) {
|
||||
logLine('Received text message: ' + event.data);
|
||||
} else if (typeof event.data == 'object') {
|
||||
logLine('Received binary message');
|
||||
} else {
|
||||
logLine('Received unknown message');
|
||||
}
|
||||
};
|
||||
} else {
|
||||
connectButton.textContent = 'Connect';
|
||||
|
||||
websocket.close();
|
||||
websocket = null;
|
||||
}
|
||||
}
|
||||
|
||||
function sendMsg(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
if (websocket === null) {
|
||||
alert('not connected!');
|
||||
return;
|
||||
}
|
||||
|
||||
websocket.send(sendInput.value);
|
||||
logLine('Sent text message: ' + sendInput.value);
|
||||
sendInput.value = '';
|
||||
sendInput.focus();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
)END";
|
||||
|
||||
m_response = fmt::format("HTTP/1.1 200 Ok\r\n"
|
||||
"Connection: keep-alive\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 WebsocketFrontendResponseHandler::written(std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (ec)
|
||||
{
|
||||
ESP_LOGW(TAG, "error: %i (%s:%hi)", ec.value(),
|
||||
m_clientConnection.remote_endpoint().address().to_string().c_str(), m_clientConnection.remote_endpoint().port());
|
||||
m_clientConnection.responseFinished(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
32
test/webserver_example/websocketfrontendresponsehandler.h
Normal file
32
test/webserver_example/websocketfrontendresponsehandler.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#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 WebsocketFrontendResponseHandler final : public ResponseHandler
|
||||
{
|
||||
public:
|
||||
WebsocketFrontendResponseHandler(ClientConnection &clientConnection);
|
||||
~WebsocketFrontendResponseHandler() override;
|
||||
|
||||
void requestHeaderReceived(std::string_view key, std::string_view value) final;
|
||||
void requestBodyReceived(std::string_view body) final;
|
||||
void sendResponse() final;
|
||||
|
||||
private:
|
||||
void written(std::error_code ec, std::size_t length);
|
||||
|
||||
ClientConnection &m_clientConnection;
|
||||
|
||||
std::string m_response;
|
||||
};
|
Reference in New Issue
Block a user