Commit Graph

37 Commits

Author SHA1 Message Date
Jason Rhinelander 730633bbae Provide caller Access in Message
This lets a callback set up something at, say, basic level, but provide
different values for an admin auth remote than a basic auth remote.
2020-04-23 21:52:39 -03:00
Jason Rhinelander fc1ea66599 Reduce heartbeat frequency to 15s
3s was excessive especially considering that the default heartbeat
timeout is set to 30s.
2020-04-18 02:58:22 -03:00
Jason Rhinelander 2966427cc0 Increase ZMQ socket limit
ZMQ's default is 1024, which we are close to hitting; this changes the
default for LokiMQ to 10000.
2020-04-17 16:13:04 -03:00
Jason Rhinelander 34bbaaf612 Use slower and exponential backoff in reconnection
ZMQ's default reconnection time is 100ms, indefinitely, which seems far
too aggressive, particularly where we have some potential for hundreds
or thousands of connections.

This changes the default to be slightly slower (250ms instead of 100ms)
on the first attempt, and to use exponential backoff doubling the time
between each failed connection attempt up to a max of 5s between
reconnection attempts to calm things down.
2020-04-17 16:09:53 -03:00
Jason Rhinelander 7de36da483 Add ZMTP heartbeating (enabled by default)
ZMTP heartbeating should help keep the connection alive, and should
result in earlier detection of connection failures.
2020-04-14 16:08:54 -03:00
Jason Rhinelander 84bd5544cc Move pubkey_set into auth.h header
This allows it to be brought in without the full lokimq.h header.
2020-04-13 13:03:19 -03:00
Jason Rhinelander 3b86eb1341 1.1.0: invocation-time SN auth; failure responses
This replaces the recognition of SN status to be checked per-command
invocation rather than on connection.  As this breaks the API quite
substantially, though doesn't really affect the functionality, it seems
suitable to bump the minor version.

This requires a fundamental shift in how the calling application tells
LokiMQ about service nodes: rather than using a callback invoked on
connection, the application now has to call set_active_sns() (or the
more efficient update_active_sns(), if changes are readily available) to
update the list whenever it changes.  LokiMQ then keeps this list
internally and uses it when determining whether to invoke.

This release also brings better request responses on errors: when a
request fails, the data argument will now be set to the failure reason,
one of:

