Exemplo n.º 1
0
/*  Ceases further I/O on the underlying socket and prepares to send a
    close handshake on the next receive. */
static void nn_sws_fail_conn (struct nn_sws *self, int code, char *reason)
{
    size_t reason_len;
    size_t payload_len;
    uint8_t mask [4];
    uint8_t *payload_pos;

    nn_assert_state (self, NN_SWS_STATE_ACTIVE);

    /*  Destroy any remnant incoming message fragments. */
    nn_msg_array_term (&self->inmsg_array);

    /*  Reason string + code. */
    reason_len = strlen (reason);
    payload_len = reason_len + 2;

    /*  Ensure text is short enough to also include code and framing. */
    nn_assert (payload_len <= NN_SWS_MAX_SMALL_PAYLOAD);

    /*  RFC 6455 section 5.5.1. */
    self->fail_msg [0] = NN_SWS_FRAME_BITMASK_FIN | NN_WS_OPCODE_CLOSE;

    /*  Size of the payload, which is the status code plus the reason. */
    self->fail_msg [1] = payload_len;

    self->fail_msg_len = NN_SWS_FRAME_SIZE_INITIAL;

    /*  Generate 32-bit mask as per RFC 6455 5.3. */
    if (self->mode == NN_WS_CLIENT) {
        self->fail_msg [1] |= NN_SWS_FRAME_BITMASK_MASKED;
        nn_random_generate (mask, sizeof (mask));
        memcpy (&self->fail_msg [NN_SWS_FRAME_SIZE_INITIAL], mask, 4);
        self->fail_msg_len += 4;
    }

    payload_pos = &self->fail_msg [self->fail_msg_len];
    
    /*  Copy Status Code in network order (big-endian). */
    nn_puts (payload_pos, (uint16_t) code);
    self->fail_msg_len += 2;

    /*  Copy Close Reason immediately following the code. */
    memcpy (payload_pos + 2, reason, reason_len);

    /*  If this is a client, apply mask. */
    if (self->mode == NN_WS_CLIENT) {
        nn_sws_mask_payload (payload_pos, payload_len, mask, NULL);
    }

    self->fail_msg_len += payload_len;

    self->instate = NN_SWS_INSTATE_FAILING;

    /*  On the next recv, the connection will be failed. Why defer
        until the next recv? Semantically, until then, this incoming
        message has not been interpreted, so it's not until then that
        it could be failed. This type of pre-processing is necessary
        to early fail chunked transfers. */
    nn_pipebase_received (&self->pipebase);
}
Exemplo n.º 2
0
static void nn_cinproc_connect (struct nn_ins_item *self,
    struct nn_ins_item *peer)
{
    struct nn_cinproc *cinproc;
    struct nn_binproc *binproc;
    struct nn_sinproc *sinproc;

    cinproc = nn_cont (self, struct nn_cinproc, item);
    binproc = nn_cont (peer, struct nn_binproc, item);

    nn_assert_state (cinproc, NN_CINPROC_STATE_ACTIVE);

    sinproc = nn_alloc (sizeof (struct nn_sinproc), "sinproc");
    alloc_assert (sinproc);
    nn_sinproc_init (sinproc, NN_CINPROC_SRC_SINPROC,
        cinproc->item.ep, &cinproc->fsm);

    nn_list_insert (&cinproc->sinprocs, &sinproc->item,
        nn_list_end (&cinproc->sinprocs));

    nn_sinproc_connect (sinproc, &binproc->fsm);

