Compare commits

...

2 Commits

Author SHA1 Message Date
Kurt Sassenrath
ba2c95ca0a Initial clang-format file 2023-10-19 16:19:50 -07:00
Kurt Sassenrath
724b426d44 WIP: Session decoupling from server impl 2023-10-17 16:13:55 -07:00
8 changed files with 453 additions and 29 deletions

45
.clang-format Normal file
View File

@ -0,0 +1,45 @@
BasedOnStyle: LLVM
---
Language: Cpp
AlignAfterOpenBracket: DontAlign
AlignOperands: AlignAfterOperator
AllowShortCaseLabelsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: Yes
BraceWrapping:
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BracedInitializerIndentWidth: 4
BreakBeforeBinaryOperators: NonAssignment
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeComma
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
IncludeBlocks: Regroup
InIncludeCategories:
- Regex: '^"parselink/'
Priority: 3
SortPriority: 3
CaseSensitive: false
- Regex: '^<(fmt|tl|magic_enum|ut)/'
Priority: 2
SortPriority: 2
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
IncludeIsMainRegex: '(_test)?$'
IndentCaseLabels: true
IndentGotoLabels: false
IndentWidth: 4
InsertNewlineAtEOF: true
KeepEmptyLinesAtEOF: true
KeepEmptyLinesAtTheStartOfBlocks: false
PointerAlignment: Left
QualifierAlignment: Right
QualifierOrder: ['inline', 'static', 'constexpr', 'const', 'volatile']
ReferenceAlignment: Pointer
---

View File

