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 the FRONTEND and BACKEND commands. 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"); then zsock_wait (proxy);. for when you need to see everything that's happening, nosy queen behavior πŸ‘‘
  • FRONTEND β€” sets the frontend socket type (peep zsock_type_str ()) and attaches it to endpoints (peep zsock_attach ()). Note: a proxy socket is always serverish. zstr_sendx (proxy, "FRONTEND", "XSUB", endpoints, NULL); then wait. πŸ’œ
  • subscribing, lowkey β€” when the socket type is XSUB or SUB, 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 an inproc PULL socket 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 (peep zsock_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 (peep zsock_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 (peep zsock_set_curve_server ()). You gotta specify both the public and secret keys of a certificate as Z85 armored strings β€” that's zcert_public_txt () and zcert_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 zproxy as a zactor_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 replaces zproxy_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 (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 πŸ“

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