GithubHelp home page GithubHelp logo

tectu / malloy Goto Github PK

View Code? Open in Web Editor NEW
66.0 5.0 8.0 3.49 MB

A cross-platform C++20 library providing embeddable server & client components for HTTP and WebSocket.

License: BSD 3-Clause "New" or "Revised" License

CMake 7.17% C++ 92.83%
cpp-library cpp20-library cpp20 boost beast network-programming http http-server cpp websocket

malloy's People

Contributors

0x00002a avatar madmongo1 avatar tectu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

malloy's Issues

Registerable mime-types

Add an interface to register mime-types.

Something similar to how nginx configuration files allow for http { types { } } .

sub-routing is broken

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.

[Suggestion] Build with all Wall and Werror

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).

README is outdated

The current example for http clients in the readme is outdated. Also should we add a warning about mingw + boost 1.75.0?

controller::make_websocket_connection() return value

@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?

CI: MSVC build messages

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?

Fix hard-coded preflight origin

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:

resp.base().set("Access-Control-Allow-Origin", "http://127.0.0.1:8080");

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.

Can't build with MinGW (only boost 1.75.0)

Since merging #14 I am unable to build using MinGW.

Environment:

  • Windows 10 (64-bit)
  • MinGW-w64 (GCC 10.3.0)
  • Boost 1.76.0
  • Target: 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?

Does the websocket handler need the full `request`?

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?

Improve response::file()

The current response::file() response generator needs to be improved/extended:

  • Allow handling HEAD requests (and GET)
  • Separately provide HTTP doc root path and request target. This way the response generator can verify the sanity of the provided request path and respond accordingly (eg. bad request, I'm a tea pot etc.

Split library into server and client libraries

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.

[Enhancement] Websocket API should allow sending pongs (and possibly pings)

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

WebSocket payload types

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?

Set agent strings properly

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>.

Non string_body requests

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:

  • Only parse the header when we first receive the message. matches for the endpoints can use this instead.
  • Template request in the same manner as response
  • Wait to parse the body until handle in endpoint_http

My current thoughts on the customisation of request setup:

  • Have the 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.
  • If it doesn't have this then just default construct the body (and require that it is default constructable at the add for nice errors)
  • Have connection pass down a "generator" object instead of a request
  • The generator object can give the header of the request and also the body via a templated rvalue method (templated on the type of the body), which reads the remainder of the message from the stream
  • The generator would have to be internal to connection since the stream type differs depending on Derived
  • To cross the 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::visits 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

Possible websocket connection closing bug

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.

Release v0.1

I'd like to start working towards the initial 0.1 release.
This issue should serve as a discussion for figuring out what should happen before the 0.1 release.

@0x00002a any inputs from your side?

Features & Fixes

Add TLS support for websockets

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?

Non string_body responses

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

Question about directory structure

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.

Allow building as shared library

df54240 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.

Re-organize routing

The routing might benefit from some reworking:

  • Introduce endpoint base class
  • Introduce endpoint_http base class

Reparse URIs before passing them to a sub-router

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.

[Enhancement] route_handler concept should include response return type

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).

Should `controller` use `optional` instead of `shared_ptr` for tls context

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?

WebSocket: Calling `stop()` when the connection is already closed.

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:

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?

client http_request() response type

@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?

[Bug] Building the tests should force building the client and server

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.

Cannot init both a server and a client

Currently there is a static guard variable in the root controller init method that means any inits 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?

Exception in `endpoint_http_regex` when invoked from sub-router

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

Support MacOS

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.

Possible bug with sending binary data

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.

Should errors be reported programatically instead of/as well as being logged

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:

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

Create response generator class

Currently, the response class provides several static methods to construct default responses.
Instead, create a dedicated response_generator class.

Remove ciso64 keywords

Replace the cisco64 keywords with their corresponding "old school" variants as some compilers don't support this out of the box.

UB when using the default constructor of router and then adding routes

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

websocket::send() default callback

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){});

Examples static content paths need adjustment

Since merging #67 (merge commit a104278) examples can no longer serve static files as the binaries are now in a different location.

This needs either adjustment of the HTTP doc path or not moving the examples to the new /bin directory.

HTTP client: Automatically follow redirects

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.

WebSocket connection `stop()` naming

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?

[Suggestion] Router shouldn't take the agent string in the constructor

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.

Enforce coding style with clang-format

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.

[Bug] websocket::connection::read does not interleave with websocket::connection::send

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.

Support capture groups in route regex

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.

Type aliases for websocket::connection

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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.