int zloop_poller (zloop_t *self, zmq_pollitem_t *item, zloop_fn handler, void *arg) { assert (self); if (item->socket && streq (zsys_sockname (zsock_type (item->socket)), "UNKNOWN")) return -1; s_poller_t *poller = s_poller_new (item, handler, arg); if (poller) { poller->list_handle = zlistx_add_end (self->pollers, poller); if (!poller->list_handle) { s_poller_destroy (&poller); return -1; } self->need_rebuild = true; if (self->verbose) zsys_debug ("zloop: register %s poller (%p, %d)", item->socket ? zsys_sockname (zsock_type (item->socket)) : "FD", item->socket, item->fd); return 0; } else return -1; }
int zloop_timer (zloop_t *self, size_t delay, size_t times, zloop_timer_fn handler, void *arg) { assert (self); // Catch excessive use of timers if (self->max_timers && zlistx_size (self->timers) == self->max_timers) { zsys_error ("zloop: timer limit reached (max=%d)", self->max_timers); return -1; } int timer_id = s_next_timer_id (self); s_timer_t *timer = s_timer_new (timer_id, delay, times, handler, arg); if (timer) { timer->list_handle = zlistx_add_end (self->timers, timer); if (!timer->list_handle) { s_timer_destroy (&timer); return -1; } if (self->verbose) zsys_debug ("zloop: register timer id=%d delay=%d times=%d", timer_id, (int) delay, (int) times); return timer_id; } else return -1; }
static void server_connect (server_t *self, const char *endpoint) { zsock_t *remote = zsock_new (ZMQ_DEALER); assert (remote); // No recovery if exhausted // Never block on sending; we use an infinite HWM and buffer as many // messages as needed in outgoing pipes. Note that the maximum number // is the overall tuple set size. zsock_set_unbounded (remote); if (zsock_connect (remote, "%s", endpoint)) { zsys_warning ("bad zgossip endpoint '%s'", endpoint); zsock_destroy (&remote); return; } // Send HELLO and then PUBLISH for each tuple we have zgossip_msg_t *gossip = zgossip_msg_new (); zgossip_msg_set_id (gossip, ZGOSSIP_MSG_HELLO); zgossip_msg_send (gossip, remote); tuple_t *tuple = (tuple_t *) zhashx_first (self->tuples); while (tuple) { zgossip_msg_set_id (gossip, ZGOSSIP_MSG_PUBLISH); zgossip_msg_set_key (gossip, tuple->key); zgossip_msg_set_value (gossip, tuple->value); zgossip_msg_send (gossip, remote); tuple = (tuple_t *) zhashx_next (self->tuples); } // Now monitor this remote for incoming messages zgossip_msg_destroy (&gossip); engine_handle_socket (self, remote, remote_handler); zlistx_add_end (self->remotes, remote); }
static void prepare_xrap_offer_command (client_t *self) { zlistx_add_end (self->replays, s_replay_new ("XRAP OFFER", self->args->method, self->args->route)); xrap_traffic_set_method (self->message, self->args->method); xrap_traffic_set_route (self->message, self->args->route); }
static void store_service_offer (client_t *self) { service_t *service = s_service_require (self, mlm_proto_address (self->message)); assert (service); offer_t *offer = s_offer_new (self, mlm_proto_pattern (self->message)); assert (offer); zlistx_add_end (service->offers, offer); }
void * zloop_ticket (zloop_t *self, zloop_timer_fn handler, void *arg) { assert (self); assert (self->ticket_delay > 0); s_ticket_t *ticket = s_ticket_new (self->ticket_delay, handler, arg); if (ticket) { ticket->list_handle = zlistx_add_end (self->tickets, ticket); assert (ticket->list_handle); } return ticket; }
void zhttp_client_test (bool verbose) { #if defined(HAVE_LIBCURL) && defined(ZMQ_STREAM) printf (" * zhttp_client: "); zsock_t *server = zsock_new_stream (NULL); int port = zsock_bind (server, "tcp://127.0.0.1:*"); char url[255]; sprintf (url, "http://127.0.0.1:%d", port); // @selftest // Simple create/destroy test zhttp_client_t *self = zhttp_client_new (verbose); assert (self); // Send the get request zlistx_t *headers = zlistx_new (); zlistx_add_end (headers, "Host: zeromq.org"); zhttp_client_get (self, url, headers, NULL); zlistx_destroy (&headers); // Receive request on the server zchunk_t *routing_id; char *request; int rc = zsock_recv (server, "cs", &routing_id, &request); assert (rc == 0); // Send the response char* response = "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello"; zsock_send (server, "cs", routing_id, response); // Receive the response on the http client int code; zchunk_t *data; zhttp_client_recv (self, &code, &data, NULL); assert (zchunk_streq (data, "Hello")); // Sending another request, without being answer // Checking the client ability to stop while request are inprogres zhttp_client_get (self, url, NULL, NULL); zchunk_destroy (&data); zchunk_destroy (&routing_id); zstr_free (&request); zhttp_client_destroy (&self); zsock_destroy (&server); // @end printf ("OK\n"); #endif }
static void store_stream_reader (client_t *self) { stream_t *stream = s_stream_require (self, mlm_proto_stream (self->message)); if (stream) { zlistx_add_end (self->readers, stream); zsock_send (stream->actor, "sps", "COMPILE", self, mlm_proto_pattern (self->message)); mlm_proto_set_status_code (self->message, MLM_PROTO_SUCCESS); } else { engine_set_exception (self, exception_event); zsys_warning ("reader trying to talk to multiple streams"); } }
static void s_stream_engine_compile (stream_engine_t *self, void *client, const char *pattern) { selector_t *selector = (selector_t *) zlistx_first (self->selectors); while (selector) { if (streq (selector->pattern, pattern)) { void *compare = zlistx_first (selector->clients); while (compare) { if (compare == client) break; // Duplicate client, ignore compare = zlistx_next (selector->clients); } // Add client, if it's new if (!compare) zlistx_add_end (selector->clients, client); break; } selector = (selector_t *) zlistx_next (self->selectors); } // Add selector, if it's new if (!selector) zlistx_add_end (self->selectors, s_selector_new (client, pattern)); }
server_connect (server_t *self, const char *endpoint) #endif { zsock_t *remote = zsock_new (ZMQ_DEALER); assert (remote); // No recovery if exhausted #ifdef CZMQ_BUILD_DRAFT_API // DRAFT-API: Security if (public_key){ zcert_t *cert = zcert_new_from_txt (self->public_key, self->secret_key); zcert_apply(cert, remote); zsock_set_curve_serverkey (remote, public_key); #ifndef ZMQ_CURVE // legacy ZMQ support // inline incase the underlying assert is removed bool ZMQ_CURVE = false; #endif assert (zsock_mechanism (remote) == ZMQ_CURVE); zcert_destroy(&cert); } #endif // Never block on sending; we use an infinite HWM and buffer as many // messages as needed in outgoing pipes. Note that the maximum number // is the overall tuple set size. zsock_set_unbounded (remote); if (zsock_connect (remote, "%s", endpoint)) { zsys_warning ("bad zgossip endpoint '%s'", endpoint); zsock_destroy (&remote); return; } // Send HELLO and then PUBLISH for each tuple we have zgossip_msg_t *gossip = zgossip_msg_new (); zgossip_msg_set_id (gossip, ZGOSSIP_MSG_HELLO); zgossip_msg_send (gossip, remote); tuple_t *tuple = (tuple_t *) zhashx_first (self->tuples); while (tuple) { zgossip_msg_set_id (gossip, ZGOSSIP_MSG_PUBLISH); zgossip_msg_set_key (gossip, tuple->key); zgossip_msg_set_value (gossip, tuple->value); zgossip_msg_send (gossip, remote); tuple = (tuple_t *) zhashx_next (self->tuples); } // Now monitor this remote for incoming messages zgossip_msg_destroy (&gossip); engine_handle_socket (self, remote, remote_handler); zlistx_add_end (self->remotes, remote); }
static void write_message_to_service (client_t *self) { mlm_msg_t *msg = mlm_msg_new ( self->address, mlm_proto_address (self->message), mlm_proto_subject (self->message), mlm_proto_tracker (self->message), mlm_proto_timeout (self->message), mlm_proto_get_content (self->message)); service_t *service = s_service_require (self, mlm_proto_address (self->message)); assert (service); zlistx_add_end (service->queue, msg); s_service_dispatch (service); }
static selector_t * s_selector_new (void *client, const char *pattern) { selector_t *self = (selector_t *) zmalloc (sizeof (selector_t)); if (self) { self->rex = zrex_new (pattern); if (self->rex) self->pattern = strdup (pattern); if (self->pattern) self->clients = zlistx_new (); if (self->clients) zlistx_add_end (self->clients, client); else s_selector_destroy (&self); } return self; }
int zloop_reader (zloop_t *self, zsock_t *sock, zloop_reader_fn handler, void *arg) { assert (self); assert (sock); s_reader_t *reader = s_reader_new (sock, handler, arg); if (reader) { reader->list_handle = zlistx_add_end (self->readers, reader); assert (reader->list_handle); self->need_rebuild = true; if (self->verbose) zsys_debug ("zloop: register %s reader", zsock_type_str (sock)); return 0; } else return -1; }
int zloop_timer_end (zloop_t *self, int timer_id) { assert (self); if (self->terminated) s_timer_remove (self, timer_id); else // We cannot touch self->timers because we may be executing that // from inside the poll loop. So, we hold the arg on the zombie // list, and process that list when we're done executing timers. // This hack lets us store an integer timer ID as a pointer zlistx_add_end (self->zombies, (byte *) NULL + timer_id); if (self->verbose) zsys_debug ("zloop: cancel timer id=%d", timer_id); return 0; }
static ztrie_node_t * s_ztrie_node_new (ztrie_node_t *parent, char *token, int token_len, zlistx_t *param_keys, int token_type) { ztrie_node_t *self = (ztrie_node_t *) zmalloc (sizeof (ztrie_node_t)); assert (self); // Initialize properties self->token = s_strndup (token, token_len); self->token_type = token_type; self->token_len = token_len; self->endpoint = false; self->parameter_count = 0; self->parameter_names = NULL; self->parameter_values = NULL; if (param_keys && zlistx_size (param_keys) > 0) { self->parameter_count = zlistx_size (param_keys); self->parameter_names = (char **) malloc (sizeof (char *) * self->parameter_count); self->parameter_values = (char **) malloc (sizeof (char *) * self->parameter_count); char *key = (char *) zlistx_first (param_keys); int index; for (index = 0; index < zlistx_size (param_keys); index++) { self->parameter_names [index] = key; self->parameter_values [index] = NULL; key = (char *) zlistx_next (param_keys); } } if (self->token_type == NODE_TYPE_REGEX || self->token_type == NODE_TYPE_PARAM) self->regex = zrex_new (self->token); self->data = NULL; // Initialize relationships self->parent = parent; if (self->parent) { zlistx_add_end (self->parent->children, self); // Sort regexes to the end to avoid conlficts zlistx_sort (self->parent->children); } size_t parent_path_len = self->parent? self->parent->path_len: 0; self->path_len = parent_path_len + strlen (self->token) + 1; // +1 path delimiter self->children = zlistx_new (); zlistx_set_comparator (self->children, s_ztrie_node_compare); return self; }
void zhashx_comment (zhashx_t *self, const char *format, ...) { if (format) { if (!self->comments) { self->comments = zlistx_new (); if (!self->comments) return; zlistx_set_destructor (self->comments, (czmq_destructor *) zstr_free); } va_list argptr; va_start (argptr, format); char *string = zsys_vprintf (format, argptr); va_end (argptr); if (string) zlistx_add_end (self->comments, string); } else zlistx_destroy (&self->comments); }
zlistx_t * zlistx_dup (zlistx_t *self) { if (!self) return NULL; zlistx_t *copy = zlistx_new (); if (copy) { // Copy item handlers copy->destructor = self->destructor; copy->duplicator = self->duplicator; copy->comparator = self->comparator; // Copy nodes node_t *node; for (node = self->head->next; node != self->head; node = node->next) zlistx_add_end (copy, node->item); } return copy; }
static int twps_create_ticket_printer(twps_server_t *self, zmsg_t *msg) { char *type = zmsg_popstr(msg); char *id = zmsg_popstr(msg); zstr_free(&id); char *path = zmsg_popstr(msg); char *model = zmsg_popstr(msg); wchar_t *manufacture = (wchar_t *) zmsg_popstr(msg); wchar_t *product = (wchar_t *) zmsg_popstr(msg); zactor_t *printer = NULL; if (streq(type, "HID")) { printer = zactor_new(ticket_hid_printer, path); } #ifdef __WIN32__ else if (streq(type, "SERIAL")) { printer = zactor_new(ticket_serial_printer, path); } #endif if (printer != NULL) { if (self->verbose) { zstr_send(printer, "VERBOSE"); } if (self->diagnostic) zstr_sendx(printer, "SETDIAGNOSTIC", NULL); zstr_sendx(printer, "START", TICKET_STORE_REP_ENDPOINT, NULL); zsock_wait(printer); zstr_sendx(printer, "SETPRINTERSTORE", PRINTER_STORE_REP_ENDPOINT, PRINTER_STORE_PUB_ENDPOINT, NULL); zsock_wait(printer); zsys_info("twps server: started ticket printer %s manufacturer|product|model %ls|%ls|%s", type, manufacture, product, model); zlistx_add_end(self->ticket_printers, printer); } else { zsys_warning("twps server: printer not added %s, %s", type, path); zmsg_print(msg); } zstr_free(&type); zstr_free(&path); zstr_free(&model); zstr_free((char **) &manufacture); zstr_free((char **) &product); return 0; }
zlistx_t *zhashx_values(zhashx_t *self) { assert(self); zlistx_t *values = zlistx_new (); if (!values) return NULL; zlistx_set_destructor (values, self->destructor); zlistx_set_duplicator (values, self->duplicator); uint index; size_t limit = primes [self->prime_index]; for (index = 0; index < limit; index++) { item_t *item = self->items [index]; while (item) { if (zlistx_add_end (values, (void *) item->value) == NULL) { zlistx_destroy (&values); return NULL; } item = item->next; } } return values; }
static ztrie_node_t * s_ztrie_parse_path (ztrie_t *self, const char *path, int mode) { int state = 0; char *needle, *beginToken = NULL, *beginRegex = NULL; ztrie_node_t *parent = self->root; if (zlistx_size (self->params) > 0) zlistx_purge (self->params); int len = strlen (path); needle = (char *) path; char *needle_stop = needle + len; // Ignore trailing delimiter if (needle[len-1] == self->delimiter) needle_stop -= 1; while (needle < needle_stop + 1) { // It is valid not to have an delimiter at the end of the path if (*needle == self->delimiter || needle == needle_stop) { // Token starts with delimiter ignore everything that comes before if (state == 0) { beginToken = needle + 1; state++; if (mode == MODE_INSERT || mode == MODE_LOOKUP) // Increment so regexes are parsed which is only relevant // during INSERT or LOOKUP. Using different states gives a small // performance boost for matching. state++; } // Token ends with delimiter. else if (state < 3) { int matchType = zlistx_size (self->params) > 0? NODE_TYPE_PARAM: beginRegex? NODE_TYPE_REGEX: NODE_TYPE_STRING; char *matchToken = beginRegex? beginRegex: beginToken; int matchTokenLen = needle - matchToken - (beginRegex? 1: 0); // Illegal token if (matchTokenLen == 0) return NULL; ztrie_node_t *match = NULL; // Asterisk nodes are only allowed at the end of a route if (needle == needle_stop && *matchToken == '*') { if (zlistx_size (parent->children) == 0) { matchType = NODE_TYPE_ASTERISK; matchToken = needle - 1; matchTokenLen = 1; } // Asterisk must be a leaf in the tree else return NULL; } else { matchType = zlistx_size (self->params) > 0? NODE_TYPE_PARAM: beginRegex? NODE_TYPE_REGEX: NODE_TYPE_STRING; matchToken = beginRegex? beginRegex: beginToken; matchTokenLen = needle - matchToken - (beginRegex? 1: 0); } // In insert and lookup mode only do a string comparison if (mode == MODE_INSERT || mode == MODE_LOOKUP) match = s_ztrie_compare_token (parent, matchToken, matchTokenLen); else // Otherwise evaluate regexes if (mode == MODE_MATCH) match = s_ztrie_matches_token (parent, matchToken, matchTokenLen); // Mismatch behavior depends on mode if (!match) { // Append to common prefix if (mode == MODE_INSERT) { // It's not allowed to append on asterisk if (parent->token_type == NODE_TYPE_ASTERISK || (zlistx_size (parent->children) == 1 && ((ztrie_node_t *) (zlistx_first (parent->children)))->token_type == NODE_TYPE_ASTERISK)) return NULL; parent = s_ztrie_node_new (parent, matchToken, matchTokenLen, self->params, matchType); } else // No match for path found if (mode == MODE_MATCH || mode == MODE_LOOKUP) return NULL; } // If a match has been found it becomes the parent for next path token else { parent = match; // In case a asterisk match has been made skip the rest of the route if (parent->token_type == NODE_TYPE_ASTERISK) break; } // Cleanup for next token beginRegex = NULL; if (zlistx_size (self->params) > 0) zlistx_purge (self->params); // Token end equals token begin beginToken = needle + 1; } } else // regex starts with '{' if (state == 2 && *needle == '{') { beginRegex = needle + 1; state++; } else // in the middle of the regex. Found a named regex. if (state == 3 && (*needle == ':')) { zlistx_add_end (self->params, s_strndup (beginRegex, needle - beginRegex)); beginRegex = needle + 1; } else // regex ends with { if (state == 3 && *needle == '}') { state--; } needle++; } // In matching mode the discovered node must be an endpoint if (parent && mode == MODE_MATCH && !parent->endpoint) return NULL; return parent; }
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"); }
/// // Add an item to the tail of the list. Calls the item duplicator, if any, // on the item. Resets cursor to list head. Returns an item handle on // success, NULL if memory was exhausted. void * QZlistx::addEnd (void *item) { void * rv = zlistx_add_end (self, item); return rv; }
void zhttp_client_test (bool verbose) { #if defined(HAVE_LIBCURL) && defined(ZMQ_STREAM) printf (" * zhttp_client: "); zsock_t *server = zsock_new_stream (NULL); int port = zsock_bind (server, "tcp://127.0.0.1:*"); char url[255]; sprintf (url, "http://127.0.0.1:%d", port); // @selftest // Simple create/destroy test zhttp_client_t *self = zhttp_client_new (verbose); assert (self); // Send the get request bool event = false; zlistx_t *headers = zlistx_new (); zlistx_add_end (headers, "Host: zeromq.org"); zhttp_client_get (self, url, headers, -1, test_handler, &event); zlistx_destroy (&headers); // Receive request on the server zchunk_t *routing_id = recv_http_request (server); // Send the response char* response = "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello"; zsock_send (server, "cs", routing_id, response); zchunk_destroy (&routing_id); // Receive the response on the http client int rc = zhttp_client_wait (self, -1); assert (rc == 0); zhttp_client_execute (self); assert (event); // Send a POST request event = false; zchunk_t *body = zchunk_new ("World", 5); zhttp_client_post (self, url, NULL, body, -1, test_handler, &event); // Receive request on the server routing_id = recv_http_request (server); // Send the response zsock_send (server, "cs", routing_id, response); zchunk_destroy (&routing_id); // Receive the response on the http client rc = zhttp_client_wait (self, -1); assert (rc == 0); zhttp_client_execute (self); assert (event); // Timeout check event = false; zhttp_client_get (self, url, NULL, 1000, test_handler, &event); rc = zhttp_client_wait (self, 1200); assert (rc == 0); zhttp_client_execute (self); assert (event); // Sending another request, without being answer // Checking the client ability to stop while request are inprogres zhttp_client_get (self, url, NULL, -1, test_handler, NULL); zchunk_destroy (&body); zhttp_client_destroy (&self); zsock_destroy (&server); // @end printf ("OK\n"); #endif }
static void handler (zsock_t *pipe, void *args) { curl_global_init(CURL_GLOBAL_ALL); CURLM *multi = curl_multi_init (); CURLSH *share = curl_share_init (); curl_share_setopt (share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); curl_share_setopt (share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); curl_share_setopt (share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); long verbose = (*(bool *) args) ? 1L : 0L; long timeout = 30; CURLMcode code; SOCKET pipefd = zsock_fd (pipe); struct curl_waitfd waitfd = {pipefd, CURL_WAIT_POLLIN}; // List to hold pending curl handles, in case we are destroy the client // while request are inprogress zlistx_t *pending_handles = zlistx_new (); zlistx_set_destructor (pending_handles, (zlistx_destructor_fn *) curl_destructor); zsock_signal (pipe, 0); bool terminated = false; while (!terminated) { int events = zsock_events (pipe); if ((events & ZMQ_POLLIN) == 0) { code = curl_multi_wait (multi, &waitfd, 1, 1000, NULL); assert (code == CURLM_OK); } events = zsock_events (pipe); if (events & ZMQ_POLLIN) { char* command = zstr_recv (pipe); if (!command) break; // Interrupted // All actors must handle $TERM in this way if (streq (command, "$TERM")) terminated = true; else if (streq (command, "GET")) { char *url; zlistx_t *headers; void *userp; int rc = zsock_recv (pipe, "slp", &url, &headers, &userp); assert (rc == 0); zchunk_t *data = zchunk_new (NULL, 100); assert (data); struct curl_slist *curl_headers = zlistx_to_slist (headers); CURL *curl = curl_easy_init (); zlistx_add_end (pending_handles, curl); http_request *request = (http_request *) zmalloc (sizeof (http_request)); assert (request); request->userp = userp; request->curl = curl; request->data = data; request->headers = curl_headers; curl_easy_setopt (curl, CURLOPT_SHARE, share); curl_easy_setopt (curl, CURLOPT_TIMEOUT, timeout); curl_easy_setopt (curl, CURLOPT_VERBOSE, verbose); curl_easy_setopt (curl, CURLOPT_HTTPHEADER, curl_headers); curl_easy_setopt (curl, CURLOPT_URL, url); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt (curl, CURLOPT_WRITEDATA, data); curl_easy_setopt (curl, CURLOPT_PRIVATE, request); code = curl_multi_add_handle (multi, curl); assert (code == CURLM_OK); zlistx_destroy (&headers); zstr_free (&url); } else { puts ("E: invalid message to actor"); assert (false); } zstr_free (&command); } int still_running; code = curl_multi_perform (multi, &still_running); assert (code == CURLM_OK); int msgq = 0; struct CURLMsg *msg = curl_multi_info_read(multi, &msgq); while (msg) { if(msg->msg == CURLMSG_DONE) { CURL *curl = msg->easy_handle; http_request *request; curl_easy_getinfo(curl, CURLINFO_PRIVATE, &request); long response_code_long; curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &response_code_long); int response_code = (int)response_code_long; int rc = zsock_send (pipe, "icp", response_code, request->data, request->userp); assert (rc == 0); curl_multi_remove_handle (multi, curl); // Remove curl from the pending handles and delete it void *handle = zlistx_find (pending_handles, curl); assert (handle); rc = zlistx_delete (pending_handles, handle); assert (rc == 0); } msg = curl_multi_info_read(multi, &msgq); } } zlistx_destroy (&pending_handles); curl_share_cleanup (share); curl_multi_cleanup (multi); curl_global_cleanup (); }
void zlistx_test (bool verbose) { printf (" * zlistx: "); // @selftest zlistx_t *list = zlistx_new (); assert (list); assert (zlistx_size (list) == 0); // Test operations on an empty list assert (zlistx_first (list) == NULL); assert (zlistx_last (list) == NULL); assert (zlistx_next (list) == NULL); assert (zlistx_prev (list) == NULL); assert (zlistx_find (list, "hello") == NULL); assert (zlistx_delete (list, NULL) == -1); assert (zlistx_detach (list, NULL) == NULL); assert (zlistx_delete (list, NULL) == -1); assert (zlistx_detach (list, NULL) == NULL); zlistx_purge (list); zlistx_sort (list); // Use item handlers zlistx_set_destructor (list, (zlistx_destructor_fn *) zstr_free); zlistx_set_duplicator (list, (zlistx_duplicator_fn *) strdup); zlistx_set_comparator (list, (zlistx_comparator_fn *) strcmp); // Try simple insert/sort/delete/next assert (zlistx_next (list) == NULL); zlistx_add_end (list, "world"); assert (streq ((char *) zlistx_next (list), "world")); zlistx_add_end (list, "hello"); assert (streq ((char *) zlistx_prev (list), "hello")); zlistx_sort (list); assert (zlistx_size (list) == 2); void *handle = zlistx_find (list, "hello"); char *item1 = (char *) zlistx_item (list); char *item2 = (char *) zlistx_handle_item (handle); assert (item1 == item2); assert (streq (item1, "hello")); zlistx_delete (list, handle); assert (zlistx_size (list) == 1); char *string = (char *) zlistx_detach (list, NULL); assert (streq (string, "world")); free (string); assert (zlistx_size (list) == 0); // Check next/back work // Now populate the list with items zlistx_add_start (list, "five"); zlistx_add_end (list, "six"); zlistx_add_start (list, "four"); zlistx_add_end (list, "seven"); zlistx_add_start (list, "three"); zlistx_add_end (list, "eight"); zlistx_add_start (list, "two"); zlistx_add_end (list, "nine"); zlistx_add_start (list, "one"); zlistx_add_end (list, "ten"); // Test our navigation skills assert (zlistx_size (list) == 10); assert (streq ((char *) zlistx_last (list), "ten")); assert (streq ((char *) zlistx_prev (list), "nine")); assert (streq ((char *) zlistx_prev (list), "eight")); assert (streq ((char *) zlistx_prev (list), "seven")); assert (streq ((char *) zlistx_prev (list), "six")); assert (streq ((char *) zlistx_prev (list), "five")); assert (streq ((char *) zlistx_first (list), "one")); assert (streq ((char *) zlistx_next (list), "two")); assert (streq ((char *) zlistx_next (list), "three")); assert (streq ((char *) zlistx_next (list), "four")); // Sort by alphabetical order zlistx_sort (list); assert (streq ((char *) zlistx_first (list), "eight")); assert (streq ((char *) zlistx_last (list), "two")); // Moving items around handle = zlistx_find (list, "six"); zlistx_move_start (list, handle); assert (streq ((char *) zlistx_first (list), "six")); zlistx_move_end (list, handle); assert (streq ((char *) zlistx_last (list), "six")); zlistx_sort (list); assert (streq ((char *) zlistx_last (list), "two")); // Copying a list zlistx_t *copy = zlistx_dup (list); assert (copy); assert (zlistx_size (copy) == 10); assert (streq ((char *) zlistx_first (copy), "eight")); assert (streq ((char *) zlistx_last (copy), "two")); zlistx_destroy (©); // Delete items while iterating string = (char *) zlistx_first (list); assert (streq (string, "eight")); string = (char *) zlistx_next (list); assert (streq (string, "five")); zlistx_delete (list, zlistx_cursor (list)); string = (char *) zlistx_next (list); assert (streq (string, "four")); zlistx_purge (list); zlistx_destroy (&list); // @end printf ("OK\n"); }
void ziflist_reload (ziflist_t *self) { assert (self); zlistx_t *list = (zlistx_t *) self; zlistx_purge (list); #if defined (HAVE_GETIFADDRS) struct ifaddrs *interfaces; if (getifaddrs (&interfaces) == 0) { struct ifaddrs *interface = interfaces; while (interface) { // On Solaris, loopback interfaces have a NULL in ifa_broadaddr if (interface->ifa_broadaddr && interface->ifa_addr && interface->ifa_addr->sa_family == AF_INET && s_valid_flags (interface->ifa_flags)) { inaddr_t address = *(inaddr_t *) interface->ifa_addr; inaddr_t netmask = *(inaddr_t *) interface->ifa_netmask; inaddr_t broadcast = *(inaddr_t *) interface->ifa_broadaddr; // If the returned broadcast address is the same as source // address, build the broadcast address from the source // address and netmask. if (address.sin_addr.s_addr == broadcast.sin_addr.s_addr) broadcast.sin_addr.s_addr |= ~(netmask.sin_addr.s_addr); interface_t *item = s_interface_new (interface->ifa_name, address, netmask, broadcast); if (item) zlistx_add_end (list, item); } interface = interface->ifa_next; } } freeifaddrs (interfaces); # elif defined (__UNIX__) int sock = socket (AF_INET, SOCK_DGRAM, 0); if (sock != -1) { int num_interfaces = 0; struct ifconf ifconfig = { 0 }; // First ioctl call gets us length of buffer; second call gets us contents if (!ioctl (sock, SIOCGIFCONF, (caddr_t) &ifconfig, sizeof (struct ifconf))) { ifconfig.ifc_buf = (char *) zmalloc (ifconfig.ifc_len); if (!ioctl (sock, SIOCGIFCONF, (caddr_t) &ifconfig, sizeof (struct ifconf))) num_interfaces = ifconfig.ifc_len / sizeof (struct ifreq); } int index; for (index = 0; index < num_interfaces; index++) { struct ifreq *ifr = &ifconfig.ifc_req [index]; // Check interface flags bool is_valid = false; if (!ioctl (sock, SIOCGIFFLAGS, (caddr_t) ifr, sizeof (struct ifreq))) is_valid = s_valid_flags (ifr->ifr_flags); // Get interface properties inaddr_t address = { 0 }; if (!ioctl (sock, SIOCGIFADDR, (caddr_t) ifr, sizeof (struct ifreq))) address = *((inaddr_t *) &ifr->ifr_addr); else is_valid = false; inaddr_t broadcast = { 0 }; if (!ioctl (sock, SIOCGIFBRDADDR, (caddr_t) ifr, sizeof (struct ifreq))) broadcast = *((inaddr_t *) &ifr->ifr_addr); else is_valid = false; inaddr_t netmask = { 0 }; if (!ioctl (sock, SIOCGIFNETMASK, (caddr_t) ifr, sizeof (struct ifreq))) netmask = *((inaddr_t *) &ifr->ifr_addr); else is_valid = false; if (is_valid) { interface_t *item = s_interface_new (ifr->ifr_name, address, netmask, broadcast); if (item) zlistx_add_end (list, item); } } free (ifconfig.ifc_buf); close (sock); } # elif defined (__WINDOWS__) ULONG addr_size = 0; DWORD rc = GetAdaptersAddresses (AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, NULL, &addr_size); assert (rc == ERROR_BUFFER_OVERFLOW); PIP_ADAPTER_ADDRESSES pip_addresses = (PIP_ADAPTER_ADDRESSES) zmalloc (addr_size); rc = GetAdaptersAddresses (AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, pip_addresses, &addr_size); assert (rc == NO_ERROR); PIP_ADAPTER_ADDRESSES cur_address = pip_addresses; while (cur_address) { PIP_ADAPTER_UNICAST_ADDRESS pUnicast = cur_address->FirstUnicastAddress; PIP_ADAPTER_PREFIX pPrefix = cur_address->FirstPrefix; PWCHAR friendlyName = cur_address->FriendlyName; size_t asciiSize = wcstombs (0, friendlyName, 0) + 1; char *asciiFriendlyName = (char *) zmalloc (asciiSize); wcstombs (asciiFriendlyName, friendlyName, asciiSize); bool valid = (cur_address->OperStatus == IfOperStatusUp) && (pUnicast && pPrefix) && (pUnicast->Address.lpSockaddr->sa_family == AF_INET) && (pPrefix->PrefixLength <= 32); if (valid) { inaddr_t address = *(inaddr_t *) pUnicast->Address.lpSockaddr; inaddr_t netmask; netmask.sin_addr.s_addr = htonl ((0xffffffffU) << (32 - pPrefix->PrefixLength)); inaddr_t broadcast = address; broadcast.sin_addr.s_addr |= ~(netmask.sin_addr.s_addr); interface_t *item = s_interface_new (asciiFriendlyName, address, netmask, broadcast); if (item) zlistx_add_end (list, item); } free (asciiFriendlyName); cur_address = cur_address->Next; } free (pip_addresses); # else # error "Interface detection TBD on this operating system" # endif }