zmq_proxy_hook(3)
=================

NAME
----
zmq_proxy_hook - start built-in 0MQ proxy with an hook to modify the messages 
between the frontend and the backend


SYNOPSIS
--------
*int zmq_proxy_hook (const void '*frontend', const void '*backend', 
     const void '*capture', const void '*hook', const void '*control');*


DESCRIPTION
-----------
The _zmq_proxy_hook()_ function starts the built-in 0MQ proxy in the 
current application thread, as _zmq_proxy()_ or _zmq_proxy_steerable()_ do. 
Please, refer to these functions for the general description and usage. 
We describe here only the additional hook provided by the structure "hook" 
passed as a fith argument.

If the hook structure pointer is not NULL, the proxy supports a hook defined as 
a structure 'zmq_proxy_hook_t' containing a data pointer to any data type and 
the address of two functions of type 'zmq_hook_f'. The first function, 
'front2back_hook' is to manipulate the message received from the frontend, before 
it is sent to the backend. The second one, 'back2front_hook' is for the way back.

Both functions receive as an argument in addition to a pointer to the message, the 
pointer to the data passed in the 'zmq_proxy_hook_t' structure. This data makes it
possible to manage statefull behaviours in the proxy. They receive also the frame 
number n_ which is 1 for the first frame, n for the nth one, 0 for the last one. This 
enable to manage specifically the identity frame when ROUTER | STREAM sockets are 
concerned. Moreover, to give the hook full capabilities, the three sockets passed 
as parameters to the proxy are also provided to the hook functions, enabling to 
consume some frames or to add others:

----
typedef int (*zmq_hook_f)(void *frontend, void *backend, void *capture, 
zmq_msg_t* msg_, size_t n_, void *data_);
typedef struct zmq_proxy_hook_t {
    void *data;
    zmq_hook_f front2back_hook;
    zmq_hook_f back2front_hook;
} zmq_proxy_hook_t;
----

If the hook pointer is NULL, zmq_proxy_hook behaves exactly as if zmq_proxy
or zmq_proxy_steerable had been called.

Refer to linkzmq:zmq_socket[3] for a description of the available socket types.
Refer to linkzmq:zmq_proxy[3] for a description of the zmq_proxy.
Refer to linkzmq:zmq_proxy_steerable[3] for a description of the zmq_proxy_steerable.

EXAMPLE USAGE
-------------

Filter
------

The most simple use is to simply filter the messages for example against vulgarity. 
Messages are simply scanned against a dictionnary and target words are replaced.

ROUTER | STREAM / ROUTER | STREAM proxy
---------------------------------------

The data field enables to multiplex as desired identities in a ROUTER/ROUTER or in a 
STREAM/STREAM proxy or what ever. Such architecture enables also custom load balancers.

Sticky ROUTER / ROUTER proxy
----------------------------

The data field enables to manage sticky identity pairing in a ROUTER/ROUTER proxy.

Security mechanism proxying
---------------------------

We expect to be able to proxy CURVE with the use of this feature.

Tests
-----

In an existing application, just change zmq_proxy or zmq_proxy_steerable for 
zmq_proxy_hook to test anythink, even "Man in the middle" attacks ws security
mechanisms with a STREAM/STREAM proxy.



RETURN VALUE
------------
The _zmq_proxy_hook()_ function returns the same values than zmq_proxy
or zmq_proxy_steerable in the same conditions of use.


EXAMPLE
-------
This simple example aims at uppercasing the traffic between the frontend and the 
backend, and lowercasing it on the way back.

.Setup the hook
----
struct stats_t {
    int qt_upper_case;
    int qt_lower_case;
} stats = {NULL, 0, 0};

int
upper_case(void*, void*, void*, zmq_msg_t* msg_, size_t n_, void *stats_)
{
    size_t size = zmq_msg_size(msg_);
    if (!size || n_ == 1) return 0; // skip identity and 0 frames
    char* message = (char*) zmq_msg_data(msg_);
    for (size_t i = 0; i < size; i++)
        if ('a' <= message[i] && message[i] <= 'z')
            message[i] += 'A' - 'a';
    struct stats_t* stats = (struct stats_t*) stats_;
    stats->qt_upper_case++;
    return 0;
}

int
lower_case(void*, void*, void*, zmq_msg_t* msg_, size_t n_, void *stats_)
{
    size_t size = zmq_msg_size(msg_);
    if (!size || n_ == 1) return 0; // skip identity and 0 frames
    char* message = (char*) zmq_msg_data(msg_);
    for (size_t i = 0; i < size; i++)
        if ('A' <= message[i] && message[i] <= 'Z')
            message[i] += 'a' - 'A';
    struct stats_t* stats = (struct stats_t*) stats_;
    stats->qt_lower_case++;
    return 0;
}

zmq_proxy_hook_t hook = {
        &stats, // data used by the hook functions, passed as void* data_
        upper_case, // hook for messages going from frontend to backend
        lower_case // hook for messages going from backend to frontend
};
----
.in main:
----
int
main (void)
{
	setup_test_environment ();
	void *context = zmq_ctx_new ();
	assert (context);
	//  Create frontend, backend and control sockets
	void *frontend = zmq_socket (context, ZMQ_ROUTER);
	assert (backend);
	void *backend = zmq_socket (context, ZMQ_DEALER);
	assert (frontend);
	void *control = zmq_socket (context, ZMQ_PUB);
	assert (control);
	
	//  Bind sockets to TCP ports
	assert (zmq_bind (frontend, "tcp://*:5555") == 0);
	assert (zmq_bind (backend, "tcp://*:5556") == 0);
	assert (zmq_connect (control, "tcp://*:5557") == 0);
	
	//  Start the queue proxy, which runs until ETERM or "TERMINATE" 
	//  received on the control socket
	zmq_proxy_hook (frontend, backend, NULL, &hook, control);
	
	printf("frontend to backend hook hits = %d\nbackend to frontend hook hits = %d\n", stats.qt_upper_case, stats.qt_lower_case);

	// close sockets and context
	rc = zmq_close (control);
	assert (rc == 0);
	rc = zmq_close (backend);
	assert (rc == 0);
	rc = zmq_close (frontend);
	assert (rc == 0);
	rc = zmq_ctx_term (ctx);
	assert (rc == 0);
	return 0;
}
----
.somewhere, the proxy is stopped with:
----
rc = zmq_send (control, "TERMINATE", 9, 0); // stops the hooked proxy
assert (rc == 9);
----
.cf test_proxy.cpp for a full implementation of this test, with clients and workers.

SEE ALSO
--------
linkzmq:zmq_proxy[3]
linkzmq:zmq_proxy_steerable[3]
linkzmq:zmq_bind[3]
linkzmq:zmq_connect[3]
linkzmq:zmq_socket[3]
linkzmq:zmq[7]


AUTHORS
-------
This page was written by the 0MQ community. To make a change please
read the 0MQ Contribution Policy at <http://www.zeromq.org/docs:contributing>.