    nn_ep_stat_increment (cinproc->item.ep, NN_STAT_INPROGRESS_CONNECTIONS, -1);
    nn_ep_stat_increment (cinproc->item.ep, NN_STAT_ESTABLISHED_CONNECTIONS, 1);
}
Exemplo n.º 3
0
static int nn_stcp_send (struct nn_pipebase *self, struct nn_msg *msg)
{
    struct nn_stcp *stcp;
    struct nn_iovec iov [3];

    stcp = nn_cont (self, struct nn_stcp, pipebase);
    nn_assert (stcp);

    nn_assert_state (stcp, NN_STCP_STATE_ACTIVE);
    nn_assert (stcp->outstate == NN_STCP_OUTSTATE_IDLE);

    /*  Move the message to the local storage. */
    nn_msg_term (&stcp->outmsg);
    nn_msg_mv (&stcp->outmsg, msg);

    /*  Serialise the message header. */
    nn_putll (stcp->outhdr, nn_chunkref_size (&stcp->outmsg.sphdr) +
        nn_chunkref_size (&stcp->outmsg.body));

    /*  Start async sending. */
    iov [0].iov_base = stcp->outhdr;
    iov [0].iov_len = sizeof (stcp->outhdr);
    iov [1].iov_base = nn_chunkref_data (&stcp->outmsg.sphdr);
    iov [1].iov_len = nn_chunkref_size (&stcp->outmsg.sphdr);
    iov [2].iov_base = nn_chunkref_data (&stcp->outmsg.body);
    iov [2].iov_len = nn_chunkref_size (&stcp->outmsg.body);
    nn_usock_send (stcp->usock, iov, 3);

    stcp->outstate = NN_STCP_OUTSTATE_SENDING;

    return 0;
}
Exemplo n.º 4
0
void nn_pipebase_term(struct nn_pipebase *self)
{
    nn_assert_state(self,NN_PIPEBASE_STATE_IDLE);
    nn_fsm_event_term(&self->out);
    nn_fsm_event_term(&self->in);
    nn_fsm_term(&self->fsm);
}
Exemplo n.º 5
0
Arquivo: ep.c Projeto: WongTai/nanomsg
void nn_ep_term (struct nn_ep *self)
{
    nn_assert_state (self, NN_EP_STATE_IDLE);

    self->epbase->vfptr->destroy (self->epbase);
    nn_list_item_term (&self->item);
    nn_fsm_term (&self->fsm);
}
Exemplo n.º 6
0
void nn_timer_term (struct nn_timer *self)
{
    nn_assert_state (self, NN_TIMER_STATE_IDLE);

    nn_fsm_event_term (&self->done);
    nn_worker_timer_term (&self->wtimer);
    nn_worker_task_term (&self->stop_task);
    nn_worker_task_term (&self->start_task);
    nn_fsm_term (&self->fsm);
}
Exemplo n.º 7
0
void nn_stcp_term(struct nn_stcp *self)
{
    nn_assert_state(self,NN_STCP_STATE_IDLE);
    nn_fsm_event_term(&self->done);
    nn_msg_term(&self->outmsg);
    nn_msg_term(&self->inmsg);
    nn_pipebase_term(&self->pipebase);
    nn_streamhdr_term(&self->streamhdr);
    nn_fsm_term(&self->fsm);
}
Exemplo n.º 8
0
void nn_slibfabric_term (struct nn_slibfabric *self)
{
    nn_assert_state (self, NN_SLIBFABRIC_STATE_IDLE);

    nn_fsm_event_term (&self->done);
    nn_msg_term (&self->outmsg);
    nn_msg_term (&self->inmsg);
    nn_pipebase_term (&self->pipebase);
    nn_streamhdr_term (&self->streamhdr);
    nn_fsm_term (&self->fsm);
}
Exemplo n.º 9
0
void nn_atcp_term (struct nn_atcp *self)
{
    nn_assert_state (self, NN_ATCP_STATE_IDLE);

    nn_list_item_term (&self->item);
    nn_fsm_event_term (&self->done);
    nn_fsm_event_term (&self->accepted);
    nn_stcp_term (&self->stcp);
    nn_usock_term (&self->usock);
    nn_fsm_term (&self->fsm);
}
Exemplo n.º 10
0
void nn_sws_term (struct nn_sws *self)
{
    nn_assert_state (self, NN_SWS_STATE_IDLE);

    nn_fsm_event_term (&self->done);
    nn_msg_term (&self->outmsg);
    nn_msg_array_term (&self->inmsg_array);
    nn_pipebase_term (&self->pipebase);
    nn_wshdr_term (&self->wshdr);
    nn_fsm_term (&self->fsm);
}
Exemplo n.º 11
0
void nn_aws_term (struct nn_aws *self)
{
    nn_assert_state (self, NN_AWS_STATE_IDLE);

    nn_list_item_term (&self->item);
    nn_fsm_event_term (&self->done);
    nn_fsm_event_term (&self->accepted);
    nn_sws_term (&self->sws);
    nn_usock_term (&self->usock);
    nn_fsm_term (&self->fsm);
}
Exemplo n.º 12
0
static int32_t nn_stcp_recv(struct nn_pipebase *self,struct nn_msg *msg)
{
    struct nn_stcp *stcp;
    stcp = nn_cont(self,struct nn_stcp, pipebase);
    nn_assert_state(stcp, NN_STCP_STATE_ACTIVE);
    nn_assert(stcp->instate == NN_STCP_INSTATE_HASMSG);
    nn_msg_mv(msg,&stcp->inmsg); // Move received message to the user
    nn_msg_init(&stcp->inmsg,0);
    stcp->instate = NN_STCP_INSTATE_HDR; // Start receiving new message
    nn_usock_recv(stcp->usock,stcp->inhdr,sizeof(stcp->inhdr),NULL);
    return 0;
}
Exemplo n.º 13
0
void nn_atcp_start (struct nn_atcp *self, struct nn_usock *listener)
{
    nn_assert_state (self, NN_ATCP_STATE_IDLE);

    /*  Take ownership of the listener socket. */
    self->listener = listener;
    self->listener_owner.src = NN_ATCP_SRC_LISTENER;
    self->listener_owner.fsm = &self->fsm;
    nn_usock_swap_owner (listener, &self->listener_owner);

    /*  Start the state machine. */
    nn_fsm_start (&self->fsm);
}
Exemplo n.º 14
0
static void nn_cinproc_connect (struct nn_ins_item *self,
                                struct nn_ins_item *peer)
{
    struct nn_cinproc *cinproc;
    struct nn_binproc *binproc;

