
zmq.Proxy Maven / Gradle / Ivy
package zmq;
import java.nio.channels.Selector;
import java.util.Arrays;
import zmq.poll.PollItem;
class Proxy
{
public static boolean proxy(SocketBase frontend, SocketBase backend, SocketBase capture)
{
return new Proxy().start(frontend, backend, capture, null);
}
public static boolean proxy(SocketBase frontend, SocketBase backend, SocketBase capture, SocketBase control)
{
return new Proxy().start(frontend, backend, capture, control);
}
public enum State
{
ACTIVE,
PAUSED,
TERMINATED
}
private State state;
private Proxy()
{
state = State.ACTIVE;
}
private boolean start(SocketBase frontend, SocketBase backend, SocketBase capture, SocketBase control)
{
// The algorithm below assumes ratio of requests and replies processed
// under full load to be 1:1.
// TODO: The current implementation drops messages when
// any of the pipes becomes full.
int rc;
int more;
Msg msg;
int count = control == null ? 2 : 3;
PollItem[] items = new PollItem[count];
items[0] = new PollItem(frontend, ZMQ.ZMQ_POLLIN);
items[1] = new PollItem(backend, ZMQ.ZMQ_POLLIN);
if (control != null) {
items[2] = new PollItem(control, ZMQ.ZMQ_POLLIN);
}
PollItem[] itemsout = new PollItem[2];
itemsout[0] = new PollItem(frontend, ZMQ.ZMQ_POLLOUT);
itemsout[1] = new PollItem(backend, ZMQ.ZMQ_POLLOUT);
Selector selector = frontend.getCtx().createSelector();
try {
while (state != State.TERMINATED) {
// Wait while there are either requests or replies to process.
rc = ZMQ.poll(selector, items, -1);
if (rc < 0) {
return false;
}
// Get the pollout separately because when combining this with pollin it makes the CPU
// because pollout shall most of the time return directly.
// POLLOUT is only checked when frontend and backend sockets are not the same.
if (frontend != backend) {
rc = ZMQ.poll(selector, itemsout, 0L);
if (rc < 0) {
return false;
}
}
// Process a control command if any
if (control != null && items[2].isReadable()) {
msg = control.recv(0);
if (msg == null) {
return false;
}
more = control.getSocketOpt(ZMQ.ZMQ_RCVMORE);
if (more < 0) {
return false;
}
// Copy message to capture socket if any
boolean success = capture(capture, msg, more);
if (!success) {
return false;
}
byte[] command = msg.data();
if (Arrays.equals(command, ZMQ.PROXY_PAUSE)) {
state = State.PAUSED;
}
else if (Arrays.equals(command, ZMQ.PROXY_RESUME)) {
state = State.ACTIVE;
}
else if (Arrays.equals(command, ZMQ.PROXY_TERMINATE)) {
state = State.TERMINATED;
}
else {
// This is an API error, we should assert
System.out
.printf("E: invalid command sent to proxy '%s'%n", new String(command, ZMQ.CHARSET));
assert false;
}
}
// Process a request.
if (process(items[0], itemsout[1], frontend, backend)) {
if (!forward(frontend, backend, capture)) {
return false;
}
}
// Process a reply.
if (process(items[1], itemsout[0], frontend, backend)) {
if (!forward(backend, frontend, capture)) {
return false;
}
}
}
}
finally {
frontend.getCtx().closeSelector(selector);
}
return true;
}
private boolean process(PollItem read, PollItem write, SocketBase frontend, SocketBase backend)
{
return state == State.ACTIVE && read.isReadable() && (frontend == backend || write.isWritable());
}
private boolean forward(SocketBase from, SocketBase to, SocketBase capture)
{
int more;
boolean success;
while (true) {
Msg msg = from.recv(0);
if (msg == null) {
return false;
}
more = from.getSocketOpt(ZMQ.ZMQ_RCVMORE);
if (more < 0) {
return false;
}
// Copy message to capture socket if any
success = capture(capture, msg, more);
if (!success) {
return false;
}
success = to.send(msg, more > 0 ? ZMQ.ZMQ_SNDMORE : 0);
if (!success) {
return false;
}
if (more == 0) {
break;
}
}
return true;
}
private boolean capture(SocketBase capture, Msg msg, int more)
{
if (capture != null) {
Msg ctrl = new Msg(msg);
return capture.send(ctrl, more > 0 ? ZMQ.ZMQ_SNDMORE : 0);
}
return true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy