{"id":19973256,"url":"https://github.com/zeromq/zebra","last_synced_at":"2026-03-04T22:32:01.870Z","repository":{"id":66081779,"uuid":"45446927","full_name":"zeromq/zebra","owner":"zeromq","description":"REST/HTTP to XRAP gateway","archived":false,"fork":false,"pushed_at":"2016-08-25T09:06:53.000Z","size":1013,"stargazers_count":27,"open_issues_count":1,"forks_count":10,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-06-05T17:45:34.974Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zeromq.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2015-11-03T06:39:18.000Z","updated_at":"2024-04-11T01:50:47.000Z","dependencies_parsed_at":null,"dependency_job_id":"e9a37e4b-ad89-4ab7-85d2-87de96ac2fdb","html_url":"https://github.com/zeromq/zebra","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/zeromq/zebra","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeromq%2Fzebra","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeromq%2Fzebra/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeromq%2Fzebra/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeromq%2Fzebra/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zeromq","download_url":"https://codeload.github.com/zeromq/zebra/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zeromq%2Fzebra/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30096767,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T21:59:23.547Z","status":"ssl_error","status_checked_at":"2026-03-04T21:57:50.415Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-13T03:10:48.716Z","updated_at":"2026-03-04T22:32:01.859Z","avatar_url":"https://github.com/zeromq.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\u003cA name=\"toc1-3\" title=\"zebra - REST/HTTP to XRAP gateway.\" /\u003e\n# zebra - REST/HTTP to XRAP gateway.\n\n[![Build Status](https://travis-ci.org/zeromq/zebra.svg)](https://travis-ci.org/zeromq/zebra)\n\n\u003cA name=\"toc2-8\" title=\"Contents\" /\u003e\n## Contents\n\n\n**\u003ca href=\"#toc2-13\"\u003eOverview\u003c/a\u003e**\n\n**\u003ca href=\"#toc3-16\"\u003eScope and Goals\u003c/a\u003e**\n\n**\u003ca href=\"#toc2-49\"\u003eOwnership and License\u003c/a\u003e**\n\n**\u003ca href=\"#toc2-60\"\u003eUsing zebra\u003c/a\u003e**\n\n**\u003ca href=\"#toc3-63\"\u003eRequirements\u003c/a\u003e**\n\n**\u003ca href=\"#toc3-71\"\u003eBuilding and Installing\u003c/a\u003e**\n\n**\u003ca href=\"#toc3-108\"\u003eGetting started\u003c/a\u003e**\n\n**\u003ca href=\"#toc3-113\"\u003eUser information\u003c/a\u003e**\n*  \u003ca href=\"#toc4-116\"\u003eUser Agent Required\u003c/a\u003e\n*  \u003ca href=\"#toc4-121\"\u003eRate Limiting\u003c/a\u003e\n*  \u003ca href=\"#toc4-138\"\u003eConditional requests\u003c/a\u003e\n\n**\u003ca href=\"#toc3-143\"\u003eUsage\u003c/a\u003e**\n*  \u003ca href=\"#toc4-148\"\u003e - Handler for XRAP requests\u003c/a\u003e\n*  \u003ca href=\"#toc4-582\"\u003e - Simple HTTP web server\u003c/a\u003e\n*  \u003ca href=\"#toc4-831\"\u003e - zebra service broker\u003c/a\u003e\n*  \u003ca href=\"#toc4-1006\"\u003e - Broker client\u003c/a\u003e\n\n**\u003ca href=\"#toc3-1206\"\u003eHints to Contributors\u003c/a\u003e**\n\n**\u003ca href=\"#toc3-1215\"\u003eThis Document\u003c/a\u003e**\n\n\u003cA name=\"toc2-13\" title=\"Overview\" /\u003e\n## Overview\n\n\u003cA name=\"toc3-16\" title=\"Scope and Goals\" /\u003e\n### Scope and Goals\n\nzebra is designed to take HTTP requests for the common HTTP methods GET, POST, PUT and DELETE and convert them into the [XRAP](http://rfc.zeromq.org/spec:40) format. The converted messages will be passed to the request handlers which compose a response in the XRAP format which will be converted back into HTTP. To allow handler to come and go a they please, they need to register at a broker which will forward XRAP messages both ways.\n\n\u003ccenter\u003e\n\u003cimg src=\"https://github.com/zeromq/zebra/raw/master/images/README_1.png\" alt=\"1\"\u003e\n\u003c/center\u003e\n\n\u003cA name=\"toc2-49\" title=\"Ownership and License\" /\u003e\n## Ownership and License\n\nThe contributors are listed in AUTHORS. This project uses the MPL v2 license, see LICENSE.\n\nzebra uses the [C4.1 (Collective Code Construction Contract)](http://rfc.zeromq.org/spec:22) process for contributions.\n\nzebra uses the [CLASS (C Language Style for Scalabilty)](http://rfc.zeromq.org/spec:21) guide for code style.\n\nTo report an issue, use the [zebra issue tracker](https://github.com/zeromq/zebra/issues) at github.com.\n\n\u003cA name=\"toc2-60\" title=\"Using zebra\" /\u003e\n## Using zebra\n\n\u003cA name=\"toc3-63\" title=\"Requirements\" /\u003e\n### Requirements\n\n* libmicrohttpd (\u003e= 0.9.38)\n* libzmq (\u003e= 4.1)\n* czmq (\u003e= 3.0.3)\n* libcurl [optional, to run HTTP tests]\n\n\u003cA name=\"toc3-71\" title=\"Building and Installing\" /\u003e\n### Building and Installing\n\nHere's how to build zebra, including libmicrohttpd, libzmq and czmq:\n\n    wget http://ftpmirror.gnu.org/libmicrohttpd/libmicrohttpd-0.9.38.tar.gz\n    tar -xvf libmicrohttpd-0.9.38.tar.gz\n    cd libmicrohttpd-0.9.38\n    ./configure \u0026\u0026 make check\n    sudo make install\n    sudo ldconfig\n    cd ..\n\n    git clone git://github.com/zeromq/libzmq.git\n    cd libzmq\n    ./autogen.sh\n    ./configure \u0026\u0026 make check\n    sudo make install\n    sudo ldconfig\n    cd ..\n\n    git clone git://github.com/zeromq/czmq.git\n    cd czmq\n    ./autogen.sh\n    ./configure \u0026\u0026 make check\n    sudo make install\n    sudo ldconfig\n    cd ..\n\n    git clone git://github.com/zeromq/zebra.git\n    cd zebra\n    ./autogen.sh\n    ./configure \u0026\u0026 make check\n    sudo make install\n    sudo ldconfig\n    cd ..\n\n\u003cA name=\"toc3-108\" title=\"Getting started\" /\u003e\n### Getting started\n\nCheckout the zebra examples.\n\n\u003cA name=\"toc3-113\" title=\"User information\" /\u003e\n### User information\n\n\u003cA name=\"toc4-116\" title=\"User Agent Required\" /\u003e\n#### User Agent Required\n\nAll HTTP requests MUST include a valid User-Agent header. Requests with no User-Agent header will be rejected. A good User-Agent header value is the name of your application.\n\n\u003cA name=\"toc4-121\" title=\"Rate Limiting\" /\u003e\n#### Rate Limiting\n\nTo allow to compensate hardware limitations or to mitigate DOS attacks zebra has a built in rate limiting which allows to limit the number of request within an time interval.\n\nYou can check the returned HTTP headers of any HTTP request to see your current rate limit status:\n\n```\nHTTP/1.1 200 OK\nStatus: 200 OK\nX-RateLimit-Limit: 10\nX-RateLimit-Remaining: 5\nX-RateLimit-Reset: 22\n```\n\nOnce you go over the rate limit you will receive an 403 Forbidden error.\n\n\u003cA name=\"toc4-138\" title=\"Conditional requests\" /\u003e\n#### Conditional requests\n\nXRAP allows responses to return an ETag header as well as a Last-Modified header. You can use the values of these headers to make subsequent requests to those resources using the If-None-Match and If-Modified-Since headers, respectively. If the resource has not changed, the handler might return a 304 Not Modified. Also note: making a conditional request and receiving a 304 response does not count against the Rate Limit which zebra takes automatically care of.\n\n\u003cA name=\"toc3-143\" title=\"Usage\" /\u003e\n### Usage\n\nThis is the API provided by zebra v0.x, in alphabetical order.\n\n\u003cA name=\"toc4-148\" title=\" - Handler for XRAP requests\" /\u003e\n####  - Handler for XRAP requests\n\nzeb_handler - Handler for XRAP requests\n\nPlease add @discuss section in ../src/zeb_handler.c.\n\nThis is the class interface:\n\n    //  This API is a draft, and may change without notice.\n    #ifdef ZEBRA_BUILD_DRAFT_API\n    //  To work with zeb_handler, use the CZMQ zactor API:                      \n    //                                                                          \n    //  Create new zeb_handler instance, passing the broker endpoint:           \n    //                                                                          \n    //      zactor_t *handler = zactor_new (zeb_handler, \"inproc://broker\");    \n    //                                                                          \n    //  Destroy zeb_handler instance                                            \n    //                                                                          \n    //      zactor_destroy (\u0026handler);                                          \n    //                                                                          \n    //  Enable verbose logging of commands and activity:                        \n    //                                                                          \n    //      zstr_send (handler, \"VERBOSE\");                                     \n    //                                                                          \n    //  Receive API calls from zeb_handler:                                     \n    //                                                                          \n    //      char *command = zstr_recv (handler);                                \n    //                                                                          \n    //  Check if an etag is current, MUST signal 0 if true otherwise 1.         \n    //                                                                          \n    //    if (streq (command, \"CHECK ETAG\")) {                                  \n    //       char *etag = zstr_recv (handler);                                  \n    //       zsock_signal (handler, 0);                                         \n    //    }                                                                     \n    //                                                                          \n    //  Check if a last modified timestamp is current, MUST signal 0 if true    \n    //  otherwise 1.                                                            \n    //                                                                          \n    //    if (streq (command, \"CHECK LAST MODIFIED\")) {                         \n    //       uint64_t last_modified;                                            \n    //       zsock_brecv (handler, \"8\", \u0026last_modified);                        \n    //       zsock_signal (handler, 0);                                         \n    //    }                                                                     \n    //                                                                          \n    //  Handle incomming request from clients. MUST return a response.          \n    //                                                                          \n    //  if (streq (command, \"HANDLE REQUEST\")) {                                \n    //      zmsg_t *request = zmsg_recv (handle);                               \n    //      xrap_msg_t *xrequest = xrap_msg_decode (\u0026request);                  \n    //      zmsg_t *response = xrap_msg_encode (\u0026xrequest);                     \n    //      zmsg_send (\u0026response, handler);                                     \n    //  }                                                                       \n    //                                                                          \n    //  This is the handler actor which runs in its own thread and polls its two\n    //  sockets to process incoming messages.                                   \n    ZEBRA_EXPORT void\n        zeb_handler (zsock_t *pipe, void *args);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Add a new offer this handler will handle. Returns 0 if successful,       \n    //  otherwise -1.                                                            \n    //  The content type parameter is optional and is used to                    \n    //  filter requests upon their requested (GET) or provided (POST/PUT)        \n    //  content's type. The content type parameter may be a regex which is       \n    //  useful for GET offers that can supply resources in different formats.    \n    //  If the client did request multiple content types then the first match    \n    //  will be chosen and applied to the request. All other content types are   \n    //  drooped. If the request's content type does not match it is automatically\n    //  rejected with the error code 406 (Not acceptable).                       \n    ZEBRA_EXPORT int\n        zeb_handler_add_offer (zactor_t *self, int method, const char *uri, const char *content_type);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Self test of this class.\n    ZEBRA_EXPORT void\n        zeb_handler_test (bool verbose);\n    \n    #endif // ZEBRA_BUILD_DRAFT_API\n\nThis is the class self test code:\n\n    //  Simple create/destroy test\n    \n    //  Start a server to test against, and bind to endpoint\n    zactor_t *server = zactor_new (zeb_broker, \"zeb_client_test\");\n    if (verbose)\n        zstr_send (server, \"VERBOSE\");\n    zstr_sendx (server, \"LOAD\", \"src/zeb_client.cfg\", NULL);\n    \n    //  Create a client and connect it to the server\n    zeb_client_t *client = zeb_client_new ();\n    int rc = zeb_client_connect (client, \"tcp://127.0.0.1:9999\", 1000, \"client\");\n    assert (rc == 0);\n    assert (zeb_client_connected (client) == true);\n    \n    //  Create a handler\n    zactor_t *handler = zactor_new (zeb_handler, (void *) \"tcp://127.0.0.1:9999\");\n    assert (handler);\n    \n    //  Offer a service\n    rc = zeb_handler_add_offer (handler, XRAP_MSG_GET, \"/dummy\", \"application/(xml|json)\");\n    assert (rc == 0);\n    rc = zeb_handler_add_offer (handler, XRAP_MSG_PUT, \"/dummy\", \"application/(xml|json)\");\n    assert (rc == 0);\n    \n    //  Provide Rubbish Offering\n    rc = zeb_handler_add_offer (handler, XRAP_MSG_GET, \"/dummy\", NULL);\n    assert (rc == -1);\n    \n    //  ================================\n    //  GET Tests\n    \n    //  1.1 Send Request\n    xrap_msg_t *xmsg = xrap_msg_new (XRAP_MSG_GET);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/dummy\");\n    xrap_msg_set_content_type (xmsg, \"application/xml\");\n    zmsg_t *msg = xrap_msg_encode (\u0026xmsg);\n    rc = zeb_client_request (client, 0, \u0026msg);\n    assert (rc == 0);\n    \n    //  1.2 Receive request and Echo response\n    char *command;\n    zsock_recv (handler, \"sm\", \u0026command, \u0026msg);\n    assert (streq (command, \"HANDLE REQUEST\"));\n    zstr_free (\u0026command);\n    zmsg_send (\u0026msg, handler);\n    \n    //  1.3 Receive Response (is echo)\n    msg = zeb_client_recv (client);\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_GET);\n    assert (streq (xrap_msg_resource (xmsg), \"/dummy\"));\n    assert (streq (xrap_msg_content_type (xmsg), \"application/xml\"));\n    xrap_msg_destroy (\u0026xmsg);\n    zuuid_t *sender = zeb_client_sender (client);\n    zuuid_destroy (\u0026sender);\n    \n    //  2.1 Send Request with default Firefox accept header\n    xmsg = xrap_msg_new (XRAP_MSG_GET);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/dummy\");\n    xrap_msg_set_content_type (xmsg,\n            \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\");\n    msg = xrap_msg_encode (\u0026xmsg);\n    rc = zeb_client_request (client, 0, \u0026msg);\n    assert (rc == 0);\n    \n    //  2.2 Receive request and Echo response\n    zsock_recv (handler, \"sm\", \u0026command, \u0026msg);\n    assert (streq (command, \"HANDLE REQUEST\"));\n    zstr_free (\u0026command);\n    zmsg_send (\u0026msg, handler);\n    \n    //  2.3 Receive Response (is echo)\n    msg = zeb_client_recv (client);\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_GET);\n    assert (streq (xrap_msg_resource (xmsg), \"/dummy\"));\n    //  zeb_handler choose the first matching accept type\n    assert (streq (xrap_msg_content_type (xmsg), \"application/xml\"));\n    xrap_msg_destroy (\u0026xmsg);\n    sender = zeb_client_sender (client);\n    zuuid_destroy (\u0026sender);\n    \n    //  3.1 Send Request without ACCEPT\n    xmsg = xrap_msg_new (XRAP_MSG_GET);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/dummy\");\n    msg = xrap_msg_encode (\u0026xmsg);\n    rc = zeb_client_request (client, 0, \u0026msg);\n    assert (rc == 0);\n    \n    //  3.2 Receive Request without ACCEPT\n    msg = zeb_client_recv (client);\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_ERROR);\n    assert (xrap_msg_status_code (xmsg) == XRAP_TRAFFIC_NOT_ACCEPTABLE);\n    xrap_msg_destroy (\u0026xmsg);\n    sender = zeb_client_sender (client);\n    zuuid_destroy (\u0026sender);\n    \n    //  4.1 Send Request with conditionals (not changed)\n    xmsg = xrap_msg_new (XRAP_MSG_GET);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/dummy\");\n    xrap_msg_set_content_type (xmsg, \"application/json\");\n    xrap_msg_set_if_none_match (xmsg, \"MATCH\");\n    xrap_msg_set_if_modified_since (xmsg, 10);\n    msg = xrap_msg_encode (\u0026xmsg);\n    rc = zeb_client_request (client, 0, \u0026msg);\n    assert (rc == 0);\n    \n    //  4.2 Check etag (match)\n    char *etag;\n    zsock_recv (handler, \"ss\", \u0026command, \u0026etag);\n    assert (streq (command, \"CHECK ETAG\"));\n    zstr_free (\u0026command);\n    assert (streq (etag, \"MATCH\"));\n    zstr_free (\u0026etag);\n    zsock_signal (handler, 0);\n    \n    //  4.3 Check last modified (not modified)\n    uint64_t last_modified;\n    zsock_recv (handler, \"s8\", \u0026command, \u0026last_modified);\n    assert (streq (command, \"CHECK LAST MODIFIED\"));\n    zstr_free (\u0026command);\n    zsock_signal (handler, 0);\n    \n    //  4.4 Receive Request with conditionals\n    msg = zeb_client_recv (client);\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_GET_EMPTY);\n    xrap_msg_destroy (\u0026xmsg);\n    sender = zeb_client_sender (client);\n    zuuid_destroy (\u0026sender);\n    \n    //  5.1 Send Request with conditionals (changed)\n    xmsg = xrap_msg_new (XRAP_MSG_GET);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/dummy\");\n    xrap_msg_set_content_type (xmsg, \"application/json\");\n    xrap_msg_set_if_none_match (xmsg, \"NONE MATCH\");\n    xrap_msg_set_if_modified_since (xmsg, 20);\n    msg = xrap_msg_encode (\u0026xmsg);\n    rc = zeb_client_request (client, 0, \u0026msg);\n    assert (rc == 0);\n    \n    //  5.2 Check etag (none match)\n    zsock_recv (handler, \"ss\", \u0026command, \u0026etag);\n    assert (streq (command, \"CHECK ETAG\"));\n    zstr_free (\u0026command);\n    assert (streq (etag, \"NONE MATCH\"));\n    zstr_free (\u0026etag);\n    zsock_signal (handler, 1);\n    \n    //  5.3 Check last modified (modified)\n    zsock_recv (handler, \"s8\", \u0026command, \u0026last_modified);\n    assert (streq (command, \"CHECK LAST MODIFIED\"));\n    zstr_free (\u0026command);\n    assert (last_modified == 20);\n    zsock_signal (handler, 1);\n    \n    //  5.4 Receive request and Echo response\n    zsock_recv (handler, \"sm\", \u0026command, \u0026msg);\n    assert (streq (command, \"HANDLE REQUEST\"));\n    zstr_free (\u0026command);\n    zmsg_send (\u0026msg, handler);\n    \n    //  5.5 Receive response with conditionals\n    msg = zeb_client_recv (client);\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_GET);\n    assert (streq (xrap_msg_resource (xmsg), \"/dummy\"));\n    assert (streq (xrap_msg_content_type (xmsg), \"application/json\"));\n    xrap_msg_destroy (\u0026xmsg);\n    sender = zeb_client_sender (client);\n    zuuid_destroy (\u0026sender);\n    \n    //  ================================\n    //  PUT Tests\n    \n    //  1.1 Send Request\n    xmsg = xrap_msg_new (XRAP_MSG_PUT);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/dummy\");\n    xrap_msg_set_content_type (xmsg, \"application/xml\");\n    xrap_msg_set_content_body (xmsg, \"application/xml\");\n    msg = xrap_msg_encode (\u0026xmsg);\n    rc = zeb_client_request (client, 0, \u0026msg);\n    assert (rc == 0);\n    \n    //  1.2 Receive request and Echo response\n    zsock_recv (handler, \"sm\", \u0026command, \u0026msg);\n    assert (streq (command, \"HANDLE REQUEST\"));\n    zstr_free (\u0026command);\n    zmsg_send (\u0026msg, handler);\n    \n    //  1.3 Receive response (is echo)\n    msg = zeb_client_recv (client);\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_PUT);\n    assert (streq (xrap_msg_resource (xmsg), \"/dummy\"));\n    assert (streq (xrap_msg_content_type (xmsg), \"application/xml\"));\n    assert (streq (xrap_msg_content_body (xmsg), \"application/xml\"));\n    xrap_msg_destroy (\u0026xmsg);\n    sender = zeb_client_sender (client);\n    zuuid_destroy (\u0026sender);\n    \n    //  2.1 Send Request with conditionals (update, both)\n    xmsg = xrap_msg_new (XRAP_MSG_PUT);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/dummy\");\n    xrap_msg_set_content_type (xmsg, \"application/json\");\n    xrap_msg_set_if_match (xmsg, \"MATCH\");\n    xrap_msg_set_if_unmodified_since (xmsg, 10);\n    msg = xrap_msg_encode (\u0026xmsg);\n    rc = zeb_client_request (client, 0, \u0026msg);\n    assert (rc == 0);\n    \n    //  2.2 Check etag (match)\n    zsock_recv (handler, \"ss\", \u0026command, \u0026etag);\n    assert (streq (command, \"CHECK ETAG\"));\n    zstr_free (\u0026command);\n    assert (streq (etag, \"MATCH\"));\n    zstr_free (\u0026etag);\n    zsock_signal (handler, 0);\n    \n    //  2.3 Check last modified (not modified)\n    zsock_recv (handler, \"s8\", \u0026command, \u0026last_modified);\n    assert (streq (command, \"CHECK LAST MODIFIED\"));\n    zstr_free (\u0026command);\n    zsock_signal (handler, 0);\n    \n    //  2.4 Receive request and Echo response\n    zsock_recv (handler, \"sm\", \u0026command, \u0026msg);\n    assert (streq (command, \"HANDLE REQUEST\"));\n    zstr_free (\u0026command);\n    zmsg_send (\u0026msg, handler);\n    \n    //  2.5 Receive response with conditionals (update, both)\n    msg = zeb_client_recv (client);\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_PUT);\n    assert (streq (xrap_msg_resource (xmsg), \"/dummy\"));\n    assert (streq (xrap_msg_content_type (xmsg), \"application/json\"));\n    xrap_msg_destroy (\u0026xmsg);\n    sender = zeb_client_sender (client);\n    zuuid_destroy (\u0026sender);\n    \n    //  3.1 Send Request with conditionals (update, etag)\n    xmsg = xrap_msg_new (XRAP_MSG_PUT);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/dummy\");\n    xrap_msg_set_content_type (xmsg, \"application/json\");\n    xrap_msg_set_if_match (xmsg, \"MATCH\");\n    msg = xrap_msg_encode (\u0026xmsg);\n    rc = zeb_client_request (client, 0, \u0026msg);\n    assert (rc == 0);\n    \n    //  3.2 Check etag (match)\n    zsock_recv (handler, \"ss\", \u0026command, \u0026etag);\n    assert (streq (command, \"CHECK ETAG\"));\n    zstr_free (\u0026command);\n    assert (streq (etag, \"MATCH\"));\n    zstr_free (\u0026etag);\n    zsock_signal (handler, 0);\n    \n    //  3.3 Receive request and Echo response\n    zsock_recv (handler, \"sm\", \u0026command, \u0026msg);\n    assert (streq (command, \"HANDLE REQUEST\"));\n    zstr_free (\u0026command);\n    zmsg_send (\u0026msg, handler);\n    \n    //  3.4 Receive response with conditionals (update, etag)\n    msg = zeb_client_recv (client);\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_PUT);\n    xrap_msg_destroy (\u0026xmsg);\n    sender = zeb_client_sender (client);\n    zuuid_destroy (\u0026sender);\n    \n    //  4.1 Send Request with conditionals (update, last_modified)\n    xmsg = xrap_msg_new (XRAP_MSG_PUT);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/dummy\");\n    xrap_msg_set_content_type (xmsg, \"application/json\");\n    xrap_msg_set_if_unmodified_since (xmsg, 10);\n    msg = xrap_msg_encode (\u0026xmsg);\n    rc = zeb_client_request (client, 0, \u0026msg);\n    assert (rc == 0);\n    \n    //  4.2 Check last modified (not modified)\n    zsock_recv (handler, \"s8\", \u0026command, \u0026last_modified);\n    assert (streq (command, \"CHECK LAST MODIFIED\"));\n    zstr_free (\u0026command);\n    zsock_signal (handler, 0);\n    \n    //  4.3 Receive request and Echo response\n    zsock_recv (handler, \"sm\", \u0026command, \u0026msg);\n    assert (streq (command, \"HANDLE REQUEST\"));\n    zstr_free (\u0026command);\n    zmsg_send (\u0026msg, handler);\n    \n    //  4.4 Receive response with conditionals (update, last_modified)\n    msg = zeb_client_recv (client);\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_PUT);\n    xrap_msg_destroy (\u0026xmsg);\n    sender = zeb_client_sender (client);\n    zuuid_destroy (\u0026sender);\n    \n    //  5.1 Send Request with conditionals (no update)\n    xmsg = xrap_msg_new (XRAP_MSG_PUT);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/dummy\");\n    xrap_msg_set_content_type (xmsg, \"application/json\");\n    xrap_msg_set_if_match (xmsg, \"NONE MATCH\");\n    xrap_msg_set_if_unmodified_since (xmsg, 20);\n    msg = xrap_msg_encode (\u0026xmsg);\n    rc = zeb_client_request (client, 0, \u0026msg);\n    assert (rc == 0);\n    \n    //  5.2 Check etag (none match)\n    zsock_recv (handler, \"ss\", \u0026command, \u0026etag);\n    assert (streq (command, \"CHECK ETAG\"));\n    zstr_free (\u0026command);\n    assert (streq (etag, \"NONE MATCH\"));\n    zstr_free (\u0026etag);\n    zsock_signal (handler, 1);\n    \n    //  5.3 Check last modified (modified)\n    zsock_recv (handler, \"s8\", \u0026command, \u0026last_modified);\n    assert (streq (command, \"CHECK LAST MODIFIED\"));\n    zstr_free (\u0026command);\n    assert (last_modified == 20);\n    zsock_signal (handler, 1);\n    \n    //  5.4 Receive Request with conditionals (no update)\n    msg = zeb_client_recv (client);\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_ERROR);\n    assert (xrap_msg_status_code (xmsg) == XRAP_TRAFFIC_PRECONDITION_FAILED);\n    xrap_msg_destroy (\u0026xmsg);\n    sender = zeb_client_sender (client);\n    zuuid_destroy (\u0026sender);\n    \n    //  ================================\n    //  Invalid Tests\n    \n    //  1.1 Send Request\n    xmsg = xrap_msg_new (XRAP_MSG_GET_EMPTY);\n    msg = xrap_msg_encode (\u0026xmsg);\n    rc = zeb_client_request (client, 0, \u0026msg);\n    assert (rc == XRAP_TRAFFIC_NOT_FOUND);\n    \n    //  Cleanup\n    zeb_client_destroy (\u0026client);\n    zactor_destroy (\u0026handler);\n    \n    //  Done, shut down\n    zactor_destroy (\u0026server);\n\n\u003cA name=\"toc4-582\" title=\" - Simple HTTP web server\" /\u003e\n####  - Simple HTTP web server\n\nSimple HTTP webserver implementation using the libmicrohttpd library.\nIncomming HTTP request are converted to XRAP and send to the broker.\nResponses from the broker are converted back into HTTP.\n\nPlease add @discuss section in ../src/zeb_microhttpd.c.\n\nThis is the class interface:\n\n    //  Create new zeb_microhttpd actor instance. It will start a HTTP webserver\n    //  and convert incomming request from HTTP to XRAP.\n    //\n    //      zactor_t *microhttpd = zactor_new (zeb_microhttpd, NULL);\n    //\n    //  Destroy zeb_microhttpd instance.\n    //\n    //      zactor_destroy (\u0026microhttpd);\n    //\n    //  Enable verbose logging of commands and activity:\n    //\n    //      zstr_send (microhttpd, \"VERBOSE\");\n    //      zsock_wait (microhttpd);\n    //\n    //  Start zeb_microhttpd actor.\n    //\n    //      zstr_sendx (microhttpd, \"START\", NULL);\n    //      zsock_wait (microhttpd);\n    //\n    //  Stop zeb_microhttpd actor.\n    //\n    //      zstr_sendx (microhttpd, \"STOP\", NULL);\n    //      zsock_wait (microhttpd);\n    //\n    //  Attach the endpoint socket to endpoints, see zsock_attach ().\n    //\n    //      zstr_sendx (microhttpd, \"ENDPOINT\", endpoints, NULL);\n    //      zsock_wait (microhttpd);\n    //\n    //  Set the port on which the http server shall listen for requests.\n    //  The default port is 8888.\n    //  Note: The port is passed as string!\n    //  Note: If the server is already started, the server will be stopped\n    //        and restarted on the new port.\n    //\n    //      zstr_sendx (microhttpd, \"PORT\", \"8888\", NULL);\n    //      zsock_wait (microhttpd);\n    //\n    //  Set the ratelimit for HTTP request per user agent. The default limit is 10\n    //  and the default reset interval is 60000ms.\n    //  Note: Both limit and reset interval are passed as string!\n    //  Note: If the server is already started, the behavior is undefined!\n    //\n    //       zstr_sendx (microhttpd, \"RATELIMIT\", \"100\", \"3600000\", NULL);\n    //       zsock_wait (microhttpd);\n    //\n    //  All settings can be configured by using a configuration file in the czmq\n    //  cfg format.\n    //\n    //  ------------------------ zeb_microhttpd.cfg -------------------------------\n    //  | 1 | zeb_microhttpd\n    //  | 2 |     endpoint = tcp://192.168.178.1:7777  # Broker endpoint\n    //  | 3 |     port = 8888                          # Webserver port\n    //  | 4 |     verbose = 0\n    //  | 5 |     ratelimit\n    //  | 6 |        limit = 999\n    //  | 7 |        interval = 3600000\n    //  ---------------------------------------------------------------------------\n    //\n    //       zstr_sendx (microhttpd, \"LOAD\", filename, NULL);\n    //\n    //  This is the zeb_microhttpd constructor as a zactor_fn;\n    ZEBRA_EXPORT void\n        zeb_microhttpd_actor (zsock_t *pipe, void *args);\n    \n    //  Self test of this actor\n    ZEBRA_EXPORT void\n        zeb_microhttpd_test (bool verbose);\n\nThis is the class self test code:\n\n    //  Start and configure http server and broker\n    //  ------------------------------------------\n    zactor_t *zeb_microhttpd = zactor_new (zeb_microhttpd_actor, NULL);\n    \n    zstr_send (zeb_microhttpd, \"START\");\n    rc = zsock_wait (zeb_microhttpd);             //  Wait until actor started\n    assert (rc == 0);\n    \n    zstr_sendx (zeb_microhttpd, \"PORT\", \"8081\", NULL);\n    rc = zsock_wait (zeb_microhttpd);             //  Wait until port is configured\n    assert (rc == 0);\n    \n    zstr_sendx (zeb_microhttpd, \"RATELIMIT\", \"3\", \"10000\", NULL);\n    rc = zsock_wait (zeb_microhttpd);             //  Wait until port is configured\n    assert (rc == 0);\n    \n    zstr_sendx (zeb_microhttpd, \"ENDPOINT\", \"inproc://http_broker\", NULL);\n    rc = zsock_wait (zeb_microhttpd);             //  Wait until endpoint configured\n    assert (rc == 0);\n    \n    zactor_t *broker = zactor_new (zeb_broker, \"broker\");\n    \n    if (verbose)\n        zstr_send (broker, \"VERBOSE\");\n    zstr_sendx (broker, \"BIND\", \"inproc://http_broker\", NULL);\n    \n    //  Create a XRAP hander and connect it to broker\n    //  ---------------------------------------------\n    zeb_client_t *handler = zeb_client_new ();\n    assert (handler);\n    \n    //  Connect handler to server\n    rc = zeb_client_connect (handler, \"inproc://http_broker\",  1000, \"handler\");\n    assert (rc == 0);\n    assert (zeb_client_connected (handler) == true);\n    \n    //  Provide GET Offering\n    rc = zeb_client_set_handler (handler, \"GET\", \"/foo/{[^/]}\");\n    assert (rc == 0);\n    \n    //  Simple GET example\n    //  ------------------\n    \n    //  Send GET Request\n    zeb_curl_client_t *curl = zeb_curl_client_new ();\n    zeb_curl_client_send_get (curl, \"http://localhost:8081/foo/bar?page=10\");\n    \n    //  Receive Request\n    zmsg_t *request = zeb_client_recv (handler);\n    assert (request);\n    xrap_msg_t *xrap_msg = xrap_msg_decode (\u0026request);\n    assert (xrap_msg_id (xrap_msg) == XRAP_MSG_GET);\n    assert (streq (\"/foo/bar\", xrap_msg_resource (xrap_msg)));\n    xrap_msg_destroy (\u0026xrap_msg);\n    \n    //  Send Response\n    xrap_msg = xrap_msg_new (XRAP_MSG_GET_OK);\n    xrap_msg_set_status_code (xrap_msg, 200);\n    xrap_msg_set_content_type (xrap_msg, \"text/hello\");\n    xrap_msg_set_content_body (xrap_msg, \"Hello World!\");\n    zmsg_t *response = xrap_msg_encode (\u0026xrap_msg);\n    zeb_client_deliver (handler, zeb_client_sender (handler), \u0026response);\n    zuuid_t *sender = zeb_client_sender (handler);\n    zuuid_destroy (\u0026sender);\n    \n    //  Receive GET Response\n    zeb_curl_client_verify_response (curl, 200, \"Hello World!\");\n    zeb_curl_client_destroy (\u0026curl);\n    \n    //  Simple GET not found example\n    //  ----------------------------\n    \n    //  Send GET Request 2\n    curl = zeb_curl_client_new ();\n    zeb_curl_client_send_get (curl, \"http://localhost:8081/foo/bar/baz\");\n    \n    //  Receive GET Response 2\n    zeb_curl_client_verify_response (curl, 404, PAGE_NOT_FOUND);\n    zeb_curl_client_destroy (\u0026curl);\n    \n    //  Simple POST example\n    //  -------------------\n    \n    //  Provide POST Offering\n    rc = zeb_client_set_handler (handler, \"POST\", \"/foo/{[^/]}\");\n    assert (rc == 0);\n    \n    curl = zeb_curl_client_new ();\n    zeb_curl_client_send_post (curl, \"http://localhost:8081/foo/bar\", \"abc\");\n    \n    //  Receive Request\n    request = zeb_client_recv (handler);\n    assert (request);\n    xrap_msg = xrap_msg_decode (\u0026request);\n    assert (xrap_msg_id (xrap_msg) == XRAP_MSG_POST);\n    assert (streq (xrap_msg_content_type (xrap_msg), \"text/plain\"));\n    assert (streq (xrap_msg_content_body (xrap_msg), \"abc\"));\n    assert (streq (\"/foo/bar\", xrap_msg_parent (xrap_msg)));\n    xrap_msg_destroy (\u0026xrap_msg);\n    \n    //  Send Response\n    xrap_msg = xrap_msg_new (XRAP_MSG_POST_OK);\n    xrap_msg_set_status_code (xrap_msg, 201);\n    xrap_msg_set_location (xrap_msg, \"/foo/bar\");\n    xrap_msg_set_etag (xrap_msg, \"a3fsd3\");\n    xrap_msg_set_date_modified (xrap_msg, 0);\n    xrap_msg_set_content_type (xrap_msg, \"text/hello\");\n    xrap_msg_set_content_body (xrap_msg, \"Hello World!\");\n    response = xrap_msg_encode (\u0026xrap_msg);\n    zeb_client_deliver (handler, zeb_client_sender (handler), \u0026response);\n    sender = zeb_client_sender (handler);\n    zuuid_destroy (\u0026sender);\n    \n    //  Give response time to arrive\n    zclock_sleep (250);\n    \n    zeb_curl_client_verify_response (curl, 201, \"Hello World!\");\n    zeb_curl_client_destroy (\u0026curl);\n    \n    //  Simple PUT example\n    //  ------------------\n    \n    //  Provide PUT Offering\n    rc = zeb_client_set_handler (handler, \"PUT\", \"/foo/{[^/]}\");\n    assert (rc == 0);\n    \n    curl = zeb_curl_client_new ();\n    zeb_curl_client_send_put (curl, \"http://localhost:8081/foo/bar\", \"abc\");\n    \n    //  Receive Request\n    request = zeb_client_recv (handler);\n    assert (request);\n    xrap_msg = xrap_msg_decode (\u0026request);\n    assert (xrap_msg_id (xrap_msg) == XRAP_MSG_PUT);\n    assert (streq (xrap_msg_content_type (xrap_msg), \"text/plain\"));\n    assert (streq (xrap_msg_content_body (xrap_msg), \"abc\"));\n    assert (streq (\"/foo/bar\", xrap_msg_resource (xrap_msg)));\n    xrap_msg_destroy (\u0026xrap_msg);\n    \n    //  Send Response\n    xrap_msg = xrap_msg_new (XRAP_MSG_POST_OK);\n    xrap_msg_set_status_code (xrap_msg, 200);\n    xrap_msg_set_location (xrap_msg, \"/foo/bar\");\n    xrap_msg_set_etag (xrap_msg, \"a3fsd3\");\n    xrap_msg_set_date_modified (xrap_msg, 0);\n    response = xrap_msg_encode (\u0026xrap_msg);\n    zeb_client_deliver (handler, zeb_client_sender (handler), \u0026response);\n    sender = zeb_client_sender (handler);\n    zuuid_destroy (\u0026sender);\n    \n    //  Give response time to arrive\n    zclock_sleep (250);\n    \n    zeb_curl_client_verify_response (curl, 200, NULL);\n    zeb_curl_client_destroy (\u0026curl);\n    \n    //  Cleanup\n    //  -------\n    zeb_client_destroy (\u0026handler);\n    zactor_destroy (\u0026broker);\n    \n    zstr_send (zeb_microhttpd, \"STOP\");\n    rc = zsock_wait (zeb_microhttpd);             //  Wait until actor stopped\n    assert (rc == 0);\n    \n    zactor_destroy (\u0026zeb_microhttpd);\n\n\u003cA name=\"toc4-831\" title=\" - zebra service broker\" /\u003e\n####  - zebra service broker\n\nThe zeb_broker implements the zproto server. This broker connects\nclient requests to handler offers.\n\nPlease add @discuss section in ../src/zeb_broker.c.\n\nThis is the class interface:\n\n    //  To work with zeb_broker, use the CZMQ zactor API:\n    //\n    //  Create new zeb_broker instance, passing logging prefix:\n    //\n    //      zactor_t *zeb_broker = zactor_new (zeb_broker, \"myname\");\n    //\n    //  Destroy zeb_broker instance\n    //\n    //      zactor_destroy (\u0026zeb_broker);\n    //\n    //  Enable verbose logging of commands and activity:\n    //\n    //      zstr_send (zeb_broker, \"VERBOSE\");\n    //\n    //  Bind zeb_broker to specified endpoint. TCP endpoints may specify\n    //  the port number as \"*\" to aquire an ephemeral port:\n    //\n    //      zstr_sendx (zeb_broker, \"BIND\", endpoint, NULL);\n    //\n    //  Return assigned port number, specifically when BIND was done using an\n    //  an ephemeral port:\n    //\n    //      zstr_sendx (zeb_broker, \"PORT\", NULL);\n    //      char *command, *port_str;\n    //      zstr_recvx (zeb_broker, \u0026command, \u0026port_str, NULL);\n    //      assert (streq (command, \"PORT\"));\n    //\n    //  Specify configuration file to load, overwriting any previous loaded\n    //  configuration file or options:\n    //\n    //      zstr_sendx (zeb_broker, \"LOAD\", filename, NULL);\n    //\n    //  Set configuration path value:\n    //\n    //      zstr_sendx (zeb_broker, \"SET\", path, value, NULL);\n    //\n    //  Save configuration data to config file on disk:\n    //\n    //      zstr_sendx (zeb_broker, \"SAVE\", filename, NULL);\n    //\n    //  Send zmsg_t instance to zeb_broker:\n    //\n    //      zactor_send (zeb_broker, \u0026msg);\n    //\n    //  Receive zmsg_t instance from zeb_broker:\n    //\n    //      zmsg_t *msg = zactor_recv (zeb_broker);\n    //\n    //  This is the zeb_broker constructor as a zactor_fn:\n    //\n    ZEBRA_EXPORT void\n        zeb_broker (zsock_t *pipe, void *args);\n    \n    //  Self test of this class\n    ZEBRA_EXPORT void\n        zeb_broker_test (bool verbose);\n\nThis is the class self test code:\n\n    zactor_t *server = zactor_new (zeb_broker, \"broker\");\n    if (verbose)\n        zstr_send (server, \"VERBOSE\");\n    \n    zstr_sendx (server, \"BIND\", \"tcp://127.0.0.1:9999\", NULL);\n    \n    zsock_t* client = zsock_new_dealer (\"\u003etcp://127.0.0.1:9999\");\n    zsock_t* worker = zsock_new_dealer (\"\u003etcp://127.0.0.1:9999\");\n    assert (client);\n    assert (worker);\n    \n    xrap_traffic_t *traffic = xrap_traffic_new ();\n    \n    //  Invalid Command\n    xrap_traffic_set_id (traffic, XRAP_TRAFFIC_XRAP_OFFER);\n    xrap_traffic_send (traffic, client);\n    xrap_traffic_recv (traffic, client);\n    assert (xrap_traffic_id (traffic) == XRAP_TRAFFIC_ERROR);\n    assert (xrap_traffic_status_code (traffic) == XRAP_TRAFFIC_COMMAND_INVALID);\n    \n    //  Open Connections for client \u0026 worker\n    xrap_traffic_set_id (traffic, XRAP_TRAFFIC_CONNECTION_OPEN);\n    xrap_traffic_send (traffic, client);\n    xrap_traffic_recv (traffic, client);\n    assert (xrap_traffic_id (traffic) == XRAP_TRAFFIC_OK);\n    \n    xrap_traffic_set_id (traffic, XRAP_TRAFFIC_CONNECTION_OPEN);\n    xrap_traffic_send (traffic, worker);\n    xrap_traffic_recv (traffic, worker);\n    assert (xrap_traffic_id (traffic) == XRAP_TRAFFIC_OK);\n    \n    //  Provide Rubish Offering\n    xrap_traffic_set_id (traffic, XRAP_TRAFFIC_XRAP_OFFER);\n    xrap_traffic_set_route (traffic, \"///\");\n    xrap_traffic_set_method (traffic, \"GET\");\n    xrap_traffic_send (traffic, worker);\n    xrap_traffic_recv (traffic, worker);\n    assert (xrap_traffic_id (traffic) == XRAP_TRAFFIC_FAIL);\n    assert (xrap_traffic_status_code (traffic) == XRAP_TRAFFIC_CONFLICT);\n    \n    //  Provide Offering\n    xrap_traffic_set_id (traffic, XRAP_TRAFFIC_XRAP_OFFER);\n    xrap_traffic_set_route (traffic, \"/foo/{[^/]}\");\n    xrap_traffic_set_method (traffic, \"GET\");\n    xrap_traffic_send (traffic, worker);\n    xrap_traffic_recv (traffic, worker);\n    assert (xrap_traffic_id (traffic) == XRAP_TRAFFIC_OK);\n    \n    //  Send Request\n    xrap_msg_t *xmsg = xrap_msg_new (XRAP_MSG_GET);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/foo/bar\");\n    zmsg_t *msg = xrap_msg_encode (\u0026xmsg);\n    xrap_traffic_set_id (traffic, XRAP_TRAFFIC_XRAP_SEND);\n    xrap_traffic_set_content (traffic, \u0026msg);\n    xrap_traffic_send (traffic, client);\n    xrap_traffic_recv (traffic, client);\n    assert (xrap_traffic_id (traffic) == XRAP_TRAFFIC_OK);\n    \n    //  Receive Request\n    xrap_traffic_recv (traffic, worker);\n    assert (xrap_traffic_id (traffic) == XRAP_TRAFFIC_XRAP_DELIVER);\n    msg = zmsg_dup (xrap_traffic_content (traffic));\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_GET);\n    assert (streq (\"/foo/bar\", xrap_msg_resource (xmsg)));\n    xrap_msg_destroy (\u0026xmsg);\n    \n    //  Send Response\n    xmsg = xrap_msg_new (XRAP_MSG_GET_OK);\n    xrap_msg_set_status_code (xmsg, 200);\n    xrap_msg_set_content_type (xmsg, \"text/hello\");\n    xrap_msg_set_content_body (xmsg, \"Hello World!\");\n    msg = xrap_msg_encode (\u0026xmsg);\n    xrap_traffic_set_id (traffic, XRAP_TRAFFIC_XRAP_DELIVER);\n    xrap_traffic_set_content (traffic, \u0026msg);\n    xrap_traffic_send (traffic, worker);\n    \n    //  Receive Response\n    xrap_traffic_recv (traffic, client);\n    assert (xrap_traffic_id (traffic) == XRAP_TRAFFIC_XRAP_DELIVER);\n    msg = zmsg_dup (xrap_traffic_content (traffic));\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_GET_OK);\n    assert (xrap_msg_status_code (xmsg) == 200);\n    assert (streq (\"text/hello\", xrap_msg_content_type (xmsg)));\n    assert (streq (\"Hello World!\", xrap_msg_content_body (xmsg)));\n    xrap_msg_destroy (\u0026xmsg);\n    \n    //  Send Request 2\n    xmsg = xrap_msg_new (XRAP_MSG_GET);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/fou/baz\");\n    msg = xrap_msg_encode (\u0026xmsg);\n    xrap_traffic_set_id (traffic, XRAP_TRAFFIC_XRAP_SEND);\n    xrap_traffic_set_content (traffic, \u0026msg);\n    xrap_traffic_send (traffic, client);\n    xrap_traffic_recv (traffic, client);\n    assert (xrap_traffic_id (traffic) == XRAP_TRAFFIC_FAIL);\n    assert (xrap_traffic_status_code (traffic) == XRAP_TRAFFIC_NOT_FOUND);\n    \n    xrap_traffic_destroy (\u0026traffic);\n    \n    //  Finished, we can clean up\n    zsock_destroy (\u0026client);\n    zsock_destroy (\u0026worker);\n    zactor_destroy (\u0026server);\n\n\u003cA name=\"toc4-1006\" title=\" - Broker client\" /\u003e\n####  - Broker client\n\nClient implementation to communicate with the broker. This\nimplementation is used by both clients (i.e. zeb_microhttpd) and the\nhandlers.\n\nPlease add @discuss section in ../src/zeb_client.c.\n\nThis is the class interface:\n\n    //  This API is a draft, and may change without notice.\n    #ifdef ZEBRA_BUILD_DRAFT_API\n    //  *** Draft method, for development use, may change without warning ***\n    //  Create a new zeb_client, return the reference if successful,   \n    //  or NULL if construction failed due to lack of available memory.\n    ZEBRA_EXPORT zeb_client_t *\n        zeb_client_new (void);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Destroy the zeb_client and free all memory used by the object.\n    ZEBRA_EXPORT void\n        zeb_client_destroy (zeb_client_t **self_p);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Return actor, when caller wants to work with multiple actors and/or\n    //  input sockets asynchronously.                                      \n    ZEBRA_EXPORT zactor_t *\n        zeb_client_actor (zeb_client_t *self);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Return message pipe for asynchronous message I/O. In the high-volume case,\n    //  we send methods and get replies to the actor, in a synchronous manner, and\n    //  we send/recv high volume message data to a second pipe, the msgpipe. In   \n    //  the low-volume case we can do everything over the actor pipe, if traffic  \n    //  is never ambiguous.                                                       \n    ZEBRA_EXPORT zsock_t *\n        zeb_client_msgpipe (zeb_client_t *self);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Return true if client is currently connected, else false. Note that the   \n    //  client will automatically re-connect if the server dies and restarts after\n    //  a successful first connection.                                            \n    ZEBRA_EXPORT bool\n        zeb_client_connected (zeb_client_t *self);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Connect to server endpoint, with specified timeout in msecs (zero means wait    \n    //  forever). Constructor succeeds if connection is successful. The caller may      \n    //  specify its address.                                                            \n    //  Returns \u003e= 0 if successful, -1 if interrupted.                                  \n    ZEBRA_EXPORT int\n        zeb_client_connect (zeb_client_t *self, const char *endpoint, uint32_t timeout, const char *address);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Offer to handle particular XRAP requests, where the route matches request's     \n    //  resource.                                                                       \n    //  Returns \u003e= 0 if successful, -1 if interrupted.                                  \n    ZEBRA_EXPORT int\n        zeb_client_set_handler (zeb_client_t *self, const char *method, const char *route);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  No explanation                                                                  \n    //  Returns \u003e= 0 if successful, -1 if interrupted.                                  \n    ZEBRA_EXPORT int\n        zeb_client_request (zeb_client_t *self, uint32_t timeout, zmsg_t **content);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Send XRAP DELIVER message to server, takes ownership of message\n    //  and destroys message when done sending it.                     \n    ZEBRA_EXPORT int\n        zeb_client_deliver (zeb_client_t *self, zuuid_t *sender, zmsg_t **content);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Receive message from server; caller destroys message when done\n    ZEBRA_EXPORT zmsg_t *\n        zeb_client_recv (zeb_client_t *self);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Return last received command. Can be one of these values:\n    //      \"XRAP DELIVER\"                                       \n    ZEBRA_EXPORT const char *\n        zeb_client_command (zeb_client_t *self);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Return last received status\n    ZEBRA_EXPORT int\n        zeb_client_status (zeb_client_t *self);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Return last received reason\n    ZEBRA_EXPORT const char *\n        zeb_client_reason (zeb_client_t *self);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Return last received sender\n    ZEBRA_EXPORT zuuid_t *\n        zeb_client_sender (zeb_client_t *self);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Return last received content\n    ZEBRA_EXPORT zmsg_t *\n        zeb_client_content (zeb_client_t *self);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Enable verbose tracing (animation) of state machine activity.\n    ZEBRA_EXPORT void\n        zeb_client_set_verbose (zeb_client_t *self, bool verbose);\n    \n    //  *** Draft method, for development use, may change without warning ***\n    //  Self test of this class.\n    ZEBRA_EXPORT void\n        zeb_client_test (bool verbose);\n    \n    #endif // ZEBRA_BUILD_DRAFT_API\n\nThis is the class self test code:\n\n    //  Start a server to test against, and bind to endpoint\n    zactor_t *server = zactor_new (zeb_broker, \"zeb_client_test\");\n    if (verbose)\n        zstr_send (server, \"VERBOSE\");\n    zstr_sendx (server, \"LOAD\", \"src/zeb_client.cfg\", NULL);\n    \n    //  Create clients for testing\n    zeb_client_t *client = zeb_client_new ();\n    zeb_client_t *handler = zeb_client_new ();\n    assert (client);\n    assert (handler);\n    \n    //  Enable verbose logging\n    zeb_client_set_verbose (client, verbose);\n    \n    //  Connect clients to server\n    int rc = zeb_client_connect (client, \"tcp://127.0.0.1:9999\", 1000, \"client\");\n    assert (rc == 0);\n    assert (zeb_client_connected (client) == true);\n    \n    rc = zeb_client_connect (handler, \"tcp://127.0.0.1:9999\", 1000, \"handler\");\n    assert (rc == 0);\n    assert (zeb_client_connected (handler) == true);\n    \n    //  Provide Rubbish Offering\n    rc = zeb_client_set_handler (handler, \"GET\", \"///\");\n    assert (rc != 0);\n    assert (zeb_client_status (handler) == XRAP_TRAFFIC_CONFLICT);\n    \n    //  Provide Offering\n    rc = zeb_client_set_handler (handler, \"GET\", \"/foo/{[^/]}\");\n    assert (rc == 0);\n    \n    //  Send Request\n    xrap_msg_t *xmsg = xrap_msg_new (XRAP_MSG_GET);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/foo/bar\");\n    zmsg_t *msg = xrap_msg_encode (\u0026xmsg);\n    rc = zeb_client_request (client, 0, \u0026msg);\n    assert (rc == 0);\n    \n    //  Receive Request\n    msg = zeb_client_recv (handler);\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_GET);\n    assert (streq (\"/foo/bar\", xrap_msg_resource (xmsg)));\n    xrap_msg_destroy (\u0026xmsg);\n    \n    //  Send Response\n    xmsg = xrap_msg_new (XRAP_MSG_GET_OK);\n    xrap_msg_set_status_code (xmsg, 200);\n    xrap_msg_set_content_type (xmsg, \"text/hello\");\n    xrap_msg_set_content_body (xmsg, \"Hello World!\");\n    msg = xrap_msg_encode (\u0026xmsg);\n    zeb_client_deliver (handler, handler-\u003esender, \u0026msg);\n    zuuid_t *sender = zeb_client_sender (handler);\n    zuuid_destroy (\u0026sender);\n    \n    //  Receive Response\n    msg = zeb_client_recv (client);\n    xmsg = xrap_msg_decode (\u0026msg);\n    assert (xrap_msg_id (xmsg) == XRAP_MSG_GET_OK);\n    assert (xrap_msg_status_code (xmsg) == 200);\n    assert (streq (\"text/hello\", xrap_msg_content_type (xmsg)));\n    assert (streq (\"Hello World!\", xrap_msg_content_body (xmsg)));\n    xrap_msg_destroy (\u0026xmsg);\n    sender = zeb_client_sender (client);\n    zuuid_destroy (\u0026sender);\n    \n    //  Send Request 2\n    xmsg = xrap_msg_new (XRAP_MSG_GET);\n    xrap_msg_set_resource (xmsg, \"%s\", \"/fou/baz\");\n    msg = xrap_msg_encode (\u0026xmsg);\n    rc = zeb_client_request (client, 0, \u0026msg);\n    assert (rc == XRAP_TRAFFIC_NOT_FOUND);\n    \n    zeb_client_destroy (\u0026client);\n    zeb_client_destroy (\u0026handler);\n    \n    //  Done, shut down\n    zactor_destroy (\u0026server);\n\n\n\u003cA name=\"toc3-1206\" title=\"Hints to Contributors\" /\u003e\n### Hints to Contributors\n\nRead the CLASS style guide please, and write your code to make it indistinguishable from the rest of the code in the library. That is the only real criteria for good style: it's invisible.\n\nDo read your code after you write it and ask, \"Can I make this simpler?\" We do use a nice minimalist and yet readable style. Learn it, adopt it, use it.\n\nBefore opening a pull request read our [contribution guidelines](https://github.com/zeromq/zebra/blob/master/CONTRIBUTING.md). Thanks!\n\n\u003cA name=\"toc3-1215\" title=\"This Document\" /\u003e\n### This Document\n\n_This documentation was generated from zebra/README.txt using [Gitdown](https://github.com/zeromq/gitdown)_\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzeromq%2Fzebra","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzeromq%2Fzebra","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzeromq%2Fzebra/lists"}