@ -19,7 +19,7 @@ cc_library(
name = "proto",
hdrs = glob(["proto/**/*.h"]),
includes = ["."],
deps = ["//include:path"],
deps = ["@expected", "//include:path"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,65 @@
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Proto
//
// Session management for the "user" protocol.
//
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
#ifndef session_07eae057feface79
#define session_07eae057feface79
#include <tl/expected.hpp>
#include <cstdint>
#include <span>
#include <string>
namespace parselink {
namespace proto {
enum class error {
system_error,
incomplete,
bad_data,
too_large,
};
// Structure containing header information parsed from a buffer.
struct header_info {
std::uint32_t message_size; // Size of the message, minus the header.
std::uint32_t bytes_read; // How many bytes of the buffer were used.
};
class session {
public:
session() = default;
// Parse the protocol header out of a buffer. This is a member function
// as we may choose to omit data once a session is established.
tl::expected<header_info, error> parse_header(
std::span<std::byte const> buffer) noexcept;
// The maximum size of a single message. Should probably depend on whether
// the session is established or not.
constexpr static std::uint32_t max_message_size = 128 * 1024;
private:
std::string user_id;
};
} // namespace proto
} // namespace parselink
#endif // session_0c61530748b9f966

238
llvm.clangd-format Normal file
View File

@ -0,0 +1,238 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseColons: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAfterAttributes: Never
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
KeepEmptyLinesAtEOF: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
PPIndentWidth: -1
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
...

View File

@ -15,9 +15,9 @@ cc_binary(
deps = [
"headers",
"//include/parselink:msgpack",
"//include/parselink:proto",
"//include/parselink:utility",
"//source/logging",
"//source/proto",
"@boost//:beast",
],
)

17
source/proto/BUILD Normal file
View File

@ -0,0 +1,17 @@
# parselink
cc_library(
name = "proto",
srcs = [
"session.cpp",
],
deps = [
"//include/parselink:proto",
"//include/parselink:msgpack",
"//source/logging",
],
visibility = [
# TODO: Fix visibility
"//visibility:public",
],
)

57
source/proto/session.cpp Normal file
View File

@ -0,0 +1,57 @@
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Server
//
// Server implementation. Currently, a monolithic server which:
// * Communicates with users via TCP (msgpack).
// * Runs the websocket server for overlays to read.
//
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
#include "parselink/proto/session.h"
#include "parselink/logging.h"
#include "parselink/msgpack/token.h"
using namespace parselink;
using namespace parselink::proto;
namespace {
logging::logger logger("session");
}
tl::expected<header_info, error> session::parse_header(
std::span<std::byte const> buffer) noexcept {
auto reader = msgpack::token_reader(buffer);
auto magic = reader.read_one();
if (!magic || *magic != "prs") {
logger.error("Failed to parse magic");
return tl::unexpected(magic ? error::bad_data : error::incomplete);
}
auto size = reader.read_one().and_then(
[](auto t){ return t.template get<std::uint32_t>(); });
if (!size || !*size) {
logger.error("Failed to get valid message size");
return tl::unexpected(magic ? error::bad_data : error::incomplete);
}
if (*size > max_message_size) {
logger.error("Message {} exceeds max size {}", *size, max_message_size);
return tl::unexpected(error::too_large);
}
std::uint32_t amt = std::distance(buffer.begin(), reader.current());
return tl::expected<header_info, error>(tl::in_place, *size, amt);
}

View File

@ -23,7 +23,7 @@
#include "parselink/msgpack/token/reader.h"
#include "parselink/msgpack/token/views.h"
#include "parselink/proto/message.h"
#include "parselink/proto/session.h"
#include <fmt/ranges.h>
@ -53,12 +53,6 @@ using net::use_awaitable;
using net::deferred;
using net::detached;
enum class error {
system,
msgpack,
};
//-----------------------------------------------------------------------------
// TODO(ksassenrath): These are logging formatters for various boost/asio types.
// Not all code is exposed to them, so they cannot be defined inside the
@ -280,15 +274,14 @@ public:
}
awaitable<tl::expected<std::monostate, boost::system::error_code>>
buffer_message(std::vector<std::byte>& buffer) noexcept {
auto amt = buffer.size();
auto total = buffer.capacity();
buffer.resize(total);
buffer_message(std::span<std::byte> buffer) noexcept {
while (amt < total) {
auto subf = std::span(buffer.begin() + amt, buffer.end());
std::size_t amt = 0;
while (amt < buffer.size()) {
auto subsp = buffer.subspan(amt);
auto [ec, n] = co_await socket_.async_read_some(
net::buffer(subf), no_ex_coro);
net::buffer(subsp), no_ex_coro);
logger.debug("Read {} bytes, total is now {}", n, amt + n);
if (ec || n == 0) {
logger.error("Reading from user socket failed: {}", ec);
@ -299,8 +292,7 @@ public:
co_return std::monostate{};
}
awaitable<tl::expected<std::vector<std::byte>, boost::system::error_code>>
await_message() noexcept {
awaitable<void> await_connect() noexcept {
// Use a small buffer on the stack to read the initial header.
std::array<std::byte, 8> buffer;
auto [ec, n] = co_await socket_.async_read_some(
@ -308,29 +300,38 @@ public:
if (ec) {
logger.error("Reading from user socket failed: {}", ec);
co_return tl::make_unexpected(ec);
co_return;
}
logger.debug("Read {} bytes from client: {}", n,
std::span(buffer.data(), n));
auto hdr = parse_header(std::span(buffer.data(), n));
if (!hdr) {
logger.error("Unable to parse header: {}", hdr.error());
co_return tl::make_unexpected(boost::system::error_code: );
proto::session session;
auto maybe_hdr = session.parse_header(std::span(buffer.data(), n));
if (!maybe_hdr) {
logger.error("Unable to parse header: {}", maybe_hdr.error());
co_return;
}
auto msg = std::move(*hdr);
// TODO(ksassenrath): Replace with specialized allocator.
auto msg = std::vector<std::byte>(maybe_hdr->message_size);
if (auto result = co_await buffer_message(msg); !result) {
// Copy remaining portion of message in initial read to the message
// buffer.
std::copy(
std::next(buffer.begin(), maybe_hdr->bytes_read),
std::next(buffer.begin(), n),
msg.begin());
auto msg_span = std::span(msg.begin() + n - maybe_hdr->bytes_read, msg.end());
if (auto result = co_await buffer_message(msg_span); !result) {
logger.error("Unable to parse header: {}", result.error());
co_return;
}
}
awaitable<bool> await_connect() noexcept {
auto reader = msgpack::token_reader(msg);
std::array<msgpack::token, 32> tokens;
auto maybe_user = reader.read_some(tokens)
@ -345,7 +346,7 @@ public:
// Authenticate against database.
logger.debug("User {} established connection", maybe_user->user_id);
session_ = server_.establish_session(std::move(*maybe_user));
//session_ = server_.establish_session(std::move(*maybe_user));
}
enum class state {
@ -401,6 +402,7 @@ user_session* monolithic_server::establish_session(user_session session) {
active_user_sessions_.emplace(session.user_id,
std::move(session.user_id));
}
return {};
}
std::unique_ptr<server> parselink::make_server(std::string_view address,