Skip to content

Commit

Permalink
BRAYNS-635 Refactor websocket server. (#1258)
Browse files Browse the repository at this point in the history
  • Loading branch information
Adrien4193 committed May 28, 2024
1 parent 5f271e5 commit e97af8a
Show file tree
Hide file tree
Showing 14 changed files with 1,030 additions and 35 deletions.
17 changes: 0 additions & 17 deletions src/brayns/core/jsonrpc/Messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,21 +127,4 @@ struct JsonObjectReflector<JsonRpcProgress>
return builder.build();
}
};

struct JsonRpcNotification
{
JsonRpcProgress params;
};

template<>
struct JsonObjectReflector<JsonRpcNotification>
{
static auto reflect()
{
auto builder = JsonObjectInfoBuilder<JsonRpcNotification>();
builder.constant("jsonrpc", "2.0");
builder.field("params", [](auto &object) { return &object.params; });
return builder.build();
}
};
}
6 changes: 3 additions & 3 deletions src/brayns/core/jsonv2/JsonValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const JsonArray &getArray(const JsonValue &json)
}
catch (const Poco::Exception &e)
{
throw JsonException(e.displayText());
throw JsonException(e.message());
}
}

Expand All @@ -68,7 +68,7 @@ const JsonObject &getObject(const JsonValue &json)
}
catch (const Poco::Exception &e)
{
throw JsonException(e.displayText());
throw JsonException(e.message());
}
}

Expand All @@ -88,7 +88,7 @@ JsonValue parseJson(const std::string &data)
}
catch (const Poco::Exception &e)
{
throw JsonException(e.displayText());
throw JsonException(e.message());
}
}
}
2 changes: 1 addition & 1 deletion src/brayns/core/utils/Binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ T swapBytes(T value)
inline std::string_view extractBytes(std::string_view &bytes, std::size_t count)
{
auto extracted = bytes.substr(0, count);
bytes.remove_prefix(count);
bytes.remove_prefix(extracted.size());
return extracted;
}

Expand Down
55 changes: 55 additions & 0 deletions src/brayns/core/utils/IdGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* Copyright (c) 2015-2024 EPFL/Blue Brain Project
* All rights reserved. Do not distribute without permission.
*
* Responsible Author: [email protected]
*
* This file is part of Brayns <https://github.com/BlueBrain/Brayns>
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 3.0 as published
* by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#pragma once

#include <vector>

namespace brayns::experimental
{
template<typename T>
class IdGenerator
{
public:
T next()
{
if (_recycled.empty())
{
return _counter++;
}

auto id = _recycled.back();

_recycled.pop_back();

return id;
}

void recycle(T id)
{
_recycled.push_back(id);
}

private:
T _counter = 0;
std::vector<T> _recycled;
};
}
15 changes: 1 addition & 14 deletions src/brayns/core/utils/Log.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,6 @@

#include "Log.h"

#include <iostream>

namespace
{
using namespace brayns;

Logger consoleLogger()
{
auto handler = [](const auto &record) { std::cout << toString(record) << '\n'; };
return Logger("Brayns", LogLevel::Info, handler);
}
}

namespace brayns
{
void Log::setLevel(LogLevel level)
Expand All @@ -46,5 +33,5 @@ void Log::disable()
setLevel(LogLevel::Off);
}

Logger Log::_logger = consoleLogger();
Logger Log::_logger = createConsoleLogger("Brayns");
} // namespace brayns
7 changes: 7 additions & 0 deletions src/brayns/core/utils/Logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "Logger.h"

#include <iostream>
#include <stdexcept>

namespace brayns
Expand Down Expand Up @@ -71,4 +72,10 @@ bool Logger::isEnabled(LogLevel level) const
{
return level >= _level;
}

