tectu / malloy Goto Github PK
View Code? Open in Web Editor NEWA cross-platform C++20 library providing embeddable server & client components for HTTP and WebSocket.
License: BSD 3-Clause "New" or "Revised" License
A cross-platform C++20 library providing embeddable server & client components for HTTP and WebSocket.
License: BSD 3-Clause "New" or "Revised" License
Currently there is a static
guard variable in the root controller init
method that means any init
s after the first will always fail. This is an issue if one wants to have multiple servers or clients (or client + server). A use case example is having a server which sometimes needs to make requests to other sites and which creates a temp client to do it. I'm guessing its to prevent multiple io_context
's? Maybe could add a ctor or init
argument that takes in another controller and duplicates the needed state?
In order to keep a websocket session alive, the usual method is to have the server periodically send "pong" frames (done in beast via async_pong
). I suggest adding a send_pong
method to websocket::connection
that would facilitate this behaviour.
imo doing it automatically stretches the "lightweight wrapper" definition a bit, but I'm not particularly opposed to it. nvm theres literally an option for it built into beast
Currently the library does not build with warnings outside of the defaults. It would be nice to have the library build with -Werror -Wpedantic -Wextra -Wall
(and the equivalent flags on windows). Currently it fails mostly on -Werror=reorder
and extra ;
(from -Wpedantic
).
Currently, the response
class provides several static methods to construct default responses.
Instead, create a dedicated response_generator
class.
Add an interface to register mime-types.
Something similar to how nginx configuration files allow for http { types { } }
.
I'm in the process of updating one of my application that consume malloy
to the latest main
branch (the application in question still uses malloy
from before @0x00002a 's heavy rework).
Before the rework, WebSocket connections were hardcoded to use binary mode. This was dropped 72a5678 introduced a configuration option for this instead. Currently I experience problems that the data that the client sends comes in with some partial garbling at the beginning on the server side.
Both client & server use conn->set_binary(true)
immediately as the first statement in the respective handlers for client::controller::ws_connect()
and server::router::add_websocket()
.
I'm currently not sure whether this is a bug in malloy
or whether I am overlooking something obvious.
In my particular case the client sends an XML document as a string over the websocket connection:
Client sends:
<configuration type="foo">
<dummy/>
</configuration>
Server recieves:
â┘ºj☻ αl┘ºj☻ iguration type="foo">
<dummy/>
</configuration>
Relevant client code:
void
handler::run_document_once(
std::string document_contents,
std::function<void(const malloy::error_code, const std::string& msg)>&& handler
)
{
m_malloy_controller->ws_connect(
m_host,
m_port,
"/process/run/once",
[
doc = std::move(document_contents),
handler = std::move(handler)
](const malloy::error_code ec, auto conn) {
conn->set_binary(true);
if (ec) {
std::cerr << "ERROR 1: " << ec.message() << std::endl;
// ToDo
return;
}
conn->send(malloy::buffer(doc), [conn, handler = std::move(handler)](const malloy::error_code& ec, const std::size_t& size){
if (ec) {
std::cerr << "ERROR 2: " << ec.message() << std::endl;
// ToDo
return;
}
auto buffer = std::make_shared<boost::beast::flat_buffer>();
conn->read(*buffer, [buffer, handler = std::move(handler)](auto ec, auto) {
std::cout << "foo1" << std::endl;
handler(ec, malloy::buffers_to_string(malloy::buffer(buffer->cdata(), buffer->size())));
});
});
}
);
}
Relevant server code:
router->add_websocket("/process/run/once", [this](const auto& req, auto conn)
{
conn->set_binary(true);
conn->accept(req, [conn, this](){
spdlog::info("requested: document run once");
auto buffer = std::make_shared<boost::beast::flat_buffer>();
conn->read(*buffer, [buffer, conn, this](auto ec, auto) {
if (ec) {
spdlog::error("error: {}", ec.message());
return;
}
// Extract payload
const std::string payload = malloy::buffers_to_string(malloy::buffer(buffer->cdata(), buffer->size()));
spdlog::warn("payload: \n{}\n", payload);
// ...
});
});
});
});
@0x00002a am I missing something obvious? Are you by any chance using the new, restructured websocket system in any of your applications?
Note: I am aware that in this case I could send the data as text instead. However, eventually protobuf messages are sent over that connection instead. The parsing of the protobuf message on the server side failed so I started investigating and dumb it down to the part where I simply set the XML as a string rather than encapsulating it in a protobuf message.
router::router::generate_preflight_response()
currently uses a hardcoded value for Access-Control-Allow-Origin
. This should be instead replaced by whatever configuration was passed to the controller
:
malloy/lib/malloy/server/routing/router.cpp
Line 169 in beb0cc6
This will require that we also add a scheme field to the controller's configuration as the value for this header field needs to include the scheme if an URL is supplied.
Thinking of it, we should also allow the user of the library to specify other values such as a wildcard.
While upgrading one of my applications to latest malloy
I noticed that something doesn't work correctly with the routing of sub-routers.
Running /example/server/routing/subrouters
confirms this.
I noticed a coding style document has been added. I use clang-format personally which I disabled when working on this project. Only (some of) my changes are clang-formatted (and therefore do wrap at 80 :p). I suggest having a .clang-format file that enforces the style checked in. I've heard you can setup github actions to also run it over pull requests automagically but I'm not sure how (I'll look into it).
If we do add a clang-format file I'd also like to request that we run it over the whole codebase once and check that in, that way I can turn format on save back on without messing up the entire file :p.
So this library uses a single lib
directory which is quite unusual. I'm wondering if there is a specific reason behind this? Not really opposed to it but for packaging purposes for example, having an include
directory would make life easier.
Currently there is no way to have non string_body responses. This is an issue for file serving for example, with multi-GB files it is infeasible to load them into a string before sending. The ideal usage imo would be to have the regex route handler allow returning any response with a valid body. No idea how to actually implement it though :p
WebSocket: The current implementation of connection::read()
does correctly test for a closed connection. However, it calls connection::stop()
when the connection is already closed:
malloy/lib/malloy/websocket/connection.hpp
Line 202 in 218de3b
This is in my opinion incorrect as the connection is already closed at that point.
The corresponding boost::beast
example simply returns from the read
handler: link
@0x00002a Thoughts?
@0x00002a Something that got my attention while upgrading my applications that use malloy
to the latest main
: client::controller::make_websocket_connection()
used to return an std::shared_ptr
pointing to the connection. In the current design the return type is void
instead.
In some of my applications I store the connection
returned in a class member for later use. For example, if I have a server that provides a websocket connection for something like a heartbeat it's nice to keep the connection around so the client can disconnect/close the connection.
One might still grab the connection passed into the handler and store it that way but that seems.... "meh".
I was wondering whether you have something to say on how to handle a situation like this?
The cmake scripts are currently lacking install()
targets and corresponding cpack
configuration to make packaging easier.
Taken from: #10
Would be nice if the regex route supported capture groups, maybe passed in as a vector (to the handler function). Currently I'm doing my own parsing of the request's target in order to grab the bits I need from it. Could wrap the version without the extra args in a lambda before storing it or something.
The current example for http clients in the readme is outdated. Also should we add a warning about mingw + boost 1.75.0?
Currently, we have implemented TLS support for HTTP client & server. However, WebSocket support is currently plain only.
@0x00002a did you by any chance already start working on this when you restructured the websocket connections?
The current response::file()
response generator needs to be improved/extended:
HEAD
requests (and GET
)bad request
, I'm a tea pot
etc.Since merging #14 I am unable to build using MinGW.
Environment:
malloy-example-server-session
(with added -> std::variant<response<>>
return type on router handlers)It seems like @0x00002a is able to build according to #18 (comment)
Using make
:
====================[ Build | malloy-example-server-session | Debug ]===========
C:\Users\joel\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\211.7442.42\bin\cmake\win\bin\cmake.exe --build C:\Users\joel\Documents\projects\malloy\cmake-build-debug --target malloy-example-server-session -- -j 9
...
[ 91%] Building CXX object lib/malloy/CMakeFiles/malloy-objs.dir/controller.cpp.obj
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.3.0/../../../../x86_64-w64-mingw32/bin/as.exe: CMakeFiles\malloy-objs.dir\server\routing\router.cpp.obj: section .rdata$_ZTVN5boost5beast12basic_streamINS_4asio2ip3tcpENS2_9execution12any_executorIJNS5_12context_as_tIRNS2_17execution_contextEEENS5_6detail8blocking7never_tILi0EEENS5_11prefer_onlyINSC_10possibly_tILi0EEEEENSF_INSB_16outstanding_work9tracked_tILi0EEEEENSF_INSJ_11untracked_tILi0EEEEENSF_INSB_12relationship6fork_tILi0EEEEENSF_INSQ_14continuation_tILi0EEEEEEEENS0_21unlimited_rate_policyEE3ops11transfer_opILb0ENS2_15const_buffers_1ENS2_6detail8write_opISZ_NS2_14mutable_bufferEPKS15_NS13_14transfer_all_tENS2_3ssl6detail5io_opISZ_NS1A_8write_opINS0_19buffers_prefix_viewINS0_6detail11buffers_refINS1D_IRKNS0_14buffers_suffixINS0_16buffers_cat_viewIJNS1F_INS1H_IJNS2_12const_bufferES1I_S1I_NS0_4http12basic_fieldsISaIcEE6writer11field_rangeENS1J_10chunk_crlfEEEEEENS1J_6detail10chunk_sizeES1I_S1P_S1I_S1P_S1I_S1I_S1P_EEEEEEEEEEEEENS0_11flat_streamINS19_6streamISZ_EEE3ops8write_opINS1S_13write_some_opINS1S_8write_opINS1S_12write_msg_opINS1E_18bind_front_wrapperIMN6malloy6server4http10connectionINS2E_14connection_tlsEEEFvbNS_6system10error_codeEyEJSt10shared_ptrIS2G_EbEEENS0_10ssl_streamISZ_EELb0ENS1J_15basic_file_bodyINS0_10file_stdioEEES1M_EES2Q_NS1S_18serializer_is_doneELb0ES2T_S1M_EES2Q_Lb0ES2T_S1M_EEEEEEEEEE: string table overflow at offset 10000487
C:\Users\joel\AppData\Local\Temp\ccs8Ilkr.s: Assembler messages:
C:\Users\joel\AppData\Local\Temp\ccs8Ilkr.s: Fatal error: CMakeFiles\malloy-objs.dir\server\routing\router.cpp.obj: file too big
mingw32-make[3]: *** [lib\malloy\CMakeFiles\malloy-objs.dir\build.make:194: lib/malloy/CMakeFiles/malloy-objs.dir/server/routing/router.cpp.obj] Error 1
mingw32-make[2]: *** [CMakeFiles\Makefile2:749: lib/malloy/CMakeFiles/malloy-objs.dir/all] Error 2
mingw32-make[1]: *** [CMakeFiles\Makefile2:979: examples/server/session/CMakeFiles/malloy-example-server-session.dir/rule] Error 2
mingw32-make: *** [Makefile:240: malloy-example-server-session] Error 2
Using ninja
:
====================[ Build | malloy-example-server-session | Debug ]===========
C:\Users\joel\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\211.7442.42\bin\cmake\win\bin\cmake.exe --build C:\Users\joel\Documents\projects\malloy\cmake-build-debug --target malloy-example-server-session
[1/3] Building CXX object examples/server/session/CMakeFiles/malloy-example-server-session.dir/main.cpp.obj
[2/3] Building CXX object lib/malloy/CMakeFiles/malloy-objs.dir/server/routing/router.cpp.obj
FAILED: lib/malloy/CMakeFiles/malloy-objs.dir/server/routing/router.cpp.obj
C:\msys64\mingw64\bin\c++.exe -DBOOST_BEAST_USE_STD_STRING_VIEW -DBOOST_DATE_TIME_NO_LIB -DMALLOY_FEATURE_TLS -DSPDLOG_COMPILED_LIB -DUNICODE -DWIN32_LEAN_AND_MEAN -D_UNICODE -I../lib/malloy/client/3rdparty -I../lib/malloy/.. -I/lib/include -I/lib -I_deps/spdlog-src/include -isystem C:/OpenSSL/include -g -Wa,-mbig-obj -O3 -std=gnu++2a -MD -MT lib/malloy/CMakeFiles/malloy-objs.dir/server/routing/router.cpp.obj -MF lib\malloy\CMakeFiles\malloy-objs.dir\server\routing\router.cpp.obj.d -o lib/malloy/CMakeFiles/malloy-objs.dir/server/routing/router.cpp.obj -c ../lib/malloy/server/routing/router.cpp
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.3.0/../../../../x86_64-w64-mingw32/bin/as.exe: lib/malloy/CMakeFiles/malloy-objs.dir/server/routing/router.cpp.obj: section .rdata$_ZTVN5boost5beast12basic_streamINS_4asio2ip3tcpENS2_9execution12any_executorIJNS5_12context_as_tIRNS2_17execution_contextEEENS5_6detail8blocking7never_tILi0EEENS5_11prefer_onlyINSC_10possibly_tILi0EEEEENSF_INSB_16outstanding_work9tracked_tILi0EEEEENSF_INSJ_11untracked_tILi0EEEEENSF_INSB_12relationship6fork_tILi0EEEEENSF_INSQ_14continuation_tILi0EEEEEEEENS0_21unlimited_rate_policyEE3ops11transfer_opILb0ENS2_15const_buffers_1ENS2_6detail8write_opISZ_NS2_14mutable_bufferEPKS15_NS13_14transfer_all_tENS2_3ssl6detail5io_opISZ_NS1A_8write_opINS0_19buffers_prefix_viewINS0_6detail11buffers_refINS1D_IRKNS0_14buffers_suffixINS0_16buffers_cat_viewIJNS1F_INS1H_IJNS2_12const_bufferES1I_S1I_NS0_4http12basic_fieldsISaIcEE6writer11field_rangeENS1J_10chunk_crlfEEEEEENS1J_6detail10chunk_sizeES1I_S1P_S1I_S1P_S1I_S1I_S1P_EEEEEEEEEEEEENS0_11flat_streamINS19_6streamISZ_EEE3ops8write_opINS1S_13write_some_opINS1S_8write_opINS1S_12write_msg_opINS1E_18bind_front_wrapperIMN6malloy6server4http10connectionINS2E_14connection_tlsEEEFvbNS_6system10error_codeEyEJSt10shared_ptrIS2G_EbEEENS0_10ssl_streamISZ_EELb0ENS1J_15basic_file_bodyINS0_10file_stdioEEES1M_EES2Q_NS1S_18serializer_is_doneELb0ES2T_S1M_EES2Q_Lb0ES2T_S1M_EEEEEEEEEE: string table overflow at offset 10000487
C:\Users\joel\AppData\Local\Temp\ccM9wQke.s: Assembler messages:
C:\Users\joel\AppData\Local\Temp\ccM9wQke.s: Fatal error: lib/malloy/CMakeFiles/malloy-objs.dir/server/routing/router.cpp.obj: file too big
ninja: build stopped: subcommand failed.
As @0x00002a mentioned it might be worth separating things out into dedicated libraries for server & client. I fully agree that this should be done in the future. However, as I understand, the problem is based on the resulting translation unit(s) exceeding limits and I don't think that separating the resulting library components will affect that?
Currently, we build one library containing both server & client components (if enabled).
It might make sense to build separate libraries for the server
components and the client
components.
@0x00002a I've been thinking about something today while on the road (so didn't check actual code): If I remember correctly, the current way that client::controller::http_request()
and the corresponding connections are implemented does not allow defining a callback which accepts malloy::http::response
right?
The HTTP client should provide a setting that allows the user to control whether redirects are followed automatically.
Currently, redirects are not followed automatically.
I'm considering adding this for the initial v0.1
release.
Apparently github uses main
as the default branch. Let's change this to master
.
Currently, router::handle_requests()
modifies the requests target without re-parsing the request's uri
object.
This means that sub-routers can correctly select the appropriate handler but the handlers themselves will receive an uri
object where the resource points to the original, unmodified resource.
The uri
object needs to be reparsed to fix this.
Currently the controller uses a std::shared_ptr
for managing the TLS context. I'm guessing this is so it works with the forward definition. This works but shared_ptr
has a not insignificant overhead compared to a pointer and of course requires dynamic allocation. unique_ptr
would solve the overhead issue, but still has needless dynamic allocation. std::optional
allows lazy construction of the context while ensuring it is stack allocated and serving as an almost drop-in replacement for shared_ptr
.
Is there something I'm missing here?
The routing might benefit from some reworking:
endpoint
base classendpoint_http
base classdf54240 has split the library up into components, which is nice. However, currently it only allows building static libs, since BUILD_SHARED_LIBS
only affects add_library
calls which don't explicitly specify shared or static. I was wondering if there was a specific reason for this? I would quite like to build shared libs to reduce my link time for all the examples (not to mention binary sizes, the debug build for all of them is ~4gb for me combined).
I suggest adding an option()
to malloy like MALLOY_BUILD_SHARED
which toggles BUILD_SHARED_LIBS
internally. If there is a specific reason they need to be static I am curious as to why :p.
The current behaviour of router requires one to pass in an agent string to the ctor every time a new one is constructed. This is gets a bit repetative and also lets the user have different agent strings per subrouter (which may or may not be a feature we want to keep).
I suggest removing that from the constructor and having it as a seperate method, which can be used by add_subrouter
to set the agent string if there isn't one currently (if we want to keep the different string per subrouter an option). Alternatively we could have a default value for the agent string argument, since add_subrouter
would be able to access the private members of the subrouter being added.
As of today, the handler for handling incoming data via a WebSocket connection is defined as:
using handler_t = std::function<void(const malloy::http::request<>&, const std::shared_ptr<connection>&)>;
I've been reading through the corresponding documentation and I couldn't find any hard evidence that payloads send over WebSocket need to be HTTP requests. Any payload expressible as bytes can be sent via WebSocket.
@0x00002a as you introduced this change (before it was std::string
) I was wondering what the rationale behind this is/was?
Currently the full request is parsed and then handed off to the handler on the server side. I'm wondering, do we actually need to do this, or is the header enough? Is it possible that the request body could be anything but empty?
Replace the cisco64 keywords with their corresponding "old school" variants as some compilers don't support this out of the box.
Looking at the build output of the various MSVC based CI actions, I see a lot of these messages:
Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:
- add -D_WIN32_WINNT=0x0601 to the compiler command line; or
- add _WIN32_WINNT=0x0601 to your project's Preprocessor Definitions.
Assuming _WIN32_WINNT=0x0601 (i.e. Windows 7 target).
@0x00002a as you're the MSVC guru here - any input on this?
Using malloy-example-server-websocket
and connecting to the /timer
endpoint does correctly send the 10 messages with 1 second interval. However, it seems like the connection closing is not working as expected.
I am using websocat
as a client. On first glance it seems like the connection is not closed properly.
This needs some investigation and possibly fixing.
server::endponit_http_files
is currently returning a string body:
This should probably be a file body instead.
Currently it is possible to tell cmake to build the tests without either the client, server, or (I'm guessing) both. This results in a linking error since the tests link to both the server and client.
Unless we want to split up the tests (which might prove difficult and which I can see no benefits for, upstream packages don't run the tests on build I'd hope), this should not be an allowed configuration. I suggest having it fail rather than (silently or not) toggle the flags automatically.
This one is a good deal more complex than #13 imo. I've made a start on a possible implementation but its early stages and I might end up scrapping it. The main issue is that request bodies sometimes need some setup before being used (e.g. file_body
needs opening before reading out). How that is actually exposed as a configuration point is one of the sticking points. My ideas on how this can be done outside of handling request setup:
matches
for the endpoints can use this instead.request
in the same manner as response
handle
in endpoint_http
My current thoughts on the customisation of request setup:
Func
passed to add
in router be optionally something which has a function which takes in a header and returns a variant of requests for it.add
for nice errors)connection
pass down a "generator" object instead of a requestconnection
since the stream type differs depending on Derived
endpoint_http
interface boundry the generator will be a variant based on connection_plain
and connection_tls
endpoint_http_regex
will have the pile of std::visit
s needed to sort out the message in handle
endpoint_http_files
will be removed and implemented in terms of endpoint_http_regex
in router
This should allow extensive customisation while keeping the interface simple for the usual use-case. I am concerned about the Fields
option in header
though, this currently does not leave room for that to be custom. Not sure if its in-scope for this library (I've never used them myself) but still worth mentioning. Also currently this does not include websockets which I think also need something similar. Possibly could use the same logic and just pass in request.body()
which would happen to be std::string
by default.
As I said at the beginning, I have an implementation along these lines (not quite complete) and I'd appreciate thoughts and feedback on this
Using a regex endpoint from a sub-router always throws an exception:
terminate called after throwing an instance of 'std::logic_error'
what(): endpoint_http_regex passed request which does not match: /router1/regex?one=sffff&two=asfaf
Simple example snipped to reproduce:
// Subrouter
auto sub_router_1 = std::make_shared<malloy::server::router>();
sub_router_1->add(method::get, R"(^/regex\?one=(\w+)&two=(\w+)$)", [](const auto& req, const std::vector<std::string>& captures){
std::string body;
body += "captures:\n";
for (std::size_t i = 0; i < captures.size(); i++)
body += " " + std::to_string(i) + ": " + captures[i] + "\n";
response resp{ status::ok };
resp.body() = body;
return resp;
});
router->add_subrouter("/router1", sub_router_1);
Test target: http://127.0.0.1:8080/router1/regex?one=sffff&two=asfaf
Personally I don't like to see code like this in an application / outside of a library:
class my_app
{
private:
malloy::websocket::connection<false> m_ws;
};
It is not immediately clear what this boolean template parameter does without further investigation (or simply knowing it).
I would vote for adding type aliases for client & server connections which are defined as connection<true>
and connection<false>
respectively.
@0x00002a any thoughts?
This is mostly for tracking.
MacOS should probably be supported, as clang catches up to msvc and gcc it should be easier/feasible to support clang and by extension mac. I'm currently working on getting to clang to compile malloy
, but even then I doubt it will work straight off as libc++
has very different (and generally cleaner) transitive includes to libstdc++ (and microsofts version too I guess). I can setup clang to use libc++
on my linux box, but theres still differences. Therefore we at least need the CI to build mac and preferably also someone who actually has a mac box who can contribute.
Currently, websocket::connection()
has the following (relevant for this discussion) functions:
make()
connect()
accept()
stop()
read()
send()
I would like to argue that stop()
is not really fitting in this scheme. Personally I would go with renaming it to disconnect()
instead.
@0x00002a Any objections?
In my opinion there are plenty of cases where one might want to send something through a websocket but not necessarily care about the callback. Currently, this requires the user to specify an empty callback handler.
@0x00002a what do you think about providing a default callback handler (an empty one) so a user might just do this:
conn->send(malloy::buffer("foo"));
instead of:
conn->send(malloy::buffer("foo"), [](auto, auto){});
The default constructor of router does not initialise the logger field. This results in attempting to call a method through a null pointer whenever it tries to log. I'm guessing the reason this isn't caught on the tests is that msvc is the only one to reliably abort when this happens (gcc sometimes segfaults but the msvc runtime straight up says no). I haven't run the test suite on msvc yet but I will when I get a chance.
This was rather surprising behaviour, especially since the subrouter example suffers from this (based on the code anyway), I'm guessing it didn't show up since the router uses debug logging and spdlog by default uses info level, so it not printing anything was expected and non-msvc just ignored it. I'm not really sure what the best way to resolve this is, just saying as someone trying out the library, it was surprising.
Mostly a breeze to use so far though, great work- gives me django vibes :p
This is mostly for tracking. I'm currently working on a fix and have a test which repros it (ish, see below)
Currently send
's are queued up and executed in order by the websocket connection, making it much easier for users since they do not need to worry about manually syncing them. However, read
is not interleaved or indeed queued at all, which is somewhat suprisingly behaviour (to me anyway on encountering it). The result of read not being properly interleaved is that it is possible to call send
and then read
but have the read
block waiting for messages before the send ever goes through (which can result in both peers trying to read and the connection shutting down).
Due to the nature of the bug (relies on send not being dispatched before the read call is hit), its a pain to reproduce, and the test I've created only reproduces it some of the time.
Add CI infrastructure to build this library on Windows using MinGW.
This is more of a design question that I just wanted to put out there. Currently the library generally deals with errors by logging them with a user-provided logger and moving on. The use of user-provided loggers allows suppressing or custom reporting of the errors in a customisable way. However, it does not let the user of the library take action if a problem occures, such as being unable to connect (in which case the user may want to try again, for example).
One way to report errors would be through C++'s built-in method of exceptions. I like exceptions personally but I think this is not a great place for them because A) They do not mesh well with async stuff yet (although coroutines will hopefully improve that) and B) Some things are not neccarsarily exceptional for the user code, a server might frequently timeout for example.
Personally I very much like the spdlog
intergration and I don't think that should be removed. But I also think there needs to be some way the user can handle errors. The future/promise
route works well imo, but it is a single-use channel, so for stuff like server::router
thats no good. Another option would be a C# like event
that would allow registering multiple callbacks be fired on call. Then we could attach the logging code to said event, thus allowing the replacement of code which currently logs to instead fire the event, and have the user able to add their own handlers independently.
As I said, I'm hoping for this to be somewhere we can discuss this, if there is already a policy in place I'm not aware of I apologise :p
Stuff that could benifit from this:
malloy/lib/malloy/websocket/stream.hpp
Line 75 in 9eecc9c
It is worth noting that the client-side currently has quite a bit of error feedback currently. http_request
gives an future<error_code>
and the callback for make_websocket_connection
may get invoked on an error (but some are just logged), however the use of std::future
for the reporting mechanism is somewhat problematic because it can't be waited on asynchronously afaik. I think boost::asio::awaitable
could solve this and I will be investigating at some point
This is mostly for tracking
Currently the route_handler
concept (which is used by router::add
to constrain the callback type) only checks for invocable with the request and optionally the capture results. This is an incomplete definition as the type must also return a response<...>
when invocked with said arguments.
The current lack of this additional requirement in the concept means that the user is presented with a rather obscure template error about void
if their function does not have a return type (e.g. a default lambda) and which I spent a few minutes staring at before I realised the issue (bearing in mind I wrote that code in the first place :p).
We should provide the user with the option of controlling whether message opcodes are set as text
or binary
.
See https://www.boost.org/doc/libs/develop/libs/beast/doc/html/beast/ref/boost__beast__websocket__stream/binary/overload1.html
Currently, agent strings are all over the place. This needs some cleaning up / more consistency.
We want to have something like malloy-client <version>
and malloy-server <version>
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.