Commit Graph

82 Commits

Author SHA1 Message Date
Thomas Winget 85437d167b initial implementation of generic pub/sub management
Implements a generic pub/sub system for RPC endpoints to allow clients
to subscribe to things.

patch version bump

tests included and passing
2022-09-28 15:43:45 -04:00
Jason Rhinelander edcde9246a
Fix zmq socket limit setting
MAX_SOCKETS wasn't working properly because ZMQ uses it when the context
is initialized, which happens when the first socket is constructed on
that context.

For OxenMQ, we had several sockets constructed on the context during
OxenMQ construction, which meant the context_t was being initialized
during OxenMQ construction, rather than during start(), and so setting
MAX_SOCKETS would have no effect and you'd always get the default.

This fixes it by making all the member variable zmq::socket_t's
default-constructed, then replacing them with proper zmq::socket_t's
during startup() so that we also defer zmq::context_t initialization to
the right place.

A second issue found during testing (also fixed here) is that the socket
worker threads use to communicate to the proxy could fail if the worker
socket creation would violate the zmq max sockets limit, which wound up
throwing an uncaught exception and aborting.  This pre-initializes (but
doesn't connect) all potential worker threads sockets during start() so
that the lazily-initialized worker thread will have one already set up
rather than having to create a new one (which could fail).
2022-08-05 10:40:01 -03:00
Jason Rhinelander 045df9cb9b
Use oxen-encoding and add compatibility shim headers
bt_*, hex, base32z, base64 all moved to oxen-encoding a while ago; this
finishes the move by removing them from oxenmq and instead making oxenmq
depend on oxen-encoding.
2022-01-18 10:30:23 -04:00
Jason Rhinelander 430951bf3c
Merge pull request #66 from jagerman/address-hashing
Add std::hash implementation for oxenmq::address
2021-11-30 14:21:24 -04:00
Jason Rhinelander 375cfab4ce Rebrand variables LMQ -> OMQ
Various things still were using the lmq (or LMQ) names; change them all
to omq/OMQ.
2021-11-30 14:10:47 -04:00
Jason Rhinelander 31f64821f8 Add std::hash implementation for oxenmq::address
So that you can store addresses in unordered_sets _maps.
2021-11-28 10:26:35 -04:00
Jason Rhinelander 519a107542
Merge pull request #53 from jagerman/allow-disabling-werror
Allow disabling -Werror via a cmake option
2021-10-14 11:43:10 -03:00
Jason Rhinelander 23c2d537a3 Maybe fix macos tests? 2021-10-14 11:10:28 -03:00
Jason Rhinelander 5e9b8c0948 Update catch2 to latest 2.x upstream
Should fix build issues with newer libc.
2021-10-13 20:35:37 -03:00
Jason Rhinelander 504d0d10ea
Merge pull request #52 from jagerman/convert-iterators
Make (and use) iterator approach for encoding/decoding
2021-10-13 18:17:28 -03:00
Jason Rhinelander 9a8adb5bfd Add methods for unpadded base64 construction
The iterator has them; this adds wrapper methods to access them when not
using the iterator directly.
2021-10-01 18:53:05 -03:00
Jason Rhinelander ee1d69f333 Add b32z/b64 invalid garbage tests
Tests the new restricted added for b32z/b64 trailing crap.
2021-10-01 18:52:30 -03:00
Jason Rhinelander 24dd7a3854 Make (and use) iterator approach for encoding/decoding
This allows for on-the-fly encoding/decoding, and also allows for
on-the-fly transcoding between types without needing intermediate string
allocations (see added test cases for examples).
2021-10-01 18:23:29 -03:00
Jason Rhinelander cd56ad8e08 Expose size calculations; stricter b32z/b64 validity checking
- Add {to,from}_{base64,base32z,hex}_size functions to calculate the
  resulting output size from a given input size.

- Use it internally

- Make b32z and b64 validity checking slightly stricter: currently we
  "accept" some b32z and b64 strings that contain an extra character
  that leave us with 5-7 trailing bits (base32z) or 6 trailing bits
  (base64).  We simply ignore the extra one if decoding, but we
  shouldn't accept it in the "is valid" calls.
2021-10-01 17:54:03 -03:00
Jason Rhinelander 6100802f82
Merge pull request #48 from majestrate/boob-operator-overload-2021-09-24
add operator() overload for defered message that sends reply
2021-09-28 01:32:58 -03:00
Jeff Becker 5a41e84378
add operator() overload for defered message that sends reply 2021-09-24 16:01:31 -04:00
Jason Rhinelander cdd21a9e81 Another workaround for crapple 2021-09-07 02:00:09 -03:00
Jason Rhinelander 9e3469d968 Add allocation-free bt-list and bt-dict producer
This should allow for b-encoding with better performance and less memory
fragmentation.

Documentation and test suite show how it's used.
2021-09-07 01:12:47 -03:00
Jason Rhinelander f12a48a195 Remove ancient dead code
I don't remember why this was here.
2021-09-06 21:48:56 -03:00
Jason Rhinelander 2ac4379fa6 Make {to,from}_{hex/b64/b32} return output iterator
Changes the 3-iterator versions of to_hex, from_b32z, etc. to return the
final output iterator, which allows for much easier in-place "from"
conversion without needing a new string by doing something like:

    std::string data = /* some hex */;
    auto end = oxenmq::from_hex(data.begin(), data.end(), data.begin();
    data.erase(end, data.end());

Returning from the "to" converters is a bit less useful but doing it
anyway for consistency (and because it could still have some use, e.g.
if output is into some fixed buffer it lets you determine how much was
written).
2021-08-20 16:08:33 -03:00
Jason Rhinelander f553085558 Add support for inproc: requests
inproc support is special in zmq: in particular it completely bypasses
the auth layer, which causes problems in OxenMQ because we assume that a
message will always have auth information (set during initial connection
handshake).

This adds an "always-on" inproc listener and adds a new `connect_inproc`
method for a caller to establish a connection to it.

It also throws exceptions if you try to `listen_plain` or `listen_curve`
on an inproc address, because that won't work for the reasons detailed
above.
2021-08-04 20:15:16 -03:00
Jason Rhinelander bae71ec6a8 Another apple time fix attempt 2021-08-04 20:15:00 -03:00
Jason Rhinelander 917c7d64c5 Apple test suite hacks
Because macOS is really slow.
2021-08-04 19:54:17 -03:00
Jason Rhinelander e1d21d3faf Tweak test timers to deal with Apple's shitty thread scheduling 2021-08-04 17:48:53 -03:00
Jason Rhinelander 4a6bb3f702 Fix messages coming back on an outgoing connection
The recent PR that revamped the connection IDs missed a case when
connecting to service nodes where we store the SN pubkey in peers, but
then fail to find the peer when we look it up by connection id.

This adds the required tracking to fix that case (and adds a test that
fails without the fix here).
2021-07-01 00:37:55 -03:00
Jason Rhinelander 45db87f712 Fix uninitialized value in post-start listen test 2021-06-23 14:04:16 -03:00
Jason Rhinelander a0642a894e Miscellaneous small test suite fixes/improvements
- Allow up to 200ms (instead of 100ms) for the things we are waiting on
to become available, to prevent occasional spurious failures.
- Add unscoped info for how long we waited.
- Avoid calling into oxenmq with the catch lock held in the "hey google"
tests (because this will deadlock if the oxenmq call invokes any
logging).
- Replace an old std::cerr logger with the updated catch2 logger.
2021-06-23 10:55:47 -03:00
Jason Rhinelander 5dd7c12219 Add support for listening after startup
This commit adds support for listening on new ports after startup.  This
will make things easier in storage server, in particular, where we want
to delay listening on public ports until we have an established
connection and initial block status update from oxend.
2021-06-23 10:51:08 -03:00
Jason Rhinelander cdc6a9709c Add thread-safe timerid assignment version of add_timer()
I realized after merging the previous PR that it is difficult to
correctly pass ownership into a timer, because something like:

    TimerID x = omq.add_timer([&] { omq.cancel_timer(x); }, 5ms);

doesn't work when the timer job needs to outlive the caller.  My next
approach was:

    auto x = std::make_shared<TimerID>();
    *x = omq.add_timer([&omq, x] { omq.cancel_timer(*x); }, 5ms);

but this has two problems: first, TimerID wasn't default constructible,
and second, there is no guarantee that the assignment to *x happens
before (and is visible to) the access for the cancellation.

This commit fixes both issues: TimerID is now default constructible, and
an overload is added that takes the lvalue reference to the TimerID to
set rather than returning it (and guarantees that it will be set before
the timer is created).
2021-05-25 17:29:25 -03:00
Jason Rhinelander 26745299ed Add timer cancellation & timer tests
Updates `add_timer` to return a new opaque TimerID object that can later
be passed to `cancel_timer` to cancel an existing timer.

Also adds timer tests, which was omitted (except for one in the tagged
threads section), along with a new test for timer deletion.
2021-05-20 22:05:58 -03:00
Jason Rhinelander 5ccacafdb1 Add support for std::optional<T> send arguments
If the optional is set it gets applied as if you specified the `T` in
the send(...), if unset it works as if you didn't specify the argument
at all.
2021-04-20 13:54:42 -03:00
Jason Rhinelander ac58e5b574 Rename PUBKEY_BASED_ROUTING_ID to EPHEMERAL_ROUTING_ID
And similarly for the connect_option
2021-04-15 15:42:04 -03:00
Jason Rhinelander 506bd65b05 Add better deferred reply capabilities to Message
This provides an interface for sending a reply to a message later (i.e.
after the Message& itself is no longer valid) by using a new
`send_later()` method of the Message instance that returns an object
that can properly route replies (and can outlive the Message it was
called on).

Intended use is:

    run_this_lambda_later([send=msg.send_later()] {
        send.reply("content");
    });

which is equivalent to:

    run_this_lambda_later([&msg] {
        msg.send_reply("content");
    });

except that it works properly even if the lambda is invoked beyond the
lifetime of `msg`.
2021-01-21 11:59:39 -04:00
Jason Rhinelander 2ae6b96016 Rename LokiMQ to OxenMQ 2021-01-14 15:32:38 -04:00
Jason Rhinelander bd9313bf19 Fix decoding into a std::byte
Decoding into a std::byte output iterator was not working because the
`*out++ = val` assignment doesn't work when the output is std::byte and
val is a char/unsigned char/uint8_t.  Instead we need to explicitly
cast, but figuring out what we have to cast to is a little bit tricky.

This PR makes it work (and bumps the version for this and the is_hex
fix).
2020-12-14 13:05:14 -04:00
Jason Rhinelander 90701e5d62 Make lokimq::is_hex check for size being a multiple of 2
`is_hex()` is a bit misleading as `from_hex()` requires an even-length
hex string, but `is_hex()` also allows odd-length hex strings, which
means currently callers should be doing `if (lokimq::is_hex(str) &&
str.size() % 2 == 0)`, but probably aren't.

Since the main point of `lokimq/hex.h` is for byte<->hex conversions it
doesn't make much sense to allow `is_hex()` to return true for something
that can't be validly decoded via `from_hex()`, thus this PR changes it
to return false.

If someone *really* wants to test for an odd-length hex string (though
I'm skeptical that there is a need for this), this also exposes
`is_hex_digit` so that they could use:

    bool all_hex = std::all_of(str.begin(), str.end(), lokimq::is_hex_digit<char>)
2020-12-12 20:25:01 -04:00
Jason Rhinelander 7049d3cb5a Test suite: use different ports for each test
Apple, in particular, often fails tests with an address already in use
if attempt to reuse a port that the process just closed, because it is a
wonderful OS.
2020-10-15 16:55:33 -03:00
Jason Rhinelander 8ed529200b macOS 10.12 compatibility
Add var::get/var::visit implementations of std::get/std::visit that get
used if compiling for an old macos target, and use those.

The issue is that on a <10.14 macos target Apple's libc++ is missing
std::bad_variant_access, and so any method that can throw it (such as
std::get and std::visit) can't be used.  This workaround is ugly, but
such is life when you want to support running on Apple platforms.
2020-10-15 16:55:33 -03:00
Jason Rhinelander 9467c4682c Add C string bt_value ctor 2020-09-01 17:57:18 -03:00
Jason Rhinelander 30faadf01a Add serialization/deserialization of tuples and pairs
On the wire they are just lists, but this lets you put tuples onto and
pull tuples off of the wire.  (Also supports std::pair).

Supports direct serialization (via bt_serialize()/bt_deserialize()),
list/dict consumer deserialization, and conversion from a bt_value or
bt_list via a new bt_tuple() function.
2020-08-03 00:40:49 -03:00
Jason Rhinelander e5cf174b83 Fix & add tests for send_option::data_parts(...)
data_parts() wasn't currently used anywhere, and was broken: it was
calling bt_deserialize which was just wrong.

This repurposes it to take iterators over strings (or string-like types)
and append those parts as message parts.

Also adds tests for it.
2020-07-20 14:37:01 -03:00
Jason Rhinelander d2f852c217 Add test that destruction doesn't throw 2020-07-10 17:54:45 -03:00
Jason Rhinelander 932bbb33d7 Allow injecting tasks into lokimq job queue
This allows mixing some outside task into the lokimq job queue for a
category (queued up with native LMQ requests for that category) for use
when there is some external process that is able to generate messages.

For example, the most immediate use for this is to allow an HTTP server
to handle incoming RPC requests and, as soon as they arrive, inject them
into LokiMQ's queue for the "rpc" category so that native LMQ rpc
requests and HTTP rpc requests share the same thread pool and queue.

These injected jobs bypass all of LokiMQ's authentication and response
mechanisms: that's up to the invoked callback itself to manage.

Injected tasks are somewhat similar to batch jobs, but unlike batch jobs
the are queued and prioritized as ordinary external LokiMQ requests.
(Batch jobs, in contrast, have a higher scheduling priority, no queue
limits, and typically a larger available thread pool).
2020-06-30 18:44:11 -03:00
Jason Rhinelander 07b31bd8a1 Take lokimq::address as connect_remote argument
Deprecates the existing connect_remote() that takes remote addr and
pubkey as separate strings, just taking a `address` instead (into which
the caller can set pubkey/curve data as desired).

Also slightly changes how `connect_remote()` works when called with a
string remote but no pubkey: that string is now an augmented
lokimq::address string so that it can use the various formats supported
by `lokimq::address`.

(This was meant to be included in the PR that added `address` but
apparently didn't get implemented.)
2020-06-30 13:09:34 -03:00
Jason Rhinelander 278909db77 Resolve race condition in test suite
There can be a spurious failure here if the backdoor_details element
hasn't been added yet, so lock & check it when waiting for the test
conditions.

The weirdest thing about this error is that it can fail but then when
expanding values they expand to *correct* values, i.e. so you get:

FAILED:
  REQUIRE( backdoor_details == all_the_things )
with expansion:
  { "Alaska", "I'm the luckiest man in the world", "Loretta", "because all my
  life are belong to Google", "moustache hatred", "photos", "scallops",
  "snorted when she laughed", "tickled pink" }
  ==
  { "Alaska", "I'm the luckiest man in the world", "Loretta", "because all my
  life are belong to Google", "moustache hatred", "photos", "scallops",
  "snorted when she laughed", "tickled pink" }
2020-06-07 21:28:53 -03:00
Jason Rhinelander ae8dd27cdd Drop tagged thread init function; add synchronization dance
The init function doesn't seem all that useful and makes the interface a
bit more complicated, so drop it.

Also addresses a race condition that can happen with tagged thread
startup when the proxy tries to talk to a tagged thread but the tagged
thread hasn't connected yet (which then aborts the proxy because it
assumes workers are always routable).
2020-06-07 21:28:53 -03:00
Jason Rhinelander 8caab97355 Rename TaggedThread to TaggedThreadID, drop .name attribute
This renames the class to make it clearer what it does, and drops the
.name attribute from it so that it can cheaply be passed around.  This
then means it can be cheaply passed by value (using std::optionals)
rather than by pointer when specifying a thread.
2020-06-07 21:28:53 -03:00
Jason Rhinelander 29380922bf Tagged threads for jobs, batches, and timers
This adds to ability to have lokimq manage specific threads to which
jobs (individual, batch jobs, batch completions, or timers) can be
directed to.  This allows dedicating a thread to some slow or
thread-unsafe action where you can dump jobs to the tagged thread as
a method of lockless job queuing.
2020-06-07 21:28:53 -03:00
Jason Rhinelander 211d5211b0 Tweak test timeout
macos sometimes needs more time here
2020-05-19 22:56:12 -03:00
Jason Rhinelander a24e87d4d0 Fix sodium linking and call sodium_init()
We call libsodium functions which require a sodium_init() call; this is
usually a no-op (zmq will have already called it for us), but in case
zmq is built with tweetnacl instead of sodium we need to call it before
we call it directly in the LokiMQ ctor and the test suite.
2020-05-15 01:33:02 -03:00