    cinproc = nn_cont (self, struct nn_cinproc, item);
    binproc = nn_cont (peer, struct nn_binproc, item);

    nn_assert_state (cinproc, NN_CINPROC_STATE_DISCONNECTED);
    nn_sinproc_connect (&cinproc->sinproc, &binproc->fsm);
    nn_fsm_action (&cinproc->fsm, NN_CINPROC_ACTION_CONNECT);
}
Exemplo n.º 15
0
static void nn_bws_destroy (struct nn_epbase *self)
{
    struct nn_bws *bws;

    bws = nn_cont (self, struct nn_bws, epbase);

    nn_assert_state (bws, NN_BWS_STATE_IDLE);
    nn_list_term (&bws->awss);
    nn_assert (bws->aws == NULL);
    nn_usock_term (&bws->usock);
    nn_epbase_term (&bws->epbase);
    nn_fsm_term (&bws->fsm);

    nn_free (bws);
}
Exemplo n.º 16
0
static void nn_btcp_destroy (struct nn_epbase *self)
{
    struct nn_btcp *btcp;

    btcp = nn_cont (self, struct nn_btcp, epbase);

    nn_assert_state (btcp, NN_BTCP_STATE_IDLE);
    nn_list_term (&btcp->atcps);
    nn_assert (btcp->atcp == NULL);
    nn_usock_term (&btcp->usock);
    nn_epbase_term (&btcp->epbase);
    nn_fsm_term (&btcp->fsm);

    nn_free (btcp);
}
Exemplo n.º 17
0
static void nn_bipc_destroy (struct nn_epbase *self)
{
    struct nn_bipc *bipc;

    bipc = nn_cont (self, struct nn_bipc, epbase);

    nn_assert_state (bipc, NN_BIPC_STATE_IDLE);
    nn_list_term (&bipc->aipcs);
    nn_assert (bipc->aipc == NULL);
    nn_usock_term (&bipc->usock);
    nn_backoff_term (&bipc->retry);
    nn_epbase_term (&bipc->epbase);
    nn_fsm_term (&bipc->fsm);

    nn_free (bipc);
}
Exemplo n.º 18
0
int32_t nn_pipebase_start(struct nn_pipebase *self)
{
    int32_t rc;
    nn_assert_state(self,NN_PIPEBASE_STATE_IDLE);
    self->state = NN_PIPEBASE_STATE_ACTIVE;
    self->instate = NN_PIPEBASE_INSTATE_ASYNC;
    self->outstate = NN_PIPEBASE_OUTSTATE_IDLE;
    rc = nn_sock_add(self->sock,(struct nn_pipe *)self);
    if ( nn_slow (rc < 0) )
    {
        self->state = NN_PIPEBASE_STATE_FAILED;
        return rc;
    }
    if ( self->sock )
        nn_fsm_raise(&self->fsm,&self->out,NN_PIPE_OUT);
    return 0;
}
Exemplo n.º 19
0
int nn_usock_listen (struct nn_usock *self, int backlog)
{
    int rc;

    /*  You can start listening only before the socket is connected. */
    nn_assert_state (self, NN_USOCK_STATE_STARTING);

    /*  Start listening for incoming connections. */
    rc = listen (self->s, backlog);
    //printf("usock.%d -> listen rc.%d errno.%d %s\n",self->s,rc,errno,nn_strerror(errno));
    if (nn_slow (rc != 0))
        return -errno;

    /*  Notify the state machine. */
    nn_fsm_action (&self->fsm, NN_USOCK_ACTION_LISTEN);

    return 0;
}
Exemplo n.º 20
0
void nn_usock_send (struct nn_usock *self, const struct nn_iovec *iov,
    int iovcnt)
{
    int rc;
    int i;
    int out;

    /*  Make sure that the socket is actually alive. */
    nn_assert_state (self, NN_USOCK_STATE_ACTIVE);

    /*  Copy the iovecs to the socket. */
    nn_assert (iovcnt <= NN_USOCK_MAX_IOVCNT);
    self->out.hdr.msg_iov = self->out.iov;
    out = 0;
    for (i = 0; i != iovcnt; ++i) {
        if (iov [i].iov_len == 0)
            continue;
        self->out.iov [out].iov_base = iov [i].iov_base;
        self->out.iov [out].iov_len = iov [i].iov_len;
        out++;
        //PNACL_message("{%d} ",(int)iov [i].iov_len);
    }
    //PNACL_message("iov[%d]\n",out);
    self->out.hdr.msg_iovlen = out;

    /*  Try to send the data immediately. */
    rc = nn_usock_send_raw (self, &self->out.hdr);

    /*  Success. */
    if (nn_fast (rc == 0)) {
        nn_fsm_raise (&self->fsm, &self->event_sent, NN_USOCK_SENT);
        return;
    }

    /*  Errors. */
    if (nn_slow (rc != -EAGAIN)) {
        errnum_assert (rc == -ECONNRESET, -rc);
        nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ERROR);
        return;
    }

    /*  Ask the worker thread to send the remaining data. */
    nn_worker_execute (self->worker, &self->task_send);
}
Exemplo n.º 21
0
static int nn_slibfabric_recv (struct nn_pipebase *self, struct nn_msg *msg)
{
    struct nn_slibfabric *slibfabric;

    slibfabric = nn_cont (self, struct nn_slibfabric, pipebase);

    nn_assert_state (slibfabric, NN_SLIBFABRIC_STATE_ACTIVE);
    nn_assert (slibfabric->instate == NN_SLIBFABRIC_INSTATE_HASMSG);

    /*  Move received message to the user. */
    nn_msg_mv (msg, &slibfabric->inmsg);
    nn_msg_init (&slibfabric->inmsg, 0);

    /*  Start receiving new message. */
    slibfabric->instate = NN_SLIBFABRIC_INSTATE_HDR;
    nn_usock_recv (slibfabric->usock, slibfabric->inhdr, sizeof (slibfabric->inhdr), NULL);

    return 0;
}
Exemplo n.º 22
0
Arquivo: sipc.c Projeto: CPB9/nanomsg
static int nn_sipc_recv (struct nn_pipebase *self, struct nn_msg *msg)
{
    struct nn_sipc *sipc;

    sipc = nn_cont (self, struct nn_sipc, pipebase);

    nn_assert_state (sipc, NN_SIPC_STATE_ACTIVE);
    nn_assert (sipc->instate == NN_SIPC_INSTATE_HASMSG);

    /*  Move received message to the user. */
    nn_msg_mv (msg, &sipc->inmsg);
    nn_msg_init (&sipc->inmsg, 0);

    /*  Start receiving new message. */
    sipc->instate = NN_SIPC_INSTATE_HDR;
    nn_usock_recv (sipc->usock, sipc->inhdr, sizeof (sipc->inhdr));

    return 0;
}
Exemplo n.º 23
0
int nn_usock_bind (struct nn_usock *self, const struct sockaddr *addr,
    size_t addrlen)
{
    int rc;
    int opt;

    /*  The socket can be bound only before it's connected. */
    nn_assert_state (self, NN_USOCK_STATE_STARTING);

    /*  Allow re-using the address. */
    opt = 1;
    rc = setsockopt (self->s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));
    errno_assert (rc == 0);

    rc = bind (self->s, addr, (socklen_t) addrlen);
    //printf("usock.%d -> bind rc.%d errno.%d %s\n",self->s,rc,errno,nn_strerror(errno));
    if (nn_slow (rc != 0))
        return -errno;

    return 0;
}
Exemplo n.º 24
0
void nn_usock_term (struct nn_usock *self)
{
    nn_assert_state (self, NN_USOCK_STATE_IDLE);

    if (self->in.batch)
        nn_free (self->in.batch);

    nn_fsm_event_term (&self->event_error);
    nn_fsm_event_term (&self->event_received);
    nn_fsm_event_term (&self->event_sent);
    nn_fsm_event_term (&self->event_established);

    nn_worker_cancel (self->worker, &self->task_recv);

    nn_worker_task_term (&self->task_stop);
    nn_worker_task_term (&self->task_recv);
    nn_worker_task_term (&self->task_send);
    nn_worker_task_term (&self->task_accept);
    nn_worker_task_term (&self->task_connected);
    nn_worker_task_term (&self->task_connecting);
    nn_worker_fd_term (&self->wfd);

    nn_fsm_term (&self->fsm);
}
Exemplo n.º 25
0
void nn_usock_recv (struct nn_usock *self, void *buf, size_t len, int *fd)
{
    int rc;
    size_t nbytes;

    /*  Make sure that the socket is actually alive. */
    nn_assert_state (self, NN_USOCK_STATE_ACTIVE);

    /*  Try to receive the data immediately. */
    nbytes = len;
    self->in.pfd = fd;
    rc = nn_usock_recv_raw (self, buf, &nbytes);
    if (nn_slow (rc < 0)) {
        errnum_assert (rc == -ECONNRESET, -rc);
        //PNACL_message("rc.%d vs ECONNRESET\n",rc,ECONNRESET);
        nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ERROR);
        return;
    }
    //int i;
    //for (i=0; i<16&&i<nbytes; i++)
    //    PNACL_message("%02x ",((uint8_t *)buf)[i]);
    //PNACL_message("nn_usock_recv nbytes.%d\n",(int)nbytes);
    /*  Success. */
    if (nn_fast (nbytes == len)) {
        //PNACL_message("raise NN_USOCK_RECEIVED\n");
        nn_fsm_raise (&self->fsm, &self->event_received, NN_USOCK_RECEIVED);
        return;
    }

    /*  There are still data to receive in the background. */
    self->in.buf = ((uint8_t*) buf) + nbytes;
    self->in.len = len - nbytes;

    /*  Ask the worker thread to receive the remaining data. */
    nn_worker_execute (self->worker, &self->task_recv);
}
Exemplo n.º 26
0
static void nn_binproc_connect (struct nn_ins_item *self,
    struct nn_ins_item *peer)
{
    struct nn_binproc *binproc;
    struct nn_cinproc *cinproc;
    struct nn_sinproc *sinproc;

    binproc = nn_cont (self, struct nn_binproc, item);
    nn_assert (binproc);
    cinproc = nn_cont (peer, struct nn_cinproc, item);
    nn_assert (cinproc);
    nn_assert_state (binproc, NN_BINPROC_STATE_ACTIVE);

    sinproc = nn_alloc (sizeof (struct nn_sinproc), "sinproc");
    alloc_assert (sinproc);
    nn_sinproc_init (sinproc, NN_BINPROC_SRC_SINPROC,
        &binproc->item.epbase, &binproc->fsm);
    nn_list_insert (&binproc->sinprocs, &sinproc->item,
        nn_list_end (&binproc->sinprocs));
    nn_sinproc_connect (sinproc, &cinproc->fsm);

    nn_epbase_stat_increment (&binproc->item.epbase,
        NN_STAT_ACCEPTED_CONNECTIONS, 1);
}
Exemplo n.º 27
0
/*  Start sending a message. */
static int nn_sws_send (struct nn_pipebase *self, struct nn_msg *msg)
{
    struct nn_sws *sws;
    struct nn_iovec iov [3];
    int mask_pos;
    size_t sz;
    size_t hdrsz;
    uint8_t mask [4];

    sws = nn_cont (self, struct nn_sws, pipebase);

    nn_assert_state (sws, NN_SWS_STATE_ACTIVE);
    nn_assert (sws->outstate == NN_SWS_OUTSTATE_IDLE);

    /*  Move the message to the local storage. */
    nn_msg_term (&sws->outmsg);
    nn_msg_mv (&sws->outmsg, msg);

    /*  Compose the message header. See RFC 6455, section 5.2. */

    /*  Messages are always sent in a single fragment.
        They may be split up on the way to the peer though. */
    sws->outhdr [0] = NN_WS_OPCODE_BINARY | NN_SWS_FRAME_BITMASK_FIN;
    hdrsz = 1;
    
    /*  Frame the payload size. Don't set the mask bit yet. */
    sz = nn_chunkref_size (&sws->outmsg.sphdr) +
        nn_chunkref_size (&sws->outmsg.body);
    if (sz <= 0x7d) {
        sws->outhdr [1] = (uint8_t) sz;
        hdrsz += 1;
    }
    else if (sz <= 0xffff) {
        sws->outhdr [1] = 0x7e;
        nn_puts (&sws->outhdr [2], (uint16_t) sz);
        hdrsz += 3;
    }
    else {
        sws->outhdr [1] = 0x7f;
        nn_putll (&sws->outhdr [2], (uint64_t) sz);
        hdrsz += 9;
    }

    /*  Client-to-server communication has to be masked. See RFC 6455 5.3. */
    if (sws->mode == NN_WS_CLIENT) {

        /*  Generate 32-bit mask and store it in the frame. */
        /*  TODO: This is not a strong source of entropy. However, can we
            afford one wihout exhausting all the available entropy in the
            system at the high message rates? */
        nn_random_generate (mask, 4);
        sws->outhdr [1] |= NN_SWS_FRAME_BITMASK_MASKED;
        memcpy (&sws->outhdr [hdrsz], mask, 4);
        hdrsz += 4;

        /*  Mask payload, beginning with header and moving to body. */
        /*  TODO: This won't work if the message is shared among muliple
                   transports. We probably want to send the message in multiple
                   operations, masking only as much data at a time. */
        mask_pos = 0;
        nn_sws_mask_payload (nn_chunkref_data (&sws->outmsg.sphdr),
            nn_chunkref_size (&sws->outmsg.sphdr), mask, &mask_pos);
        nn_sws_mask_payload (nn_chunkref_data (&sws->outmsg.body),
            nn_chunkref_size (&sws->outmsg.body), mask, &mask_pos);
    }

    /*  Start async sending. */
    iov [0].iov_base = sws->outhdr;
    iov [0].iov_len = hdrsz;
    iov [1].iov_base = nn_chunkref_data (&sws->outmsg.sphdr);
    iov [1].iov_len = nn_chunkref_size (&sws->outmsg.sphdr);
    iov [2].iov_base = nn_chunkref_data (&sws->outmsg.body);
    iov [2].iov_len = nn_chunkref_size (&sws->outmsg.body);
    nn_usock_send (sws->usock, iov, 3);

    sws->outstate = NN_SWS_OUTSTATE_SENDING;

    /*  If a Close handshake was just sent, it's time to shut down. */
    if ((sws->outhdr [0] & NN_SWS_FRAME_BITMASK_OPCODE) ==
        NN_WS_OPCODE_CLOSE) {
        nn_pipebase_stop (&sws->pipebase);
        sws->state = NN_SWS_STATE_CLOSING_CONNECTION;
    }

    return 0;
}
Exemplo n.º 28
0
static int nn_sws_recv (struct nn_pipebase *self, struct nn_msg *msg)
{
    struct nn_sws *sws;
    struct nn_iovec iov [1];
    struct nn_list_item *it;
    struct msg_chunk *ch;
    int pos;
    size_t len;

    sws = nn_cont (self, struct nn_sws, pipebase);

    nn_assert_state (sws, NN_SWS_STATE_ACTIVE);

    switch (sws->instate) {
    case NN_SWS_INSTATE_FAILING:

        /*  Prevent further send/recv operations on this connection. */
        nn_pipebase_stop (self);
        sws->instate = NN_SWS_INSTATE_CLOSED;

        /*  Inform user this connection has been failed. */
        nn_msg_init (msg, 1);
        *(uint8_t *) nn_chunkref_data (&msg->body) = 0x7f |
            NN_SWS_FRAME_BITMASK_FIN;

        iov [0].iov_base = sws->fail_msg;
        iov [0].iov_len = sws->fail_msg_len;

        /*  TODO: Consider queueing and unconditionally sending close
            handshake rather than skipping it. */
        /*  RFC 6455 7.1.7 - try to send helpful Closing Handshake only if
            the socket is not currently sending. If it's still busy sending,
            forcibly close this connection, since it's not readily deterministic
            how much time that action could take to complete, or if the peer is
            even healthy enough to receive. Rationale: try to be nice, but be
            mindful of self-preservation! */
        if (sws->outstate == NN_SWS_OUTSTATE_IDLE) {
            nn_usock_send (sws->usock, iov, 1);
            sws->outstate = NN_SWS_OUTSTATE_SENDING;
            sws->state = NN_SWS_STATE_CLOSING_CONNECTION;
        }
        else {
            sws->state = NN_SWS_STATE_DONE;
            nn_fsm_raise (&sws->fsm, &sws->done,
                NN_SWS_RETURN_CLOSE_HANDSHAKE);
        }
        return 0;
    
    case NN_SWS_INSTATE_RECVD_CHUNKED:

        /*  This library should not deliver fragmented messages to the
            application, so it's expected that this is the final frame. */
        nn_assert (sws->is_final_frame);

        len = sws->inmsg_total_size;

        nn_msg_init (msg, len);

        /*  Reassemble incoming message scatter array. */
        while (!nn_list_empty (&sws->inmsg_array)) {
            it = nn_list_begin (&sws->inmsg_array);
            ch = nn_cont (it, struct msg_chunk, item);
            memcpy (((uint8_t*) nn_chunkref_data (&msg->body)) + pos,
                nn_chunkref_data (&ch->chunk),
                nn_chunkref_size (&ch->chunk));
            pos += nn_chunkref_size (&ch->chunk);
            nn_msg_chunk_term (ch, &sws->inmsg_array);
        }

        nn_assert (pos == len);
        nn_assert (nn_list_empty (&sws->inmsg_array));

        /*  No longer collecting scatter array of incoming msg chunks. */
        sws->continuing = 0;

        nn_sws_recv_hdr (sws);

        return 0;

    case NN_SWS_INSTATE_RECVD_CONTROL:

        /*  This library should not deliver fragmented messages to the user, so
        it's expected that this is the final frame. */
        nn_assert (sws->is_final_frame);

        len = sws->inmsg_current_chunk_len + sizeof (sws->inmsg_hdr);

        nn_msg_init (msg, len);

        /*  Relay opcode, RSV and FIN bits to the user in order to
            interpret payload. */
        memcpy (nn_chunkref_data (&msg->body),
            &sws->inhdr, sizeof (sws->inmsg_hdr));
        pos = sizeof (sws->inmsg_hdr);

        memcpy (((uint8_t*) nn_chunkref_data (&msg->body)) + pos,
            sws->inmsg_control, sws->inmsg_current_chunk_len);

        /*  If a closing handshake was just transferred to the application,
            discontinue continual, async receives. */
        if (sws->opcode == NN_WS_OPCODE_CLOSE) {
            sws->instate = NN_SWS_INSTATE_CLOSED;
        }
        else {
            nn_sws_recv_hdr (sws);
        }

        return 0;

    default:
        /*  Unexpected state. */
        nn_assert (0);
        return 0;
    }
}