2023-09-02 15:04:46 -07:00
|
|
|
// This file is licensed under the CC0 1.0 license.
|
|
|
|
// See: https://creativecommons.org/publicdomain/zero/1.0/legalcode
|
|
|
|
|
2023-08-27 18:10:53 -07:00
|
|
|
const std = @import("std");
|
|
|
|
|
|
|
|
const nats = @import("nats");
|
|
|
|
|
|
|
|
const util = @import("./util.zig");
|
|
|
|
|
2023-09-02 17:12:00 -07:00
|
|
|
const rsa_key = @embedFile("./data/client-rsa.key");
|
|
|
|
const rsa_cert = @embedFile("./data/client-rsa.cert");
|
|
|
|
const ecc_key = @embedFile("./data/client-ecc.key");
|
|
|
|
const ecc_cert = @embedFile("./data/client-ecc.cert");
|
|
|
|
|
2023-08-27 18:10:53 -07:00
|
|
|
test "nats.Connection.connectTo" {
|
|
|
|
{
|
2023-08-28 21:26:36 -07:00
|
|
|
var server = try util.TestServer.launch(.{});
|
|
|
|
defer server.stop();
|
|
|
|
|
2023-08-27 18:10:53 -07:00
|
|
|
try nats.init(nats.default_spin_count);
|
|
|
|
defer nats.deinit();
|
|
|
|
|
2023-09-03 17:34:29 -07:00
|
|
|
const connection = try nats.Connection.connectTo(server.url);
|
2023-08-27 18:10:53 -07:00
|
|
|
defer connection.destroy();
|
|
|
|
}
|
2023-08-28 21:26:36 -07:00
|
|
|
|
|
|
|
{
|
|
|
|
var server = try util.TestServer.launch(.{
|
|
|
|
.auth = .{ .token = "test_token" },
|
|
|
|
});
|
|
|
|
defer server.stop();
|
|
|
|
|
|
|
|
try nats.init(nats.default_spin_count);
|
|
|
|
defer nats.deinit();
|
|
|
|
|
2023-09-03 17:34:29 -07:00
|
|
|
const connection = try nats.Connection.connectTo(server.url);
|
2023-08-28 21:26:36 -07:00
|
|
|
defer connection.destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
var server = try util.TestServer.launch(.{ .auth = .{
|
|
|
|
.password = .{ .user = "user", .pass = "password" },
|
|
|
|
} });
|
|
|
|
defer server.stop();
|
|
|
|
|
|
|
|
try nats.init(nats.default_spin_count);
|
|
|
|
defer nats.deinit();
|
|
|
|
|
2023-09-03 17:34:29 -07:00
|
|
|
const connection = try nats.Connection.connectTo(server.url);
|
2023-08-28 21:26:36 -07:00
|
|
|
defer connection.destroy();
|
2023-09-02 21:56:40 -07:00
|
|
|
connection.close();
|
2023-08-28 21:26:36 -07:00
|
|
|
}
|
2023-08-27 18:10:53 -07:00
|
|
|
}
|
2023-08-28 22:46:14 -07:00
|
|
|
|
2023-09-02 21:56:40 -07:00
|
|
|
test "nats.Connection" {
|
|
|
|
var server = try util.TestServer.launch(.{});
|
|
|
|
defer server.stop();
|
|
|
|
|
|
|
|
try nats.init(nats.default_spin_count);
|
|
|
|
defer nats.deinit();
|
|
|
|
|
2023-09-03 17:34:29 -07:00
|
|
|
const connection = try nats.Connection.connectTo(server.url);
|
2023-09-02 21:56:40 -07:00
|
|
|
defer connection.destroy();
|
|
|
|
|
|
|
|
_ = connection.isClosed();
|
|
|
|
_ = connection.isReconnecting();
|
|
|
|
_ = connection.getStatus();
|
|
|
|
_ = connection.bytesBuffered();
|
|
|
|
try connection.flush();
|
|
|
|
try connection.flushTimeout(100);
|
|
|
|
_ = connection.getMaxPayload();
|
|
|
|
_ = try connection.getStats();
|
|
|
|
{
|
|
|
|
// id is 56 bytes plus terminating zero
|
|
|
|
var buf = [_]u8{0} ** 57;
|
|
|
|
_ = try connection.getConnectedUrl(&buf);
|
|
|
|
_ = try connection.getConnectedServerId(&buf);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
var servers = try connection.getServers();
|
|
|
|
defer servers.deinit();
|
|
|
|
|
|
|
|
var discovered = try connection.getDiscoveredServers();
|
|
|
|
defer discovered.deinit();
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = connection.getLastError();
|
|
|
|
_ = try connection.getClientId();
|
|
|
|
// our connection does not have a JWT, so this call will always fail
|
|
|
|
_ = connection.sign("greetings") catch {};
|
|
|
|
_ = try connection.getLocalIpAndPort();
|
|
|
|
_ = connection.getRtt() catch {};
|
|
|
|
_ = connection.hasHeaderSupport();
|
|
|
|
// this closes the connection, but it does not block until the connection is closed,
|
|
|
|
// which can result in nondeterministic behavior for calls after this one.
|
|
|
|
try connection.drain();
|
|
|
|
// this will return error.ConnectionClosed if the connection is already closed, so
|
|
|
|
// don't expect this to be error free.
|
|
|
|
connection.drainTimeout(1000) catch {};
|
|
|
|
}
|
|
|
|
|
2024-10-01 23:17:20 -07:00
|
|
|
fn callbacks(comptime UDT: type) type {
|
|
|
|
return struct {
|
|
|
|
fn reconnectDelayHandler(userdata: UDT, connection: *nats.Connection, attempts: c_int) i64 {
|
|
|
|
_ = userdata;
|
|
|
|
_ = connection;
|
|
|
|
_ = attempts;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn errorHandler(
|
|
|
|
userdata: UDT,
|
|
|
|
connection: *nats.Connection,
|
|
|
|
subscription: *nats.Subscription,
|
|
|
|
status: nats.Status,
|
|
|
|
) void {
|
|
|
|
_ = userdata;
|
|
|
|
_ = connection;
|
|
|
|
_ = subscription;
|
|
|
|
_ = status;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn connectionHandler(userdata: UDT, connection: *nats.Connection) void {
|
|
|
|
_ = userdata;
|
|
|
|
_ = connection;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn jwtHandler(userdata: UDT) nats.JwtResponseOrError {
|
|
|
|
_ = userdata;
|
|
|
|
// return .{ .jwt = std.heap.raw_c_allocator.dupeZ(u8, "abcdef") catch @panic("no!") };
|
|
|
|
return .{ .error_message = std.heap.raw_c_allocator.dupeZ(u8, "dang") catch @panic("no!") };
|
|
|
|
}
|
|
|
|
|
|
|
|
fn signatureHandler(userdata: UDT, nonce: [:0]const u8) nats.SignatureResponseOrError {
|
|
|
|
_ = userdata;
|
|
|
|
_ = nonce;
|
|
|
|
// return .{ .signature = std.heap.raw_c_allocator.dupe(u8, "01230123") catch @panic("no!") };
|
|
|
|
return .{ .error_message = std.heap.raw_c_allocator.dupeZ(u8, "whoops") catch @panic("no!") };
|
|
|
|
}
|
|
|
|
};
|
2023-08-28 22:46:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
test "nats.ConnectionOptions" {
|
|
|
|
try nats.init(nats.default_spin_count);
|
|
|
|
defer nats.deinit();
|
|
|
|
|
|
|
|
const options = try nats.ConnectionOptions.create();
|
|
|
|
defer options.destroy();
|
|
|
|
|
all: allow passing more types of pointers as callback userdata
By performing more pointer casting gymnastics, additional types of
pointer can be supported. For example, now const qualified pointers
can be passed through thanks to the use of constCast. Also, having
explicit ptrCast allows nested pointers (e.g. a pointer to a slice,
such as `*[]const u8`) to be passed as userdata as well (the compiler
refuses to coerce a pointer to a pointer to `?*anyopaque` for some
reason, I guess because maybe it's ambiguous somehow?) Hopefully this
extra casting does not appreciably reduce the compiler's ability to
catch real bugs (for example, on a 64-bit machine, ptrCast can convert
a *u64 into a **u64 because there is no alignment change).
Also, the `volatile` pointer specifier is still not supported.
`allowzero` pointers probably also have a problem. Those are both
extreme edge cases, however.
This was intended to work before but did not due to an oversight.
Specifically, because the userdata pointers are stored as ?*anyopaque,
which does *not* have the const qualifier, they must have their const
qualifiers also removed. This is safe because the thunk guarantees
that the consumer code never sees the non-const version of the
pointer, and the nats library itself does nothing except pass the
pointer through to the user callback.
The tests have been updated to ensure this case works. The examples
still use a mutable userdata pointer to show that that case also
works. More tests could be added for the sake of increased rigor, but
I don't think it adds much.
2024-04-06 15:07:13 -07:00
|
|
|
const userdata: u32 = 0;
|
2023-08-28 22:46:14 -07:00
|
|
|
|
|
|
|
try options.setUrl(nats.default_server_url);
|
|
|
|
const servers = [_][*:0]const u8{ "nats://127.0.0.1:4442", "nats://127.0.0.1:4443" };
|
|
|
|
try options.setServers(&servers);
|
|
|
|
try options.setCredentials("user", "password");
|
|
|
|
try options.setToken("test_token");
|
|
|
|
try options.setNoRandomize(false);
|
|
|
|
try options.setTimeout(1000);
|
|
|
|
try options.setName("name");
|
|
|
|
|
|
|
|
try options.setVerbose(true);
|
|
|
|
try options.setPedantic(true);
|
|
|
|
try options.setPingInterval(1000);
|
|
|
|
try options.setMaxPingsOut(100);
|
|
|
|
try options.setIoBufSize(1024);
|
|
|
|
try options.setAllowReconnect(false);
|
|
|
|
try options.setMaxReconnect(10);
|
|
|
|
try options.setReconnectWait(500);
|
|
|
|
try options.setReconnectJitter(100, 200);
|
2024-10-01 23:17:20 -07:00
|
|
|
try options.setCustomReconnectDelay(*const u32, callbacks(*const u32).reconnectDelayHandler, &userdata);
|
|
|
|
try options.setCustomReconnectDelay(void, callbacks(void).reconnectDelayHandler, {});
|
|
|
|
try options.setCustomReconnectDelay(?*const u32, callbacks(?*const u32).reconnectDelayHandler, null);
|
2023-08-28 22:46:14 -07:00
|
|
|
try options.setReconnectBufSize(1024);
|
|
|
|
try options.setMaxPendingMessages(50);
|
2024-10-01 23:17:20 -07:00
|
|
|
try options.setErrorHandler(*const u32, callbacks(*const u32).errorHandler, &userdata);
|
|
|
|
try options.setErrorHandler(void, callbacks(void).errorHandler, {});
|
|
|
|
try options.setErrorHandler(?*const u32, callbacks(?*const u32).errorHandler, null);
|
|
|
|
try options.setClosedCallback(*const u32, callbacks(*const u32).connectionHandler, &userdata);
|
|
|
|
try options.setClosedCallback(void, callbacks(void).connectionHandler, {});
|
|
|
|
try options.setClosedCallback(?*const u32, callbacks(?*const u32).connectionHandler, null);
|
|
|
|
try options.setDisconnectedCallback(*const u32, callbacks(*const u32).connectionHandler, &userdata);
|
|
|
|
try options.setDisconnectedCallback(void, callbacks(void).connectionHandler, {});
|
|
|
|
try options.setDisconnectedCallback(?*const u32, callbacks(?*const u32).connectionHandler, null);
|
|
|
|
try options.setDiscoveredServersCallback(*const u32, callbacks(*const u32).connectionHandler, &userdata);
|
|
|
|
try options.setDiscoveredServersCallback(void, callbacks(void).connectionHandler, {});
|
|
|
|
try options.setDiscoveredServersCallback(?*const u32, callbacks(?*const u32).connectionHandler, null);
|
|
|
|
try options.setLameDuckModeCallback(*const u32, callbacks(*const u32).connectionHandler, &userdata);
|
|
|
|
try options.setLameDuckModeCallback(void, callbacks(void).connectionHandler, {});
|
|
|
|
try options.setLameDuckModeCallback(?*const u32, callbacks(?*const u32).connectionHandler, null);
|
2023-08-28 22:46:14 -07:00
|
|
|
try options.ignoreDiscoveredServers(true);
|
|
|
|
try options.useGlobalMessageDelivery(false);
|
|
|
|
try options.ipResolutionOrder(.ipv4_first);
|
|
|
|
try options.setSendAsap(true);
|
|
|
|
try options.useOldRequestStyle(false);
|
|
|
|
try options.setFailRequestsOnDisconnect(true);
|
|
|
|
try options.setNoEcho(true);
|
2024-10-01 23:17:20 -07:00
|
|
|
try options.setRetryOnFailedConnect(*const u32, callbacks(*const u32).connectionHandler, true, &userdata);
|
|
|
|
try options.setRetryOnFailedConnect(void, callbacks(void).connectionHandler, true, {});
|
|
|
|
try options.setRetryOnFailedConnect(?*const u32, callbacks(?*const u32).connectionHandler, true, null);
|
|
|
|
try options.setUserCredentialsCallbacks(*const u32, *const u32, callbacks(*const u32).jwtHandler, callbacks(*const u32).signatureHandler, &userdata, &userdata);
|
|
|
|
try options.setUserCredentialsCallbacks(void, void, callbacks(void).jwtHandler, callbacks(void).signatureHandler, {}, {});
|
2023-08-28 22:46:14 -07:00
|
|
|
try options.setWriteDeadline(5);
|
|
|
|
try options.disableNoResponders(true);
|
|
|
|
try options.setCustomInboxPrefix("_FOOBOX");
|
|
|
|
try options.setMessageBufferPadding(123);
|
|
|
|
}
|
2023-09-02 17:12:00 -07:00
|
|
|
|
|
|
|
fn tokenHandler(userdata: *u32) [:0]const u8 {
|
|
|
|
_ = userdata;
|
|
|
|
return "token";
|
|
|
|
}
|
|
|
|
|
|
|
|
test "nats.ConnectionOptions (crypto edition)" {
|
|
|
|
try nats.init(nats.default_spin_count);
|
|
|
|
defer nats.deinit();
|
|
|
|
|
|
|
|
const options = try nats.ConnectionOptions.create();
|
|
|
|
defer options.destroy();
|
|
|
|
var userdata: u32 = 0;
|
|
|
|
|
2024-01-01 15:11:21 -08:00
|
|
|
try options.setTokenHandler(*u32, tokenHandler, &userdata);
|
2023-09-02 17:12:00 -07:00
|
|
|
try options.setSecure(false);
|
|
|
|
try options.setCertificatesChain(rsa_cert, rsa_key);
|
|
|
|
try options.setCiphers("-ALL:HIGH");
|
|
|
|
try options.setCipherSuites("TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256");
|
|
|
|
try options.setExpectedHostname("test.nats.zig");
|
|
|
|
try options.skipServerVerification(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
test "nats.ConnectionOptions (crypto connect)" {
|
|
|
|
{
|
|
|
|
var server = try util.TestServer.launch(.{ .tls = .rsa });
|
|
|
|
defer server.stop();
|
|
|
|
|
|
|
|
try nats.init(nats.default_spin_count);
|
|
|
|
defer nats.deinit();
|
|
|
|
|
|
|
|
const options = try nats.ConnectionOptions.create();
|
|
|
|
defer options.destroy();
|
|
|
|
|
|
|
|
try options.setSecure(true);
|
|
|
|
try options.skipServerVerification(true);
|
|
|
|
try options.setCertificatesChain(rsa_cert, rsa_key);
|
|
|
|
|
|
|
|
const connection = try nats.Connection.connect(options);
|
|
|
|
defer connection.destroy();
|
|
|
|
|
|
|
|
try connection.publish("foo", "bar");
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
var server = try util.TestServer.launch(.{ .tls = .ecc });
|
|
|
|
defer server.stop();
|
|
|
|
|
|
|
|
try nats.init(nats.default_spin_count);
|
|
|
|
defer nats.deinit();
|
|
|
|
|
|
|
|
const options = try nats.ConnectionOptions.create();
|
|
|
|
defer options.destroy();
|
|
|
|
|
|
|
|
try options.setSecure(true);
|
|
|
|
try options.skipServerVerification(true);
|
|
|
|
try options.setCertificatesChain(ecc_cert, ecc_key);
|
|
|
|
|
|
|
|
const connection = try nats.Connection.connect(options);
|
|
|
|
defer connection.destroy();
|
|
|
|
|
|
|
|
try connection.publish("foo", "bar");
|
|
|
|
}
|
|
|
|
}
|