ZPROXY(3)
okay bestie, sit down, hydrate, because we're about to read the zproxy page from the CZMQ manual (section 3, she's a library call) and honestly? it's giving icon behavior π
πΈ NAME πΈ
zproxy β a class for running a steerable proxy in the background. that's it, that's the whole name section, no notes. β¨
π¦ SYNOPSIS π¦
here's the raw tea, verbatim, because every single line of this is load-bearing:
// Create new zproxy actor instance. The proxy switches messages between
// a frontend socket and a backend socket; use the FRONTEND and BACKEND
// commands to configure these:
//
// zactor_t *proxy = zactor_new (zproxy, NULL);
//
// Destroy zproxy instance. This destroys the two sockets and stops any
// message flow between them:
//
// zactor_destroy (&proxy);
//
// Note that all zproxy commands are synchronous, so your application always
// waits for a signal from the actor after each command.
//
// Enable verbose logging of commands and activity:
//
// zstr_send (proxy, "VERBOSE");
// zsock_wait (proxy);
//
// Specify frontend socket type -- see zsock_type_str () -- and attach to
// endpoints, see zsock_attach (). Note that a proxy socket is always
// serverish:
//
// zstr_sendx (proxy, "FRONTEND", "XSUB", endpoints, NULL);
// zsock_wait (proxy);
//
// When the socket type is XSUB or SUB, topic(s) string(s) can be passed as
// additional arguments (NOTE: in DRAFT state) and the socket will subscribe
// using them.
//
// Specify backend socket type -- see zsock_type_str () -- and attach to
// endpoints, see zsock_attach (). Note that a proxy socket is always
// serverish:
//
// zstr_sendx (proxy, "BACKEND", "XPUB", endpoints, NULL);
// zsock_wait (proxy);
//
// Capture all proxied messages; these are delivered to the application
// via an inproc PULL socket that you have already bound to the specified
// endpoint:
//
// zstr_sendx (proxy, "CAPTURE", endpoint, NULL);
// zsock_wait (proxy);
//
// Pause the proxy. A paused proxy will cease processing messages, causing
// them to be queued up and potentially hit the high-water mark on the
// frontend or backend socket, causing messages to be dropped, or writing
// applications to block:
//
// zstr_sendx (proxy, "PAUSE", NULL);
// zsock_wait (proxy);
//
// Resume the proxy. Note that the proxy starts automatically as soon as it
// has a properly attached frontend and backend socket:
//
// zstr_sendx (proxy, "RESUME", NULL);
// zsock_wait (proxy);
//
// Configure an authentication domain for the "FRONTEND" or "BACKEND" proxy
// socket -- see zsock_set_zap_domain (). Call before binding socket:
//
// zstr_sendx (proxy, "DOMAIN", "FRONTEND", "global", NULL);
// zsock_wait (proxy);
//
// Configure PLAIN authentication for the "FRONTEND" or "BACKEND" proxy
// socket -- see zsock_set_plain_server (). Call before binding socket:
//
// zstr_sendx (proxy, "PLAIN", "BACKEND", NULL);
// zsock_wait (proxy);
//
// Configure CURVE authentication for the "FRONTEND" or "BACKEND" proxy
// socket -- see zsock_set_curve_server () -- specifying both the public and
// secret keys of a certificate as Z85 armored strings -- see
// zcert_public_txt () and zcert_secret_txt (). Call before binding socket:
//
// zstr_sendx (proxy, "CURVE", "FRONTEND", public_txt, secret_txt, NULL);
// zsock_wait (proxy);
//
// This is the zproxy constructor as a zactor_fn; the argument is a
// character string specifying frontend and backend socket types as two
// uppercase strings separated by a hyphen:
CZMQ_EXPORT void
zproxy (zsock_t *pipe, void *unused);
// Selftest
CZMQ_EXPORT void
zproxy_test (bool verbose);
Please add '@interface' section in './../src/zproxy.c'.
okay now let's actually translate that, because the vibes are dense β¨
- Making one:
zactor_t *proxy = zactor_new (zproxy, NULL);β boom, new zproxy actor. She switches messages between a frontend socket and a backend socket, and you configure those two with theFRONTENDandBACKENDcommands. set the stage first, bestie π - Destroying one:
zactor_destroy (&proxy);β kills the instance. Both sockets go poof, and any message flow between them stops. no lingering ghosts, we love a clean exit π - The big rule: every single zproxy command is synchronous. Your application always waits for a signal back from the actor after each command. So don't be out here sending commands and running off β that's not the vibe.
zsock_wait (proxy)is your bestie here. π«Ά VERBOSEβ turns on verbose logging of commands and activity.zstr_send (proxy, "VERBOSE");thenzsock_wait (proxy);. for when you need to see everything that's happening, nosy queen behavior πFRONTENDβ sets the frontend socket type (peepzsock_type_str ()) and attaches it to endpoints (peepzsock_attach ()). Note: a proxy socket is always serverish.zstr_sendx (proxy, "FRONTEND", "XSUB", endpoints, NULL);then wait. π- subscribing, lowkey β when the socket type is
XSUBorSUB, you can pass topic string(s) as extra arguments and the socket will subscribe using them. β οΈ NOTE: this is in DRAFT state β tread accordingly. πΈ BACKENDβ sets the backend socket type (again,zsock_type_str ()) and attaches it to endpoints (again,zsock_attach ()). And again: a proxy socket is always serverish.zstr_sendx (proxy, "BACKEND", "XPUB", endpoints, NULL);then wait. π¦CAPTUREβ grabs all the proxied messages. They get delivered to your application through aninprocPULLsocket that you already bound to the endpoint you specify.zstr_sendx (proxy, "CAPTURE", endpoint, NULL);then wait. witness protection for your messages fr fr πPAUSEβ pauses the proxy. And listen, this one has consequences: a paused proxy stops processing messages, so they queue up and can potentially hit the high-water mark on the frontend or backend socket. Which means messages can get dropped, or writing applications can block. Not a casual command.zstr_sendx (proxy, "PAUSE", NULL);then wait. πRESUMEβ unpauses. Note that the proxy starts automatically as soon as it has a properly attached frontend and backend socket, so you don't have to kick it off yourself.zstr_sendx (proxy, "RESUME", NULL);then wait. we're so back π₯DOMAINβ configures an authentication domain for the"FRONTEND"or"BACKEND"proxy socket (peepzsock_set_zap_domain ()). Call it before binding the socket.zstr_sendx (proxy, "DOMAIN", "FRONTEND", "global", NULL);then wait. πPLAINβ configures PLAIN authentication for the"FRONTEND"or"BACKEND"proxy socket (peepzsock_set_plain_server ()). Also call before binding the socket.zstr_sendx (proxy, "PLAIN", "BACKEND", NULL);then wait. βCURVEβ configures CURVE authentication for the"FRONTEND"or"BACKEND"proxy socket (peepzsock_set_curve_server ()). You gotta specify both the public and secret keys of a certificate as Z85 armored strings β that'szcert_public_txt ()andzcert_secret_txt (). Before binding the socket, bestie.zstr_sendx (proxy, "CURVE", "FRONTEND", public_txt, secret_txt, NULL);then wait. keys out, doors locked π- the constructor β this is
zproxyas azactor_fn. Its argument is a character string specifying the frontend and backend socket types as two uppercase strings separated by a hyphen. π - selftest β
zproxy_test (bool verbose);does the selftest thing. π§ - and a lil note from the source: Please add '@interface' section in './../src/zproxy.c'. the code is asking politely, we respect that π
π DESCRIPTION π
A zproxy actor switches messages between a frontend and a backend socket. She acts a whole lot like the zmq_proxy_steerable method β but she takes advantage of CZMQ's facilities to be, like, noticeably simpler to set up. Same energy, less homework. β¨
heads up: this class replaceszproxy_v2, and it's meant for applications using the CZMQ v3 API β meaning,zsock. If that's not you, this isn't your girl. π¦
π EXAMPLE π
straight from the zproxy_test method. read it slow, it's basically the whole tutorial:
// Create and configure our proxy
zactor_t *proxy = zactor_new (zproxy, NULL);
assert (proxy);
if (verbose) {
zstr_sendx (proxy, "VERBOSE", NULL);
zsock_wait (proxy);
}
zstr_sendx (proxy, "FRONTEND", "PULL", "inproc://frontend", NULL);
zsock_wait (proxy);
zstr_sendx (proxy, "BACKEND", "PUSH", "inproc://backend", NULL);
zsock_wait (proxy);
// Connect application sockets to proxy
zsock_t *faucet = zsock_new_push (">inproc://frontend");
assert (faucet);
zsock_t *sink = zsock_new_pull (">inproc://backend");
assert (sink);
// Send some messages and check they arrived
char *hello, *world;
zstr_sendx (faucet, "Hello", "World", NULL);
zstr_recvx (sink, &hello, &world, NULL);
assert (streq (hello, "Hello"));
assert (streq (world, "World"));
zstr_free (&hello);
zstr_free (&world);
// Test pause/resume functionality
zstr_sendx (proxy, "PAUSE", NULL);
zsock_wait (proxy);
zstr_sendx (faucet, "Hello", "World", NULL);
zsock_set_rcvtimeo (sink, 100);
zstr_recvx (sink, &hello, &world, NULL);
assert (!hello && !world);
zstr_sendx (proxy, "RESUME", NULL);
zsock_wait (proxy);
zstr_recvx (sink, &hello, &world, NULL);
assert (streq (hello, "Hello"));
assert (streq (world, "World"));
zstr_free (&hello);
zstr_free (&world);
// Test capture functionality
zsock_t *capture = zsock_new_pull ("inproc://capture");
assert (capture);
// Switch on capturing, check that it works
zstr_sendx (proxy, "CAPTURE", "inproc://capture", NULL);
zsock_wait (proxy);
zstr_sendx (faucet, "Hello", "World", NULL);
zstr_recvx (sink, &hello, &world, NULL);
assert (streq (hello, "Hello"));
assert (streq (world, "World"));
zstr_free (&hello);
zstr_free (&world);
zstr_recvx (capture, &hello, &world, NULL);
assert (streq (hello, "Hello"));
assert (streq (world, "World"));
zstr_free (&hello);
zstr_free (&world);
zsock_destroy (&faucet);
zsock_destroy (&sink);
zsock_destroy (&capture);
zactor_destroy (&proxy);
// Test socket creation dependency
proxy = zactor_new (zproxy, NULL);
assert (proxy);
char *frontend = NULL;
char *backend = NULL;
backend = zsys_sprintf (LOCALENDPOINT, s_get_available_port ());
zclock_sleep (200);
sink = zsock_new_sub (backend, "whatever");
assert (sink);
zstr_sendx (proxy, "BACKEND", "XPUB", backend, NULL);
zsock_wait (proxy);
zsock_destroy(&sink);
zactor_destroy(&proxy);
zstr_free (&backend);
#ifdef CZMQ_BUILD_DRAFT_API
// Create and configure our proxy with PUB/SUB to test subscriptions
proxy = zactor_new (zproxy, NULL);
assert (proxy);
if (verbose) {
zstr_sendx (proxy, "VERBOSE", NULL);
zsock_wait (proxy);
}
zstr_sendx (proxy, "FRONTEND", "SUB", "inproc://frontend", "He", "b", NULL);
zsock_wait (proxy);
zstr_sendx (proxy, "BACKEND", "PUB", "inproc://backend", NULL);
zsock_wait (proxy);
// Connect application sockets to proxy
faucet = zsock_new_pub (">inproc://frontend");
assert (faucet);
sink = zsock_new_sub (">inproc://backend", "");
assert (sink);
// Send some messages and check they arrived
zstr_sendx (faucet, "Hello", "World", NULL);
// since SUB is binding, subscription might be lost see:
// https://github.com/zeromq/libzmq/issues/2267
zsock_set_rcvtimeo (sink, 100);
hello = zstr_recv (sink);
if (hello) {
assert (streq (hello, "Hello"));
world = zstr_recv (sink);
assert (streq (world, "World"));
zstr_free (&hello);
zstr_free (&world);
}
zsock_destroy (&faucet);
zsock_destroy (&sink);
zactor_destroy(&proxy);
#endif // CZMQ_BUILD_DRAFT_API
#if (ZMQ_VERSION_MAJOR == 4)
// Test authentication functionality
const char *basedirpath = "src/selftest-rw/.test_zproxy";
const char *passfilepath = "src/selftest-rw/.test_zproxy/password-file";
const char *certfilepath = "src/selftest-rw/.test_zproxy/mycert.txt";
// Make sure old aborted tests do not hinder us
zdir_t *dir = zdir_new (basedirpath, NULL);
if (dir) {
zdir_remove (dir, true);
zdir_destroy (&dir);
}
zsys_file_delete (passfilepath);
zsys_file_delete (certfilepath);
zsys_dir_delete (basedirpath);
// Create temporary directory for test files
zsys_dir_create (basedirpath);
// Check there's no authentication
s_create_test_sockets (&proxy, &faucet, &sink, verbose);
s_bind_test_sockets (proxy, &frontend, &backend);
bool success = s_can_connect (&proxy, &faucet, &sink, frontend, backend,
verbose, true);
assert (success);
// Install the authenticator
zactor_t *auth = zactor_new (zauth, NULL);
assert (auth);
if (verbose) {
zstr_sendx (auth, "VERBOSE", NULL);
zsock_wait (auth);
}
// Check there's no authentication on a default NULL server
s_bind_test_sockets (proxy, &frontend, &backend);
success = s_can_connect (&proxy, &faucet, &sink, frontend, backend, verbose,
true);
assert (success);
// When we set a domain on the server, we switch on authentication
// for NULL sockets, but with no policies, the client connection
// will be allowed.
zstr_sendx (proxy, "DOMAIN", "FRONTEND", "global", NULL);
zsock_wait (proxy);
s_bind_test_sockets (proxy, &frontend, &backend);
success = s_can_connect (&proxy, &faucet, &sink, frontend, backend, verbose,
true);
assert (success);
// Block 127.0.0.1, connection should fail
zstr_sendx (proxy, "DOMAIN", "FRONTEND", "global", NULL);
zsock_wait (proxy);
s_bind_test_sockets (proxy, &frontend, &backend);
zstr_sendx (auth, "DENY", "127.0.0.1", NULL);
zsock_wait (auth);
success = s_can_connect (&proxy, &faucet, &sink, frontend, backend, verbose,
false);
assert (!success);
and that's where this part wraps up, bestie β there's more coming, so don't wander off π«Ά
...and here's the rest of the example, bestie β the whole test flow from here on out, straight up, no cap:
// Allow our address, which overrides the block list
zstr_sendx (proxy, "DOMAIN", "FRONTEND", "global", NULL);
zsock_wait (proxy);
zstr_sendx (proxy, "DOMAIN", "BACKEND", "global", NULL);
zsock_wait (proxy);
s_bind_test_sockets (proxy, &frontend, &backend);
zstr_sendx (auth, "ALLOW", "127.0.0.1", NULL);
zsock_wait (auth);
success = s_can_connect (&proxy, &faucet, &sink, frontend, backend, verbose,
true);
assert (success);
// Try PLAIN authentication
// Test negative case (no server-side passwords defined)
zstr_sendx (proxy, "PLAIN", "FRONTEND", NULL);
zsock_wait (proxy);
zstr_sendx (proxy, "DOMAIN", "FRONTEND", "global", NULL);
zsock_wait (proxy);
s_bind_test_sockets (proxy, &frontend, &backend);
zsock_set_plain_username (faucet, "admin");
zsock_set_plain_password (faucet, "Password");
success = s_can_connect (&proxy, &faucet, &sink, frontend, backend, verbose,
false);
assert (!success);
// Test positive case (server-side passwords defined)
FILE *password = fopen (passfilepath, "w");
assert (password);
fprintf (password, "admin=Password\n");
fclose (password);
zstr_sendx (proxy, "PLAIN", "FRONTEND", NULL);
zsock_wait (proxy);
zstr_sendx (proxy, "DOMAIN", "FRONTEND", "global", NULL);
zsock_wait (proxy);
zstr_sendx (proxy, "PLAIN", "BACKEND", NULL);
zsock_wait (proxy);
zstr_sendx (proxy, "DOMAIN", "BACKEND", "global", NULL);
zsock_wait (proxy);
s_bind_test_sockets (proxy, &frontend, &backend);
zsock_set_plain_username (faucet, "admin");
zsock_set_plain_password (faucet, "Password");
zsock_set_plain_username (sink, "admin");
zsock_set_plain_password (sink, "Password");
zstr_sendx (auth, "PLAIN", passfilepath, NULL);
zsock_wait (auth);
success = s_can_connect (&proxy, &faucet, &sink, frontend, backend, verbose,
true);
assert (success);
// Test negative case (bad client password)
zstr_sendx (proxy, "PLAIN", "FRONTEND", NULL);
zsock_wait (proxy);
zstr_sendx (proxy, "DOMAIN", "FRONTEND", "global", NULL);
zsock_wait (proxy);
s_bind_test_sockets (proxy, &frontend, &backend);
zsock_set_plain_username (faucet, "admin");
zsock_set_plain_password (faucet, "Bogus");
success = s_can_connect (&proxy, &faucet, &sink, frontend, backend, verbose,
false);
assert (!success);
if (zsys_has_curve ()) {
// We'll create two new certificates and save the client public
// certificate on disk
zcert_t *server_cert = zcert_new ();
assert (server_cert);
zcert_t *client_cert = zcert_new ();
assert (client_cert);
const char *public_key = zcert_public_txt (server_cert);
const char *secret_key = zcert_secret_txt (server_cert);
// Try CURVE authentication
// Test without setting-up any authentication
zstr_sendx (proxy, "CURVE", "FRONTEND", public_key, secret_key, NULL);
zsock_wait (proxy);
zstr_sendx (proxy, "DOMAIN", "FRONTEND", "global", NULL);
zsock_wait (proxy);
s_bind_test_sockets (proxy, &frontend, &backend);
zcert_apply (client_cert, faucet);
zsock_set_curve_serverkey (faucet, public_key);
success = s_can_connect (&proxy, &faucet, &sink, frontend, backend,
verbose, false);
assert (!success);
// Test CURVE_ALLOW_ANY
zstr_sendx (proxy, "CURVE", "FRONTEND", public_key, secret_key, NULL);
zsock_wait (proxy);
s_bind_test_sockets (proxy, &frontend, &backend);
zcert_apply (client_cert, faucet);
zsock_set_curve_serverkey (faucet, public_key);
zstr_sendx (auth, "CURVE", CURVE_ALLOW_ANY, NULL);
zsock_wait (auth);
success = s_can_connect (&proxy, &faucet, &sink, frontend, backend,
verbose, true);
assert (success);
// Test with client certificate file in authentication folder
zstr_sendx (proxy, "CURVE", "FRONTEND", public_key, secret_key, NULL);
zsock_wait (proxy);
zstr_sendx (proxy, "CURVE", "BACKEND", public_key, secret_key, NULL);
zsock_wait (proxy);
s_bind_test_sockets (proxy, &frontend, &backend);
zcert_apply (client_cert, faucet);
zsock_set_curve_serverkey (faucet, public_key);
zcert_apply (client_cert, sink);
zsock_set_curve_serverkey (sink, public_key);
zcert_save_public (client_cert, certfilepath);
zstr_sendx (auth, "CURVE", basedirpath, NULL);
zsock_wait (auth);
success = s_can_connect (&proxy, &faucet, &sink, frontend, backend,
verbose, true);
assert (success);
zcert_destroy (&server_cert);
zcert_destroy (&client_cert);
}
// Remove the authenticator and check a normal connection works
zactor_destroy (&auth);
s_bind_test_sockets (proxy, &frontend, &backend);
success = s_can_connect (&proxy, &faucet, &sink, frontend, backend, verbose,
true);
assert (success);
// Cleanup
zsock_destroy (&faucet);
zsock_destroy (&sink);
zactor_destroy (&proxy);
zstr_free (&frontend);
zstr_free (&backend);
// Delete temporary directory and test files
zsys_file_delete (passfilepath);
zsys_file_delete (certfilepath);
zsys_dir_delete (basedirpath);
#endif
#if defined (__WINDOWS__)
zsys_shutdown();
#endif
β¨ AUTHORS β¨
The czmq manual? Written by the authors in the AUTHORS file, periodt. π
π RESOURCES π
Main web site: that's the place to go, bestie π
Report bugs to the email <code>zeromq-dev@lists.zeromq.org[1]</code> β like, please actually tell them, they wanna know ππ
π COPYRIGHT π
Copyright (c) the Contributors as noted in the AUTHORS file. This file is part of CZMQ, the high-level C binding for 0MQ: http://czmq.zeromq.org. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. LICENSE included with the czmq distribution. π
okay so legally this is the whole vibe β MPL v2.0, no take-backs, the license travels with the code fr fr π
π NOTES π
- <code>zeromq-dev@lists.zeromq.org</code> <code>mailto:zeromq-dev@lists.zeromq.org</code>
And that's a wrap, bestie β this whole page is CZMQ version 4.2.1, dated 01/16/2026. Go forth and proxy, you absolute icon π
β¨π¦
π¦ where this page comes from
- fedora 44: czmq-devel 4.2.1-24.fc44 Β· MPL-2.0
- fedora 44: czmq-devel 4.2.1-24.fc44 Β· MPL-2.0
- ubuntu resolute: libczmq-dev 4.2.1-3build1 Β· MPL-2.0
- ubuntu stonking: libczmq-dev 4.2.1-3build1 Β· MPL-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms