int main (int argc, char *argv []) { // Let's start a new Malamute broker zactor_t *broker = zactor_new (mlm_server, NULL); // Switch on verbose tracing... this gets a little overwhelming so you // can comment or delete this when you're bored with it: zsock_send (broker, "s", "VERBOSE"); // We control the broker by sending it commands. It's a CZMQ actor, and // we can talk to it using the zsock API (or zstr, or zframe, or zmsg). // To get things started, let's tell the broker to bind to an endpoint: // zsock_send (broker, "ss", "BIND", "tcp://*:12345"); // This is how we configure a server from an external config file, which // is in http://rfc.zeromq.org/spec:4/ZPL format: zstr_sendx (broker, "LOAD", "src/malamute.cfg", NULL); // We can also, or alternatively, set server properties by sending it // SET commands like this (see malamute.cfg for details): zsock_send (broker, "sss", "SET", "server/timeout", "5000"); // For PLAIN authentication, we start a zauth instance. This handles // all client connection requests by checking against a password file zactor_t *auth = zactor_new (zauth, NULL); assert (auth); // We can switch on verbose tracing to debug authentication errors zstr_sendx (auth, "VERBOSE", NULL); zsock_wait (auth); // Now specify the password file; each line says 'username=password' zstr_sendx (auth, "PLAIN", "src/passwords.cfg", NULL); zsock_wait (auth); // The broker is now running. Let's start two clients, one to publish // messages and one to receive them. We're going to test the stream // pattern with some natty wildcard patterns. mlm_client_t *reader = mlm_client_new (); assert (reader); int rc = mlm_client_set_plain_auth (reader, "reader", "secret"); assert (rc == 0); rc = mlm_client_connect (reader, "tcp://127.0.0.1:9999", 1000, "reader"); assert (rc == 0); mlm_client_t *writer = mlm_client_new (); assert (writer); rc = mlm_client_set_plain_auth (writer, "writer", "secret"); assert (rc == 0); rc = mlm_client_connect (writer, "tcp://127.0.0.1:9999", 1000, "writer"); assert (rc == 0); // The writer publishes to the "weather" stream mlm_client_set_producer (writer, "weather"); // The reader consumes temperature messages off the "weather" stream mlm_client_set_consumer (reader, "weather", "temp.*"); // The writer sends a series of messages with various subjects. The // sendx method sends string data to the stream (we send the subject, // then one or more strings): mlm_client_sendx (writer, "temp.moscow", "1", NULL); mlm_client_sendx (writer, "rain.moscow", "2", NULL); mlm_client_sendx (writer, "temp.madrid", "3", NULL); mlm_client_sendx (writer, "rain.madrid", "4", NULL); mlm_client_sendx (writer, "temp.london", "5", NULL); mlm_client_sendx (writer, "rain.london", "6", NULL); // The simplest way to receive a message is via the recvx method, // which stores multipart string data: char *subject, *content; mlm_client_recvx (reader, &subject, &content, NULL); assert (streq (subject, "temp.moscow")); assert (streq (content, "1")); zstr_free (&subject); zstr_free (&content); // The last-received message has other properties: assert (streq (mlm_client_subject (reader), "temp.moscow")); assert (streq (mlm_client_command (reader), "STREAM DELIVER")); assert (streq (mlm_client_sender (reader), "writer")); assert (streq (mlm_client_address (reader), "weather")); // Let's get the other two messages: mlm_client_recvx (reader, &subject, &content, NULL); assert (streq (subject, "temp.madrid")); assert (streq (content, "3")); zstr_free (&subject); zstr_free (&content); mlm_client_recvx (reader, &subject, &content, NULL); assert (streq (subject, "temp.london")); assert (streq (content, "5")); zstr_free (&subject); zstr_free (&content); // Great, it all works. Now to shutdown, we use the destroy method, // which does a proper deconnect handshake internally: mlm_client_destroy (&reader); mlm_client_destroy (&writer); // Finally, shut down the broker by destroying the actor; this does // a proper shutdown so that all memory is freed as you'd expect. zactor_destroy (&broker); zactor_destroy (&auth); return 0; }
void mlm_server_test (bool verbose) { printf (" * mlm_server: "); if (verbose) printf ("\n"); // @selftest zactor_t *server = zactor_new (mlm_server, "mlm_server_test"); if (verbose) zstr_send (server, "VERBOSE"); zstr_sendx (server, "BIND", "tcp://127.0.0.1:*", NULL); zstr_sendx (server, "PORT", NULL); char *command, *port; int rc = zstr_recvx (server, &command, &port, NULL); assert (rc == 2); assert (streq (command, "PORT")); assert (strlen (port) > 0 && strlen (port) < 6); assert (!streq (port, "-1")); zsock_t *reader = zsock_new (ZMQ_DEALER); assert (reader); zsock_connect (reader, "tcp://127.0.0.1:%s", port); zsock_set_rcvtimeo (reader, 500); mlm_proto_t *proto = mlm_proto_new (); // Server insists that connection starts properly mlm_proto_set_id (proto, MLM_PROTO_STREAM_WRITE); mlm_proto_send (proto, reader); zclock_sleep (500); // to calm things down && make memcheck pass. Thanks @malanka mlm_proto_recv (proto, reader); zclock_sleep (500); // detto as above assert (mlm_proto_id (proto) == MLM_PROTO_ERROR); assert (mlm_proto_status_code (proto) == MLM_PROTO_COMMAND_INVALID); // Now do a stream publish-subscribe test zsock_t *writer = zsock_new (ZMQ_DEALER); assert (writer); zsock_connect (writer, "tcp://127.0.0.1:%s", port); zsock_set_rcvtimeo (reader, 500); // Open connections from both reader and writer mlm_proto_set_id (proto, MLM_PROTO_CONNECTION_OPEN); mlm_proto_send (proto, reader); mlm_proto_recv (proto, reader); assert (mlm_proto_id (proto) == MLM_PROTO_OK); mlm_proto_set_id (proto, MLM_PROTO_CONNECTION_OPEN); mlm_proto_send (proto, writer); mlm_proto_recv (proto, writer); assert (mlm_proto_id (proto) == MLM_PROTO_OK); // Prepare to write and read a "weather" stream mlm_proto_set_id (proto, MLM_PROTO_STREAM_WRITE); mlm_proto_set_stream (proto, "weather"); mlm_proto_send (proto, writer); mlm_proto_recv (proto, writer); assert (mlm_proto_id (proto) == MLM_PROTO_OK); mlm_proto_set_id (proto, MLM_PROTO_STREAM_READ); mlm_proto_set_pattern (proto, "temp.*"); mlm_proto_send (proto, reader); mlm_proto_recv (proto, reader); assert (mlm_proto_id (proto) == MLM_PROTO_OK); // Now send some weather data, with null contents mlm_proto_set_id (proto, MLM_PROTO_STREAM_SEND); mlm_proto_set_subject (proto, "temp.moscow"); mlm_proto_send (proto, writer); mlm_proto_set_subject (proto, "rain.moscow"); mlm_proto_send (proto, writer); mlm_proto_set_subject (proto, "temp.chicago"); mlm_proto_send (proto, writer); mlm_proto_set_subject (proto, "rain.chicago"); mlm_proto_send (proto, writer); mlm_proto_set_subject (proto, "temp.london"); mlm_proto_send (proto, writer); mlm_proto_set_subject (proto, "rain.london"); mlm_proto_send (proto, writer); // We should receive exactly three deliveries, in order mlm_proto_recv (proto, reader); assert (mlm_proto_id (proto) == MLM_PROTO_STREAM_DELIVER); assert (streq (mlm_proto_subject (proto), "temp.moscow")); mlm_proto_recv (proto, reader); assert (mlm_proto_id (proto) == MLM_PROTO_STREAM_DELIVER); assert (streq (mlm_proto_subject (proto), "temp.chicago")); mlm_proto_recv (proto, reader); assert (mlm_proto_id (proto) == MLM_PROTO_STREAM_DELIVER); assert (streq (mlm_proto_subject (proto), "temp.london")); mlm_proto_destroy (&proto); // Finished, we can clean up zsock_destroy (&writer); zsock_destroy (&reader); zactor_destroy (&server); zstr_free (&port); zstr_free (&command); // Test Case: // CLIENTLIST command { const char *endpoint = "inproc://mlm_server_clientlist_test"; zactor_t *server = zactor_new (mlm_server, "mlm_server_clientlist_test"); if (verbose) zstr_send (server, "VERBOSE"); zstr_sendx (server, "BIND", endpoint, NULL); mlm_client_t *client_1 = mlm_client_new (); int rv = mlm_client_connect (client_1, endpoint, 1000, "Karol"); assert (rv >= 0); mlm_client_t *client_2 = mlm_client_new (); rv = mlm_client_connect (client_2, endpoint, 1000, "Tomas"); assert (rv >= 0); mlm_client_t *client_3 = mlm_client_new (); rv = mlm_client_connect (client_3, endpoint, 1000, "Alenka"); assert (rv >= 0); zclock_sleep (500); zstr_sendx (server, "CLIENTLIST", NULL); zmsg_t *message = zmsg_recv (server); assert (message); assert (zmsg_size (message) == 4); char *pop = zmsg_popstr (message); assert (streq (pop, "CLIENTLIST")); zstr_free (&pop); zlistx_t *expected_names = zlistx_new (); assert (expected_names); zlistx_set_destructor (expected_names, (czmq_destructor *) zstr_free); zlistx_set_duplicator (expected_names, (czmq_duplicator *) strdup); zlistx_set_comparator (expected_names, (czmq_comparator *) strcmp); zlistx_add_end (expected_names, (void *) "Karol"); zlistx_add_end (expected_names, (void *) "Tomas"); zlistx_add_end (expected_names, (void *) "Alenka"); pop = zmsg_popstr (message); assert (pop); void *handle = zlistx_find (expected_names, pop); assert (handle); rv = zlistx_delete (expected_names, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_names, pop); assert (handle); rv = zlistx_delete (expected_names, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_names, pop); assert (handle); rv = zlistx_delete (expected_names, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop == NULL); assert (zlistx_size (expected_names) == 0); zmsg_destroy (&message); // remove a client Karol mlm_client_destroy (&client_1); zlistx_add_end (expected_names, (void *) "Tomas"); zlistx_add_end (expected_names, (void *) "Alenka"); zstr_sendx (server, "CLIENTLIST", NULL); zclock_sleep (100); message = zmsg_recv (server); assert (message); assert (zmsg_size (message) == 3); pop = zmsg_popstr (message); assert (streq (pop, "CLIENTLIST")); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_names, pop); assert (handle); rv = zlistx_delete (expected_names, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_names, pop); assert (handle); rv = zlistx_delete (expected_names, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop == NULL); assert (zlistx_size (expected_names) == 0); zlistx_destroy (&expected_names); zmsg_destroy (&message); mlm_client_destroy (&client_2); mlm_client_destroy (&client_3); zactor_destroy (&server); } // Test Case: // STREAMLIST command { const char *endpoint = "inproc://mlm_server_streamlist_test"; zactor_t *server = zactor_new (mlm_server, "mlm_server_streamlist_test"); if (verbose) zstr_send (server, "VERBOSE"); zstr_sendx (server, "BIND", endpoint, NULL); mlm_client_t *client_1 = mlm_client_new (); int rv = mlm_client_connect (client_1, endpoint, 1000, "Karol"); assert (rv != -1); rv = mlm_client_set_producer (client_1, "STREAM_1"); assert (rv != -1); mlm_client_t *client_2 = mlm_client_new (); rv = mlm_client_connect (client_2, endpoint, 1000, "Tomas"); assert (rv != -1); rv = mlm_client_set_producer (client_2, "STREAM_2"); assert (rv != -1); mlm_client_t *client_3 = mlm_client_new (); rv = mlm_client_connect (client_3, endpoint, 1000, "Alenka"); assert (rv != -1); rv = mlm_client_set_consumer (client_3, "STREAM_2", ".*"); assert (rv != -1); zclock_sleep (100); zlistx_t *expected_streams = zlistx_new (); assert (expected_streams); zlistx_set_destructor (expected_streams, (czmq_destructor *) zstr_free); zlistx_set_duplicator (expected_streams, (czmq_duplicator *) strdup); zlistx_set_comparator (expected_streams, (czmq_comparator *) strcmp); zlistx_add_end (expected_streams, (void *) "STREAM_1"); zlistx_add_end (expected_streams, (void *) "STREAM_2"); zstr_sendx (server, "STREAMLIST", NULL); zmsg_t *message = zmsg_recv (server); assert (message); assert (zmsg_size (message) == 3); char *pop = zmsg_popstr (message); assert (streq (pop, "STREAMLIST")); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); void *handle = zlistx_find (expected_streams, pop); assert (handle); rv = zlistx_delete (expected_streams, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_streams, pop); assert (handle); rv = zlistx_delete (expected_streams, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop == NULL); assert (zlistx_size (expected_streams) == 0); zmsg_destroy (&message); // NOTE: Currently when producer disconnects, malamute does not destroy the stream // Therefore it doesn't make sense to test removal of streams, but addition mlm_client_t *client_4 = mlm_client_new (); rv = mlm_client_connect (client_4, endpoint, 1000, "Michal"); assert (rv >= 0); rv = mlm_client_set_producer (client_4, "New stream"); assert (rv >= 0); zlistx_add_end (expected_streams, (void *) "STREAM_1"); zlistx_add_end (expected_streams, (void *) "STREAM_2"); zlistx_add_end (expected_streams, (void *) "New stream"); zclock_sleep (100); zstr_sendx (server, "STREAMLIST", NULL); zclock_sleep (100); message = zmsg_recv (server); assert (message); assert (zmsg_size (message) == 4); pop = zmsg_popstr (message); assert (streq (pop, "STREAMLIST")); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_streams, pop); assert (handle); rv = zlistx_delete (expected_streams, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_streams, pop); assert (handle); rv = zlistx_delete (expected_streams, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop); handle = zlistx_find (expected_streams, pop); assert (handle); rv = zlistx_delete (expected_streams, handle); assert (rv == 0); zstr_free (&pop); pop = zmsg_popstr (message); assert (pop == NULL); assert (zlistx_size (expected_streams) == 0); zlistx_destroy (&expected_streams); zmsg_destroy (&message); mlm_client_destroy (&client_1); mlm_client_destroy (&client_2); mlm_client_destroy (&client_3); mlm_client_destroy (&client_4); zactor_destroy (&server); } // Regression Test Case: // Segfault from deregistering zombie connection { const char *endpoint = "inproc://mlm_server_deregister_zombie_connection_test"; zactor_t *server = zactor_new (mlm_server, "mlm_server_deregister_zombie_connection_test"); if (verbose) zstr_send (server, "VERBOSE"); zstr_sendx (server, "BIND", endpoint, NULL); zstr_sendx (server, "SET", "server/timeout", "3000", NULL); // 3 second client timeout zsock_t *reader = zsock_new (ZMQ_DEALER); assert (reader); zsock_connect (reader, "inproc://mlm_server_deregister_zombie_connection_test"); zsock_set_rcvtimeo (reader, 500); mlm_proto_t *proto = mlm_proto_new (); // If the malamute server is restarted and clients have queued // up ping messages, the'll be sent before any // CONNECTION_OPEN. The server eventually tries to deregister // this and (previously) would derefence a null pointer for // the client address. mlm_proto_set_id (proto, MLM_PROTO_CONNECTION_PING); mlm_proto_send (proto, reader); printf("Regression test for segfault due to leftover client messages after restart...\n"); // Give the server more than 3 seconds to time out the client... zclock_sleep (3100); printf("passed\n"); mlm_proto_destroy (&proto); zsock_destroy (&reader); zactor_destroy (&server); } { const char *endpoint = "inproc://mlm_server_disconnect_pending_stream_traffic"; zactor_t *server = zactor_new (mlm_server, "mlm_server_disconnect_pending_stream_traffic"); if (verbose) { zstr_send (server, "VERBOSE"); printf("Regression test for use-after-free with pending stream traffic after disconnect\n"); } zstr_sendx (server, "BIND", endpoint, NULL); mlm_client_t *producer = mlm_client_new (); assert (mlm_client_connect (producer, endpoint, 1000, "producer") >= 0); assert (mlm_client_set_producer (producer, "STREAM_TEST") >= 0); zstr_sendx (server, "SLOW_TEST_MODE", NULL); mlm_client_t *consumer = mlm_client_new (); assert (mlm_client_connect (consumer, endpoint, 1000, "consumer") >= 0); assert (mlm_client_set_consumer (consumer, "STREAM_TEST", ".*") >= 0); zmsg_t *msg = zmsg_new (); zmsg_addstr (msg, "world"); assert (mlm_client_send (producer, "hello", &msg) >= 0); mlm_client_destroy (&consumer); zclock_sleep (2000); mlm_client_destroy (&producer); zactor_destroy (&server); } // @end printf ("OK\n"); }
// --------------------------------------------------------------------------- // Selftest void mlm_client_test (bool verbose) { printf (" * mlm_client: \n"); // @selftest mlm_client_verbose = verbose; // Start a server to test against, and bind to endpoint zactor_t *server = zactor_new (mlm_server, "mlm_client_test"); if (verbose) zstr_send (server, "VERBOSE"); zstr_sendx (server, "LOAD", "src/mlm_client.cfg", NULL); // Install authenticator to test PLAIN access zactor_t *auth = zactor_new (zauth, NULL); assert (auth); if (verbose) { zstr_sendx (auth, "VERBOSE", NULL); zsock_wait (auth); } zstr_sendx (auth, "PLAIN", "src/passwords.cfg", NULL); zsock_wait (auth); ///// // Test mutual address exchange /** ----------- ---------- | Frontend | -> 1) provide addr, req opp addr -> |\ / | <- 1) Provide addr, Req opp addr | Backend | >Broker< ----------- 2) Receive opp addr <- -> 2) Receive opposite address ---------- 3) Direct connection establishment ----------- ----------- | Frontend | <- Send -> | Backend | ----------- ----------- */ mlm_client_t *frontend = mlm_client_new (); assert(frontend); mlm_client_t *backend = mlm_client_new(); assert(backend); // connect front side to broker printf("connecting frontend to broker\n"); int rc = mlm_client_set_plain_auth (frontend, "writer", "secret"); assert (rc == 0); rc=mlm_client_connect (frontend, "tcp://127.0.0.1:9999", 1000, ""); assert (rc == 0); // connect back side to broker printf("connecting backend to broker\n"); rc = mlm_client_set_plain_auth (backend, "reader", "secret"); assert (rc == 0); rc=mlm_client_connect (backend, "tcp://127.0.0.1:9999", 1000, ""); assert (rc == 0); //before you ever set a service, you should've already called bind in order to facilitate a // connection from the other side. In this way, it's causally correct. // the frontend tells the broker that it is interested in addresses that are on the other side printf("setting frontend service\n"); mlm_client_set_worker(frontend, "backendEndpoints", "SET*"); // the backend tells the broker that it is interested in addresses that are on the other side printf("setting backend service\n"); mlm_client_set_worker(backend, "frontendEndpoints", "SET*"); // the frontend tells the broker what it's address is, fulfilling a need that the backend set; // consequently, the broker sends this to the backend, which has set a service that it is // subscribed to. SET* matches SET printf("sending frontend address SET message\n"); mlm_client_sendforx(frontend, "frontendEndpoints", "SET", "inproc://frontend", NULL); // the backend tells the broker what it's address is, fullfilling a need that the frontend set; // consequently, the broker sends this to the frontend, which has reported a service to the broker // the broker matches SET to SET*. printf("sending backend address SET message\n"); mlm_client_sendforx(backend, "backendEndpoints", "SET", "inproc://backend", NULL); char *set=NULL, *opp_addr=NULL; printf("receiving on backend"); mlm_client_recvx(backend, &set, &opp_addr, NULL); assert(set); assert(opp_addr); // connect backend to frontend here, then clean up zstr_free(&opp_addr); zstr_free(&set); printf("receiving on backend"); mlm_client_recvx(frontend, &set, &opp_addr, NULL); assert(set); assert(opp_addr); // connect frontend to backend here, then clean up zstr_free(&opp_addr); zstr_free(&set); // cleanup mlm_client_destroy(&backend); mlm_client_destroy(&frontend); // End of mutual address exchange tests ///// // Test stream pattern mlm_client_t *writer = mlm_client_new (); assert (writer); rc = mlm_client_set_plain_auth (writer, "writer", "secret"); assert (rc == 0); assert (mlm_client_connected (writer) == false); rc = mlm_client_connect (writer, "tcp://127.0.0.1:9999", 1000, "writer"); assert (rc == 0); assert (mlm_client_connected (writer) == true); mlm_client_t *reader = mlm_client_new (); assert (reader); rc = mlm_client_set_plain_auth (reader, "reader", "secret"); assert (rc == 0); rc = mlm_client_connect (reader, "tcp://127.0.0.1:9999", 1000, ""); assert (rc == 0); mlm_client_set_producer (writer, "weather"); mlm_client_set_consumer (reader, "weather", "temp.*"); mlm_client_sendx (writer, "temp.moscow", "1", NULL); mlm_client_sendx (writer, "rain.moscow", "2", NULL); mlm_client_sendx (writer, "temp.madrid", "3", NULL); mlm_client_sendx (writer, "rain.madrid", "4", NULL); mlm_client_sendx (writer, "temp.london", "5", NULL); mlm_client_sendx (writer, "rain.london", "6", NULL); char *subject, *content; mlm_client_recvx (reader, &subject, &content, NULL); assert (streq (subject, "temp.moscow")); assert (streq (content, "1")); assert (streq (mlm_client_command (reader), "STREAM DELIVER")); assert (streq (mlm_client_sender (reader), "writer")); zstr_free (&subject); zstr_free (&content); mlm_client_recvx (reader, &subject, &content, NULL); assert (streq (subject, "temp.madrid")); assert (streq (content, "3")); assert (streq (mlm_client_command (reader), "STREAM DELIVER")); assert (streq (mlm_client_subject (reader), "temp.madrid")); assert (streq (mlm_client_sender (reader), "writer")); zstr_free (&subject); zstr_free (&content); mlm_client_recvx (reader, &subject, &content, NULL); assert (streq (subject, "temp.london")); assert (streq (content, "5")); assert (streq (mlm_client_command (reader), "STREAM DELIVER")); assert (streq (mlm_client_sender (reader), "writer")); zstr_free (&subject); zstr_free (&content); mlm_client_destroy (&reader); // Test mailbox pattern reader = mlm_client_new (); assert (reader); rc = mlm_client_set_plain_auth (reader, "reader", "secret"); assert (rc == 0); rc = mlm_client_connect (reader, "tcp://127.0.0.1:9999", 1000, "mailbox"); assert (rc == 0); mlm_client_sendtox (writer, "mailbox", "subject 1", "Message 1", "attachment", NULL); char *attach; mlm_client_recvx (reader, &subject, &content, &attach, NULL); assert (streq (subject, "subject 1")); assert (streq (content, "Message 1")); assert (streq (attach, "attachment")); assert (streq (mlm_client_command (reader), "MAILBOX DELIVER")); assert (streq (mlm_client_subject (reader), "subject 1")); assert (streq (mlm_client_sender (reader), "writer")); zstr_free (&subject); zstr_free (&content); zstr_free (&attach); // Now test that mailbox survives reader disconnect mlm_client_destroy (&reader); mlm_client_sendtox (writer, "mailbox", "subject 2", "Message 2", NULL); mlm_client_sendtox (writer, "mailbox", "subject 3", "Message 3", NULL); reader = mlm_client_new (); assert (reader); rc = mlm_client_set_plain_auth (reader, "reader", "secret"); assert (rc == 0); rc = mlm_client_connect (reader, "tcp://127.0.0.1:9999", 500, "mailbox"); assert (rc == 0); mlm_client_recvx (reader, &subject, &content, &attach, NULL); assert (streq (subject, "subject 2")); assert (streq (content, "Message 2")); assert (streq (mlm_client_command (reader), "MAILBOX DELIVER")); zstr_free (&subject); zstr_free (&content); mlm_client_recvx (reader, &subject, &content, &attach, NULL); assert (streq (subject, "subject 3")); assert (streq (content, "Message 3")); assert (streq (mlm_client_command (reader), "MAILBOX DELIVER")); zstr_free (&subject); zstr_free (&content); // Test service pattern mlm_client_set_worker (reader, "printer", "bw.*"); mlm_client_set_worker (reader, "printer", "color.*"); mlm_client_sendforx (writer, "printer", "bw.A4", "Important contract", NULL); mlm_client_sendforx (writer, "printer", "bw.A5", "Special conditions", NULL); mlm_client_recvx (reader, &subject, &content, NULL); assert (streq (subject, "bw.A4")); assert (streq (content, "Important contract")); assert (streq (mlm_client_command (reader), "SERVICE DELIVER")); assert (streq (mlm_client_sender (reader), "writer")); zstr_free (&subject); zstr_free (&content); mlm_client_recvx (reader, &subject, &content, NULL); assert (streq (subject, "bw.A5")); assert (streq (content, "Special conditions")); assert (streq (mlm_client_command (reader), "SERVICE DELIVER")); assert (streq (mlm_client_sender (reader), "writer")); zstr_free (&subject); zstr_free (&content); // Test that writer shutdown does not cause message loss mlm_client_set_consumer (reader, "weather", "temp.*"); mlm_client_sendx (writer, "temp.brussels", "7", NULL); mlm_client_destroy (&writer); mlm_client_recvx (reader, &subject, &content, NULL); assert (streq (subject, "temp.brussels")); assert (streq (content, "7")); zstr_free (&subject); zstr_free (&content); mlm_client_destroy (&reader); // Test multiple readers and multiple writers mlm_client_t *writer1 = mlm_client_new (); assert (writer1); rc = mlm_client_set_plain_auth (writer1, "writer", "secret"); assert (rc == 0); rc = mlm_client_connect (writer1, "tcp://127.0.0.1:9999", 1000, ""); assert (rc == 0); mlm_client_t *writer2 = mlm_client_new (); assert (writer2); rc = mlm_client_set_plain_auth (writer2, "writer", "secret"); assert (rc == 0); rc = mlm_client_connect (writer2, "tcp://127.0.0.1:9999", 1000, ""); assert (rc == 0); mlm_client_t *reader1 = mlm_client_new (); assert (reader1); rc = mlm_client_set_plain_auth (reader1, "reader", "secret"); assert (rc == 0); rc = mlm_client_connect (reader1, "tcp://127.0.0.1:9999", 1000, ""); assert (rc == 0); mlm_client_t *reader2 = mlm_client_new (); assert (reader2); rc = mlm_client_set_plain_auth (reader2, "reader", "secret"); assert (rc == 0); rc = mlm_client_connect (reader2, "tcp://127.0.0.1:9999", 1000, ""); assert (rc == 0); mlm_client_set_producer (writer1, "weather"); mlm_client_set_producer (writer2, "traffic"); mlm_client_set_consumer (reader1, "weather", "newyork"); mlm_client_set_consumer (reader1, "traffic", "newyork"); mlm_client_set_consumer (reader2, "weather", "newyork"); mlm_client_set_consumer (reader2, "traffic", "newyork"); mlm_client_sendx (writer1, "newyork", "8", NULL); mlm_client_recvx (reader1, &subject, &content, NULL); assert (streq (mlm_client_address (reader1), "weather")); assert (streq (subject, "newyork")); assert (streq (content, "8")); zstr_free (&subject); zstr_free (&content); mlm_client_recvx (reader2, &subject, &content, NULL); assert (streq (mlm_client_address (reader2), "weather")); assert (streq (subject, "newyork")); assert (streq (content, "8")); zstr_free (&subject); zstr_free (&content); mlm_client_sendx (writer2, "newyork", "85", NULL); mlm_client_recvx (reader1, &subject, &content, NULL); assert (streq (mlm_client_address (reader1), "traffic")); assert (streq (subject, "newyork")); assert (streq (content, "85")); zstr_free (&subject); zstr_free (&content); mlm_client_recvx (reader2, &subject, &content, NULL); assert (streq (mlm_client_address (reader2), "traffic")); assert (streq (subject, "newyork")); assert (streq (content, "85")); zstr_free (&subject); zstr_free (&content); mlm_client_destroy (&writer1); mlm_client_destroy (&writer2); mlm_client_destroy (&reader1); mlm_client_destroy (&reader2); // Done, shut down zactor_destroy (&auth); zactor_destroy (&server); // @end printf ("OK\n"); }