libzmq/src/stream_engine.cpp
2015-04-21 22:26:32 -07:00

962 lines
28 KiB
C++

/*
Copyright (c) 2007-2015 Contributors as noted in the AUTHORS file
This file is part of 0MQ.
0MQ is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
0MQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "platform.hpp"
#if defined ZMQ_HAVE_WINDOWS
#include "windows.hpp"
#else
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#if defined ZMQ_HAVE_OPENBSD
#define ucred sockpeercred
#endif
#endif
#include <string.h>
#include <new>
#include <sstream>
#include "stream_engine.hpp"
#include "io_thread.hpp"
#include "session_base.hpp"
#include "v1_encoder.hpp"
#include "v1_decoder.hpp"
#include "v2_encoder.hpp"
#include "v2_decoder.hpp"
#include "null_mechanism.hpp"
#include "plain_client.hpp"
#include "plain_server.hpp"
#include "gssapi_client.hpp"
#include "gssapi_server.hpp"
#include "curve_client.hpp"
#include "curve_server.hpp"
#include "raw_decoder.hpp"
#include "raw_encoder.hpp"
#include "config.hpp"
#include "err.hpp"
#include "ip.hpp"
#include "tcp.hpp"
#include "likely.hpp"
#include "wire.hpp"
zmq::stream_engine_t::stream_engine_t (fd_t fd_, const options_t &options_,
const std::string &endpoint_) :
s (fd_),
inpos (NULL),
insize (0),
decoder (NULL),
outpos (NULL),
outsize (0),
encoder (NULL),
metadata (NULL),
handshaking (true),
greeting_size (v2_greeting_size),
greeting_bytes_read (0),
session (NULL),
options (options_),
endpoint (endpoint_),
plugged (false),
next_msg (&stream_engine_t::identity_msg),
process_msg (&stream_engine_t::process_identity_msg),
io_error (false),
subscription_required (false),
mechanism (NULL),
input_stopped (false),
output_stopped (false),
has_handshake_timer (false),
socket (NULL)
{
int rc = tx_msg.init ();
errno_assert (rc == 0);
// Put the socket into non-blocking mode.
unblock_socket (s);
int family = get_peer_ip_address (s, peer_address);
if (family == 0)
peer_address.clear();
#if defined ZMQ_HAVE_SO_PEERCRED
else
if (family == PF_UNIX) {
struct ucred cred;
socklen_t size = sizeof (cred);
if (!getsockopt (s, SOL_SOCKET, SO_PEERCRED, &cred, &size)) {
std::ostringstream buf;
buf << ":" << cred.uid << ":" << cred.gid << ":" << cred.pid;
peer_address += buf.str ();
}
}
#elif defined ZMQ_HAVE_LOCAL_PEERCRED
else
if (family == PF_UNIX) {
struct xucred cred;
socklen_t size = sizeof (cred);
if (!getsockopt (s, 0, LOCAL_PEERCRED, &cred, &size)
&& cred.cr_version == XUCRED_VERSION) {
std::ostringstream buf;
buf << ":" << cred.cr_uid << ":";
if (cred.cr_ngroups > 0)
buf << cred.cr_groups[0];
buf << ":";
peer_address += buf.str ();
}
}
#endif
#ifdef SO_NOSIGPIPE
// Make sure that SIGPIPE signal is not generated when writing to a
// connection that was already closed by the peer.
int set = 1;
rc = setsockopt (s, SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof (int));
errno_assert (rc == 0);
#endif
}
zmq::stream_engine_t::~stream_engine_t ()
{
zmq_assert (!plugged);
if (s != retired_fd) {
#ifdef ZMQ_HAVE_WINDOWS
int rc = closesocket (s);
wsa_assert (rc != SOCKET_ERROR);
#else
int rc = close (s);
errno_assert (rc == 0);
#endif
s = retired_fd;
}
int rc = tx_msg.close ();
errno_assert (rc == 0);
// Drop reference to metadata and destroy it if we are
// the only user.
if (metadata != NULL)
if (metadata->drop_ref ())
delete metadata;
delete encoder;
delete decoder;
delete mechanism;
}
void zmq::stream_engine_t::plug (io_thread_t *io_thread_,
session_base_t *session_)
{
zmq_assert (!plugged);
plugged = true;
// Connect to session object.
zmq_assert (!session);
zmq_assert (session_);
session = session_;
socket = session-> get_socket ();
// Connect to I/O threads poller object.
io_object_t::plug (io_thread_);
handle = add_fd (s);
io_error = false;
if (options.raw_socket) {
// no handshaking for raw sock, instantiate raw encoder and decoders
encoder = new (std::nothrow) raw_encoder_t (out_batch_size);
alloc_assert (encoder);
decoder = new (std::nothrow) raw_decoder_t (in_batch_size);
alloc_assert (decoder);
// disable handshaking for raw socket
handshaking = false;
next_msg = &stream_engine_t::pull_msg_from_session;
process_msg = &stream_engine_t::push_raw_msg_to_session;
properties_t properties;
if (init_properties(properties)) {
// Compile metadata.
zmq_assert (metadata == NULL);
metadata = new (std::nothrow) metadata_t (properties);
}
if (options.raw_notify) {
// For raw sockets, send an initial 0-length message to the
// application so that it knows a peer has connected.
msg_t connector;
connector.init();
push_raw_msg_to_session (&connector);
connector.close();
session->flush ();
}
}
else {
// start optional timer, to prevent handshake hanging on no input
set_handshake_timer ();
// Send the 'length' and 'flags' fields of the identity message.
// The 'length' field is encoded in the long format.
outpos = greeting_send;
outpos [outsize++] = 0xff;
put_uint64 (&outpos [outsize], options.identity_size + 1);
outsize += 8;
outpos [outsize++] = 0x7f;
}
set_pollin (handle);
set_pollout (handle);
// Flush all the data that may have been already received downstream.
in_event ();
}
void zmq::stream_engine_t::unplug ()
{
zmq_assert (plugged);
plugged = false;
// Cancel all timers.
if (has_handshake_timer) {
cancel_timer (handshake_timer_id);
has_handshake_timer = false;
}
// Cancel all fd subscriptions.
if (!io_error)
rm_fd (handle);
// Disconnect from I/O threads poller object.
io_object_t::unplug ();
session = NULL;
}
void zmq::stream_engine_t::terminate ()
{
unplug ();
delete this;
}
void zmq::stream_engine_t::in_event ()
{
zmq_assert (!io_error);
// If still handshaking, receive and process the greeting message.
if (unlikely (handshaking))
if (!handshake ())
return;
zmq_assert (decoder);
// If there has been an I/O error, stop polling.
if (input_stopped) {
rm_fd (handle);
io_error = true;
return;
}
// If there's no data to process in the buffer...
if (!insize) {
// Retrieve the buffer and read as much data as possible.
// Note that buffer can be arbitrarily large. However, we assume
// the underlying TCP layer has fixed buffer size and thus the
// number of bytes read will be always limited.
size_t bufsize = 0;
decoder->get_buffer (&inpos, &bufsize);
const int rc = tcp_read (s, inpos, bufsize);
if (rc == 0) {
error (connection_error);
return;
}
if (rc == -1) {
if (errno != EAGAIN)
error (connection_error);
return;
}
// Adjust input size
insize = static_cast <size_t> (rc);
}
int rc = 0;
size_t processed = 0;
while (insize > 0) {
rc = decoder->decode (inpos, insize, processed);
zmq_assert (processed <= insize);
inpos += processed;
insize -= processed;
if (rc == 0 || rc == -1)
break;
rc = (this->*process_msg) (decoder->msg ());
if (rc == -1)
break;
}
// Tear down the connection if we have failed to decode input data
// or the session has rejected the message.
if (rc == -1) {
if (errno != EAGAIN) {
error (protocol_error);
return;
}
input_stopped = true;
reset_pollin (handle);
}
session->flush ();
}
void zmq::stream_engine_t::out_event ()
{
zmq_assert (!io_error);
// If write buffer is empty, try to read new data from the encoder.
if (!outsize) {
// Even when we stop polling as soon as there is no
// data to send, the poller may invoke out_event one
// more time due to 'speculative write' optimisation.
if (unlikely (encoder == NULL)) {
zmq_assert (handshaking);
return;
}
outpos = NULL;
outsize = encoder->encode (&outpos, 0);
while (outsize < out_batch_size) {
if ((this->*next_msg) (&tx_msg) == -1)
break;
encoder->load_msg (&tx_msg);
unsigned char *bufptr = outpos + outsize;
size_t n = encoder->encode (&bufptr, out_batch_size - outsize);
zmq_assert (n > 0);
if (outpos == NULL)
outpos = bufptr;
outsize += n;
}
// If there is no data to send, stop polling for output.
if (outsize == 0) {
output_stopped = true;
reset_pollout (handle);
return;
}
}
// If there are any data to write in write buffer, write as much as
// possible to the socket. Note that amount of data to write can be
// arbitrarily large. However, we assume that underlying TCP layer has
// limited transmission buffer and thus the actual number of bytes
// written should be reasonably modest.
const int nbytes = tcp_write (s, outpos, outsize);
// IO error has occurred. We stop waiting for output events.
// The engine is not terminated until we detect input error;
// this is necessary to prevent losing incoming messages.
if (nbytes == -1) {
reset_pollout (handle);
return;
}
outpos += nbytes;
outsize -= nbytes;
// If we are still handshaking and there are no data
// to send, stop polling for output.
if (unlikely (handshaking))
if (outsize == 0)
reset_pollout (handle);
}
void zmq::stream_engine_t::restart_output ()
{
if (unlikely (io_error))
return;
if (likely (output_stopped)) {
set_pollout (handle);
output_stopped = false;
}
// Speculative write: The assumption is that at the moment new message
// was sent by the user the socket is probably available for writing.
// Thus we try to write the data to socket avoiding polling for POLLOUT.
// Consequently, the latency should be better in request/reply scenarios.
out_event ();
}
void zmq::stream_engine_t::restart_input ()
{
zmq_assert (input_stopped);
zmq_assert (session != NULL);
zmq_assert (decoder != NULL);
int rc = (this->*process_msg) (decoder->msg ());
if (rc == -1) {
if (errno == EAGAIN)
session->flush ();
else
error (protocol_error);
return;
}
while (insize > 0) {
size_t processed = 0;
rc = decoder->decode (inpos, insize, processed);
zmq_assert (processed <= insize);
inpos += processed;
insize -= processed;
if (rc == 0 || rc == -1)
break;
rc = (this->*process_msg) (decoder->msg ());
if (rc == -1)
break;
}
if (rc == -1 && errno == EAGAIN)
session->flush ();
else
if (io_error)
error (connection_error);
else
if (rc == -1)
error (protocol_error);
else {
input_stopped = false;
set_pollin (handle);
session->flush ();
// Speculative read.
in_event ();
}
}
bool zmq::stream_engine_t::handshake ()
{
zmq_assert (handshaking);
zmq_assert (greeting_bytes_read < greeting_size);
// Receive the greeting.
while (greeting_bytes_read < greeting_size) {
const int n = tcp_read (s, greeting_recv + greeting_bytes_read,
greeting_size - greeting_bytes_read);
if (n == 0) {
error (connection_error);
return false;
}
if (n == -1) {
if (errno != EAGAIN)
error (connection_error);
return false;
}
greeting_bytes_read += n;
// We have received at least one byte from the peer.
// If the first byte is not 0xff, we know that the
// peer is using unversioned protocol.
if (greeting_recv [0] != 0xff)
break;
if (greeting_bytes_read < signature_size)
continue;
// Inspect the right-most bit of the 10th byte (which coincides
// with the 'flags' field if a regular message was sent).
// Zero indicates this is a header of identity message
// (i.e. the peer is using the unversioned protocol).
if (!(greeting_recv [9] & 0x01))
break;
// The peer is using versioned protocol.
// Send the major version number.
if (outpos + outsize == greeting_send + signature_size) {
if (outsize == 0)
set_pollout (handle);
outpos [outsize++] = 3; // Major version number
}
if (greeting_bytes_read > signature_size) {
if (outpos + outsize == greeting_send + signature_size + 1) {
if (outsize == 0)
set_pollout (handle);
// Use ZMTP/2.0 to talk to older peers.
if (greeting_recv [10] == ZMTP_1_0
|| greeting_recv [10] == ZMTP_2_0)
outpos [outsize++] = options.type;
else {
outpos [outsize++] = 0; // Minor version number
memset (outpos + outsize, 0, 20);
zmq_assert (options.mechanism == ZMQ_NULL
|| options.mechanism == ZMQ_PLAIN
|| options.mechanism == ZMQ_CURVE
|| options.mechanism == ZMQ_GSSAPI);
if (options.mechanism == ZMQ_NULL)
memcpy (outpos + outsize, "NULL", 4);
else
if (options.mechanism == ZMQ_PLAIN)
memcpy (outpos + outsize, "PLAIN", 5);
else
if (options.mechanism == ZMQ_GSSAPI)
memcpy (outpos + outsize, "GSSAPI", 6);
else
if (options.mechanism == ZMQ_CURVE)
memcpy (outpos + outsize, "CURVE", 5);
outsize += 20;
memset (outpos + outsize, 0, 32);
outsize += 32;
greeting_size = v3_greeting_size;
}
}
}
}
// Position of the revision field in the greeting.
const size_t revision_pos = 10;
// Is the peer using ZMTP/1.0 with no revision number?
// If so, we send and receive rest of identity message
if (greeting_recv [0] != 0xff || !(greeting_recv [9] & 0x01)) {
if (session->zap_enabled ()) {
// reject ZMTP 1.0 connections if ZAP is enabled
error (protocol_error);
return false;
}
encoder = new (std::nothrow) v1_encoder_t (out_batch_size);
alloc_assert (encoder);
decoder = new (std::nothrow) v1_decoder_t (in_batch_size, options.maxmsgsize);
alloc_assert (decoder);
// We have already sent the message header.
// Since there is no way to tell the encoder to
// skip the message header, we simply throw that
// header data away.
const size_t header_size = options.identity_size + 1 >= 255 ? 10 : 2;
unsigned char tmp [10], *bufferp = tmp;
// Prepare the identity message and load it into encoder.
// Then consume bytes we have already sent to the peer.
const int rc = tx_msg.init_size (options.identity_size);
zmq_assert (rc == 0);
memcpy (tx_msg.data (), options.identity, options.identity_size);
encoder->load_msg (&tx_msg);
size_t buffer_size = encoder->encode (&bufferp, header_size);
zmq_assert (buffer_size == header_size);
// Make sure the decoder sees the data we have already received.
inpos = greeting_recv;
insize = greeting_bytes_read;
// To allow for interoperability with peers that do not forward
// their subscriptions, we inject a phantom subscription message
// message into the incoming message stream.
if (options.type == ZMQ_PUB || options.type == ZMQ_XPUB)
subscription_required = true;
// We are sending our identity now and the next message
// will come from the socket.
next_msg = &stream_engine_t::pull_msg_from_session;
// We are expecting identity message.
process_msg = &stream_engine_t::process_identity_msg;
}
else
if (greeting_recv [revision_pos] == ZMTP_1_0) {
if (session->zap_enabled ()) {
// reject ZMTP 1.0 connections if ZAP is enabled
error (protocol_error);
return false;
}
encoder = new (std::nothrow) v1_encoder_t (
out_batch_size);
alloc_assert (encoder);
decoder = new (std::nothrow) v1_decoder_t (
in_batch_size, options.maxmsgsize);
alloc_assert (decoder);
}
else
if (greeting_recv [revision_pos] == ZMTP_2_0) {
if (session->zap_enabled ()) {
// reject ZMTP 2.0 connections if ZAP is enabled
error (protocol_error);
return false;
}
encoder = new (std::nothrow) v2_encoder_t (out_batch_size);
alloc_assert (encoder);
decoder = new (std::nothrow) v2_decoder_t (
in_batch_size, options.maxmsgsize);
alloc_assert (decoder);
}
else {
encoder = new (std::nothrow) v2_encoder_t (out_batch_size);
alloc_assert (encoder);
decoder = new (std::nothrow) v2_decoder_t (
in_batch_size, options.maxmsgsize);
alloc_assert (decoder);
if (options.mechanism == ZMQ_NULL
&& memcmp (greeting_recv + 12, "NULL\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) == 0) {
mechanism = new (std::nothrow)
null_mechanism_t (session, peer_address, options);
alloc_assert (mechanism);
}
else
if (options.mechanism == ZMQ_PLAIN
&& memcmp (greeting_recv + 12, "PLAIN\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) == 0) {
if (options.as_server)
mechanism = new (std::nothrow)
plain_server_t (session, peer_address, options);
else
mechanism = new (std::nothrow)
plain_client_t (options);
alloc_assert (mechanism);
}
#ifdef HAVE_LIBSODIUM
else
if (options.mechanism == ZMQ_CURVE
&& memcmp (greeting_recv + 12, "CURVE\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) == 0) {
if (options.as_server)
mechanism = new (std::nothrow)
curve_server_t (session, peer_address, options);
else
mechanism = new (std::nothrow) curve_client_t (options);
alloc_assert (mechanism);
}
#endif
#ifdef HAVE_LIBGSSAPI_KRB5
else
if (options.mechanism == ZMQ_GSSAPI
&& memcmp (greeting_recv + 12, "GSSAPI\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) == 0) {
if (options.as_server)
mechanism = new (std::nothrow)
gssapi_server_t (session, peer_address, options);
else
mechanism = new (std::nothrow) gssapi_client_t (options);
alloc_assert (mechanism);
}
#endif
else {
// Temporary support for security debugging
char mechanism [21];
memcpy (mechanism, greeting_recv + 12, 20);
mechanism [20] = 0;
printf ("LIBZMQ I: security failure, self=%s peer=%s\n",
options.mechanism == ZMQ_NULL? "NULL":
options.mechanism == ZMQ_PLAIN? "PLAIN":
options.mechanism == ZMQ_CURVE? "CURVE":
options.mechanism == ZMQ_GSSAPI? "GSSAPI":
"OTHER",
mechanism);
error (protocol_error);
return false;
}
next_msg = &stream_engine_t::next_handshake_command;
process_msg = &stream_engine_t::process_handshake_command;
}
// Start polling for output if necessary.
if (outsize == 0)
set_pollout (handle);
// Handshaking was successful.
// Switch into the normal message flow.
handshaking = false;
if (has_handshake_timer) {
cancel_timer (handshake_timer_id);
has_handshake_timer = false;
}
return true;
}
int zmq::stream_engine_t::identity_msg (msg_t *msg_)
{
int rc = msg_->init_size (options.identity_size);
errno_assert (rc == 0);
if (options.identity_size > 0)
memcpy (msg_->data (), options.identity, options.identity_size);
next_msg = &stream_engine_t::pull_msg_from_session;
return 0;
}
int zmq::stream_engine_t::process_identity_msg (msg_t *msg_)
{
if (options.recv_identity) {
msg_->set_flags (msg_t::identity);
int rc = session->push_msg (msg_);
errno_assert (rc == 0);
}
else {
int rc = msg_->close ();
errno_assert (rc == 0);
rc = msg_->init ();
errno_assert (rc == 0);
}
if (subscription_required)
process_msg = &stream_engine_t::write_subscription_msg;
else
process_msg = &stream_engine_t::push_msg_to_session;
return 0;
}
int zmq::stream_engine_t::next_handshake_command (msg_t *msg_)
{
zmq_assert (mechanism != NULL);
if (mechanism->status () == mechanism_t::ready) {
mechanism_ready ();
return pull_and_encode (msg_);
}
else
if (mechanism->status () == mechanism_t::error) {
errno = EPROTO;
return -1;
}
else {
const int rc = mechanism->next_handshake_command (msg_);
if (rc == 0)
msg_->set_flags (msg_t::command);
return rc;
}
}
int zmq::stream_engine_t::process_handshake_command (msg_t *msg_)
{
zmq_assert (mechanism != NULL);
const int rc = mechanism->process_handshake_command (msg_);
if (rc == 0) {
if (mechanism->status () == mechanism_t::ready)
mechanism_ready ();
else
if (mechanism->status () == mechanism_t::error) {
errno = EPROTO;
return -1;
}
if (output_stopped)
restart_output ();
}
return rc;
}
void zmq::stream_engine_t::zap_msg_available ()
{
zmq_assert (mechanism != NULL);
const int rc = mechanism->zap_msg_available ();
if (rc == -1) {
error (protocol_error);
return;
}
if (input_stopped)
restart_input ();
if (output_stopped)
restart_output ();
}
void zmq::stream_engine_t::mechanism_ready ()
{
if (options.recv_identity) {
msg_t identity;
mechanism->peer_identity (&identity);
const int rc = session->push_msg (&identity);
if (rc == -1 && errno == EAGAIN) {
// If the write is failing at this stage with
// an EAGAIN the pipe must be being shut down,
// so we can just bail out of the identity set.
return;
}
errno_assert (rc == 0);
session->flush ();
}
next_msg = &stream_engine_t::pull_and_encode;
process_msg = &stream_engine_t::write_credential;
// Compile metadata.
properties_t properties;
init_properties(properties);
// Add ZAP properties.
const properties_t& zap_properties = mechanism->get_zap_properties ();
properties.insert(zap_properties.begin (), zap_properties.end ());
// Add ZMTP properties.
const properties_t& zmtp_properties = mechanism->get_zmtp_properties ();
properties.insert(zmtp_properties.begin (), zmtp_properties.end ());
zmq_assert (metadata == NULL);
if (!properties.empty ())
metadata = new (std::nothrow) metadata_t (properties);
}
int zmq::stream_engine_t::pull_msg_from_session (msg_t *msg_)
{
return session->pull_msg (msg_);
}
int zmq::stream_engine_t::push_msg_to_session (msg_t *msg_)
{
return session->push_msg (msg_);
}
int zmq::stream_engine_t::push_raw_msg_to_session (msg_t *msg_) {
if (metadata)
msg_->set_metadata(metadata);
return push_msg_to_session(msg_);
}
int zmq::stream_engine_t::write_credential (msg_t *msg_)
{
zmq_assert (mechanism != NULL);
zmq_assert (session != NULL);
const blob_t credential = mechanism->get_user_id ();
if (credential.size () > 0) {
msg_t msg;
int rc = msg.init_size (credential.size ());
zmq_assert (rc == 0);
memcpy (msg.data (), credential.data (), credential.size ());
msg.set_flags (msg_t::credential);
rc = session->push_msg (&msg);
if (rc == -1) {
rc = msg.close ();
errno_assert (rc == 0);
return -1;
}
}
process_msg = &stream_engine_t::decode_and_push;
return decode_and_push (msg_);
}
int zmq::stream_engine_t::pull_and_encode (msg_t *msg_)
{
zmq_assert (mechanism != NULL);
if (session->pull_msg (msg_) == -1)
return -1;
if (mechanism->encode (msg_) == -1)
return -1;
return 0;
}
int zmq::stream_engine_t::decode_and_push (msg_t *msg_)
{
zmq_assert (mechanism != NULL);
if (mechanism->decode (msg_) == -1)
return -1;
if (metadata)
msg_->set_metadata (metadata);
if (session->push_msg (msg_) == -1) {
if (errno == EAGAIN)
process_msg = &stream_engine_t::push_one_then_decode_and_push;
return -1;
}
return 0;
}
int zmq::stream_engine_t::push_one_then_decode_and_push (msg_t *msg_)
{
const int rc = session->push_msg (msg_);
if (rc == 0)
process_msg = &stream_engine_t::decode_and_push;
return rc;
}
int zmq::stream_engine_t::write_subscription_msg (msg_t *msg_)
{
msg_t subscription;
// Inject the subscription message, so that also
// ZMQ 2.x peers receive published messages.
int rc = subscription.init_size (1);
errno_assert (rc == 0);
*(unsigned char*) subscription.data () = 1;
rc = session->push_msg (&subscription);
if (rc == -1)
return -1;
process_msg = &stream_engine_t::push_msg_to_session;
return push_msg_to_session (msg_);
}
void zmq::stream_engine_t::error (error_reason_t reason)
{
if (options.raw_socket) {
// For raw sockets, send a final 0-length message to the application
// so that it knows the peer has been disconnected.
msg_t terminator;
terminator.init();
(this->*process_msg) (&terminator);
terminator.close();
}
zmq_assert (session);
socket->event_disconnected (endpoint, (int) s);
session->flush ();
session->engine_error (reason);
unplug ();
delete this;
}
void zmq::stream_engine_t::set_handshake_timer ()
{
zmq_assert (!has_handshake_timer);
if (!options.raw_socket && options.handshake_ivl > 0) {
add_timer (options.handshake_ivl, handshake_timer_id);
has_handshake_timer = true;
}
}
bool zmq::stream_engine_t::init_properties (properties_t & properties) {
if (peer_address.empty()) return false;
properties.insert (std::make_pair("Peer-Address", peer_address));
return true;
}
void zmq::stream_engine_t::timer_event (int id_)
{
zmq_assert (id_ == handshake_timer_id);
has_handshake_timer = false;
// handshake timer expired before handshake completed, so engine fails
error (timeout_error);
}