- TIMEOUT
- UNKNOWNCOMMAND
- NOT_A_SERVICE_NODE (the remote isn't running in SN mode)
- FORBIDDEN (auth level denies the request)
- FORBIDDEN_SN (SN required and the remote doesn't see us as a SN)

Some of these (UNKNOWNCOMMAND, NOT_A_SERVICE_NODE, FORBIDDEN) were
already sent by remotes, but there was no connection to a request and so
they would log a warning, but the request would have to time out.

These errors (minus TIMEOUT, plus NO_REPLY_TAG signalling that a command
is a request but didn't include a reply tag) are also sent in response
to regular commands, but they simply result in a log warning showing the
error type and the command that caused the failure when received.
2020-04-12 19:57:19 -03:00
Jason Rhinelander bc49b5e9a0 Expose advanced zmq context setting ability 2020-04-03 12:28:50 -03:00
Jason Rhinelander e3a86aaf71 Add `send_option::outgoing` to force a send on an outgoing connection
SS wants this, in particular, to be able to do reachability tests.
(Using connect_remote for this was bad with pubkey-based routing ids
because the second connection could replace an existing connection).
2020-04-03 01:34:21 -03:00
Jason Rhinelander bd196d08b8 Allow log level to be specified in constructor
It can still be set using `lmq.log_level(...)`, but this can be slightly
more convenient -- and without this log messages in the constructor are
completely useless.
2020-03-29 15:21:20 -03:00
Jason Rhinelander 716d73d196 All sends use dontwait; add send failure callbacks
We really don't *ever* want send to block, no matter how it is called,
since the send is always in the proxy thread.  This makes the actual
send call always non-blocking, and adds callbacks that we can invoke on
send failures: either on queue full errors (which might be recoverable),
or both full queue and hard failures (which are generally not
recoverable).  These callbacks are both optional: they have to be passed
in using `send_option::queue_full` (if you just want queue full
notifies) or `send_option::queue_failure` (if you want queue full
notifies *and* other send exceptions).
2020-03-29 15:21:20 -03:00
Jason Rhinelander fd19f7b183 Trim logged filenames to lokimq/*
Otherwise this includes the full build path which is gross.
2020-03-27 15:17:34 -03:00
Jason Rhinelander a7c669775f Avoid masking ReplyCallback type with template param 2020-03-27 14:48:35 -03:00
Jason Rhinelander 8b6f6f498c Make request timeout configurable
For example:

    lmq.request(conn, "some.method", callback, lokimq::request_timeout{5s});

will result in the callback being called with a failure if the response
doesn't arrive within 5s.  (If it still arrives, but after the failure
callback, it gets dropped).
2020-03-23 22:30:53 -03:00
Jason Rhinelander 75750001ce Reduce connection check interval and make configurable
The previous 1s default seems on the long side; this reduces it to
250ms.  It also makes it a public member so that it can be configured
(which is mainly needed for the test suite, but might be useful for
lokimq-calling code that needs faster or slower connection cleanups).
2020-03-23 22:29:14 -03:00
Jason Rhinelander b97f3442e7 Rename keep-alive -> keep_alive in internal serialization
This makes it consistent with other internal parameter names.
2020-03-23 22:28:23 -03:00
Jason Rhinelander 04e2bf7cf7 Change pending_connects from vector to list
Having this as a vector seems to cause armhf/gcc-6 to segfault.  On
closer inspection there's no good reason this should be a vector in the
first place: it only gets used during new connection handshaking and
isn't in any hot loop, plus the elements are fairly large tuples where
shifting elements is going to be relatively expensive.  Thus switching
it to a list everywhere (rather than just on old gcc arm) seems fine.
2020-03-21 12:56:46 -03:00
Jason Rhinelander 49f8ef21f1 Install mapbox-variant and cppzmq headers 2020-03-13 19:05:12 -03:00
Jason Rhinelander e17ca30411 Split up into logical headers and compilation units
lokimq.cpp and lokimq.h were getting monolithic; this splits lokimq.cpp
into multiple smaller cpp files by logical purpose for better parallel
compilation ability.  It also splits up the lokimq.h header slightly by
moving the ConnectionID and Message types into their own headers.
2020-03-13 14:28:21 -03:00
Jason Rhinelander fa7d4a8a42 Silence -Wmismatched-tags warning 2020-03-05 01:19:29 -04:00
Jason Rhinelander dcb7e4df0b Add a category command helper class
This allows simplifying:

    lmq.add_category("foo", ...);
    lmq.add_command("foo", "a", ...);
    lmq.add_command("foo", "b", ...);
    lmq.add_request_command("foo", "c", ...);

to:

    lmq.add_category("foo", ...)
        .add_command("a", ...)
        .add_command("b", ...)
        .add_request_command("b", ...)
        ;
2020-03-02 15:11:54 -04:00
Jason Rhinelander f18f86cf96 Allow `optional` and `incoming` to take a bool
This makes it much more convenience to use them with a run-time
condition; this simplifies:

    if (should_be_optional)
        lmq.send(..., send_option::optional{});
    else
        lmq.send(...);

to:

    lmq.send(..., send_option::optional{should_be_optional});
2020-03-02 14:24:07 -04:00
Jason Rhinelander 0493082040 Add default allow to `listen_plain()`
The default on listen_curve() was supposed to go on both.
2020-03-01 23:54:06 -04:00
Jason Rhinelander 09c487f327 Add ability to use random routing ids for outgoing 2020-02-29 16:03:25 -04:00
Jason Rhinelander ece8870896 Move routing prefix into ConnectionID
This allows storing a ConnectionID received in a message callback and
using it later to send another message along the connection without
worrying about a routing id: the ConnectionID will have it if it is
required.  Previously you would have had to store the ConnectionID *and*
the routing prefix, and then specified the route as a
send_option::route{}, which was annoying and cumbersome.
2020-02-29 16:01:47 -04:00
Jason Rhinelander 28e36a3eaf Make AuthLevel stream printable 2020-02-29 15:16:58 -04:00
Jason Rhinelander 2743e576b2 Distinguish between batch jobs and reply jobs
This adds a separate category (and reserve count) for "reply jobs",
which are jobs triggered by receiving a reply to a request, or after a
successful connect or unsuccessful timeout.  Previously these were
scheduled as regular batch jobs; this schedules them as a new "reply
jobs" category with its own reserved threads count.

This also changes the defaults for batch jobs and reply jobs to be based
on the specified general workers count rather than directly on hardware
concurrency, so that if you are on a 16-thread CPU but override general
workers from its default of 16 to 4 and don't change batch workers you
now get reserved batch workers set to 2 rather than 8 which constrains
the typical parallel batch jobs to 4 (i.e. the general worker limit)
rather than exceeding it with the batch job limit.

Similarly for reply jobs, which is now ceil(general/8) by default.
2020-02-28 17:54:00 -04:00
Jason Rhinelander 57f0ca74da Added support for general (non-SN) communications
The existing code was largely set up for SN-to-SN or client-to-SN
communications, where messages can always get to the right place because
we can always send by pubkey.

This doesn't work when we want general communications with a random
remote address.

This commit overhauls the way loki-mq handles communication in a few
important ways:

- Listening instances no longer pass bind addresses into the
constructor; instead they call `listen_curve()` or `listen_plain()`
before invoking `start()`.

- `listen_curve()` is equivalent to the existing bind support: it
listens on a socket and accepts encrypted handshaked connections from
anyone who already knows the server's public key.

- `listen_plain()` is all new: it sets up a plain text listening socket
over which random clients can connect and talk.  End-points aren't
verified, and it isn't encrypted, but if you don't know who you are
talking to then encryption isn't doing anything anyway.

- Connecting to a remote now connections in CURVE encryption or NULL
(plain-text) encryption based on whether you provide a remote_pubkey.
For CURVE, the connection will fail if the pubkey does not match.

- `ConnectionID` objects are now returned when connecting to a remote
address; this object is then passed in to send/request/etc. to direct
the message.  For SN communication, ConnectionID's can be created
implicitly from SN pubkey strings, so the existing interface of
`lmq.send(pubkey, ...)` will still work in most cases.

- A ConnectionID is now passed to the ConnectSuccess and ConnectFailure
callbacks.  This can be used to uniquely identify which connection
succeeded or failed, and can determine whether the remote is a service
node (`.sn()`) and/or the pubkey (`.pubkey()`).  (Obviously the service
node status is only available when the client can do service node
lookups, and the pubkey() is only non-empty for encrypted connections).
2020-02-28 00:16:43 -04:00
Jason Rhinelander e4d371b026 Fixed string_view c++17 compatibility
string_view isn't supposed to be implicitly convertible to std::string
and code would break compiling under c++17 (when our local string_view
is simply a std::string_view typedef).
2020-02-24 22:20:56 -04:00
Jason Rhinelander 1b6f38fc07 Add LMQ_TRACE macro instead of LMQ_LOG(trace
LMQ_TRACE becomes nothing under a release build, which is good because
many traces are in the proxy hot path.

Also fixes some confusing log level comparison logic by flipping the
order of log levels.  Previous trace < debug < info < warn, which was
confusing: now that order is reversed which makes way more sense (i.e.
larger LogLevel means more logging).
2020-02-12 22:19:18 -04:00
Jason Rhinelander 13e7953bd7 Updated headers and remove dead code 2020-02-12 22:05:15 -04:00
Jason Rhinelander 63e70f9912 Fix delayed proxy-scheduled batch jobs
Batch jobs scheduled by the proxy thread itself were delayed to the next
poll timeout (because nothing ever gets sent on a socket).  Add a
variable to bypass the next poll to handle this case.
2020-02-11 19:08:19 -04:00
Jason Rhinelander ccfb6d080b Add request/reply abstraction
This allows making RPC requests with a callback that gets called when
the response comes back.  The is essentially a wrapper around doing it
yourself (i.e. by setting up a server-side "request" and client-side
"reply" command where "request" responds with a "reply" command), but
abstracted into lokimq itself as it is likely to be very useful when
integrating client/server connections rather than peer-to-peer
connections.
2020-02-11 02:30:07 -04:00
Jason Rhinelander 061bdee0a8 Add zmq timer support 2020-02-11 02:29:00 -04:00
Jason Rhinelander 03ea49167c Various small optimizations 2020-02-06 00:50:31 -04:00
Jason Rhinelander f75b6cf221 Added batch job implementation + various improvements
This overhauls the proposed batch implementation (described in the
README but previously not implemented) and implements it.

Various other minor improvements and code restructuring.

Added a proposed "request" type to the README to be implemented; this is
like a command, but always expects a ["REPLY", TAG, ...] response.  The
request invoker provides a callback to invoke when such a REPLY arrives.
2020-02-05 20:21:27 -04:00
Jason Rhinelander f3d583c520 Initial LokiMQ release
This library is adapted from lokid's existing quorumnet code (added in
6.x) used for SN-to-SN communication for quorum voting but generalized
to be usable both there and as a basis for other communication channels
with loki projects (for example: wallet-to-lokid communication; loki-ss
and lokinet internal communication with lokid; loki-ss to loki-ss
communication and message passing; perhaps eventually loki p2p traffic).

This initial release compiles but likely has a few warts and bugs that
need ironing out in the implementation before it is production ready.
Some tests will follow.
2020-02-02 22:39:26 -04:00