Logger createConsoleLogger(std::string name)
{
auto handler = [](const auto &record) { std::cout << toString(record) << '\n'; };
return Logger(std::move(name), LogLevel::Info, handler);
}
}
2 changes: 2 additions & 0 deletions src/brayns/core/utils/Logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,6 @@ class Logger
LogLevel _level;
LogHandler _handler;
};

Logger createConsoleLogger(std::string name);
}
161 changes: 161 additions & 0 deletions src/brayns/core/websocket/WebSocket.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/* Copyright (c) 2015-2024 EPFL/Blue Brain Project
* All rights reserved. Do not distribute without permission.
*
* Responsible Author: [email protected]
*
* This file is part of Brayns <https://github.com/BlueBrain/Brayns>
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 3.0 as published
* by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "WebSocket.h"

#include <limits>
#include <optional>

#include <fmt/format.h>

#include <Poco/Buffer.h>
#include <Poco/Net/NetException.h>

namespace
{
using namespace brayns::experimental;

WebSocketFrame receiveFrame(Poco::Net::WebSocket &websocket)
{
auto flags = 0;
auto buffer = Poco::Buffer<char>(0);

websocket.receiveFrame(buffer, flags);

if (flags == 0 && buffer.size() == 0)
{
throw WebSocketClosed("Empty frame received");
}

auto finalFrame = flags & Poco::Net::WebSocket::FRAME_FLAG_FIN;

auto opcode = flags & Poco::Net::WebSocket::FRAME_OP_BITMASK;

return {
.opcode = static_cast<WebSocketOpcode>(opcode),
.data = {buffer.begin(), buffer.size()},
.finalFrame = static_cast<bool>(finalFrame),
};
}

int getFlags(WebSocketOpcode opcode, bool finalFrame)
{
auto flags = 0;

if (finalFrame)
{
flags |= Poco::Net::WebSocket::FRAME_FLAG_FIN;
}

return flags | static_cast<int>(opcode);
}

WebSocketStatus getStatus(int errorCode)
{
switch (errorCode)
{
case Poco::Net::WebSocket::WS_ERR_PAYLOAD_TOO_BIG:
return WebSocketStatus::PayloadTooBig;
case Poco::Net::WebSocket::WS_ERR_INCOMPLETE_FRAME:
return WebSocketStatus::PayloadNotAcceptable;
default:
return WebSocketStatus::UnexpectedCondition;
}
}

WebSocketException websocketException(const Poco::Exception &e)
{
auto status = getStatus(e.code());
const auto &message = e.message();
return WebSocketException(status, message);
}
}

namespace brayns::experimental
{
WebSocketException::WebSocketException(WebSocketStatus status, const std::string &message):
runtime_error(message),
_status(status)
{
}

WebSocketStatus WebSocketException::getStatus() const
{
return _status;
}

WebSocket::WebSocket(const Poco::Net::WebSocket &websocket):
_websocket(websocket)
{
}

std::size_t WebSocket::getMaxFrameSize() const
{
auto size = _websocket.getMaxPayloadSize();
return static_cast<std::size_t>(size);
}

WebSocketFrame WebSocket::receive()
{
try
{
return receiveFrame(_websocket);
}
catch (const Poco::Exception &e)
{
throw websocketException(e);
}
}

void WebSocket::send(const WebSocketFrameView &frame)
{
const auto &[opcode, data, finalFrame] = frame;

auto flags = getFlags(opcode, finalFrame);

if (data.size() > std::numeric_limits<int>::max())
{
throw std::invalid_argument("Payload too big");
}

auto size = static_cast<int>(data.size());

try
{
_websocket.sendFrame(data.data(), size, flags);
}
catch (const Poco::Exception &e)
{
throw websocketException(e);
}
}

void WebSocket::close(WebSocketStatus status, std::string_view message)
{
try
{
_websocket.shutdown(static_cast<Poco::UInt16>(status), std::string(message));
}
catch (...)
{
}
}
}
Loading

0 comments on commit e97af8a

Please sign in to comment.