Ejemplo n.º 1
0
static void nn_stream_received (const struct nn_cp_sink **self,
    struct nn_usock *usock)
{
    struct nn_stream *stream;
    uint64_t size;

    stream = nn_cont (self, struct nn_stream, sink);
    switch (stream->instate) {
    case NN_STREAM_INSTATE_HDR:
        size = nn_getll (stream->inhdr);
        nn_msg_term (&stream->inmsg);
        nn_msg_init (&stream->inmsg, (size_t) size);
        if (!size) {
            nn_pipebase_received (&stream->pipebase);
            break;
        }
        stream->instate = NN_STREAM_INSTATE_BODY;
        nn_usock_recv (stream->usock, nn_chunkref_data (&stream->inmsg.body),
            (size_t) size);
        break;
    case NN_STREAM_INSTATE_BODY:
        nn_pipebase_received (&stream->pipebase);
        break;
    default:
        nn_assert (0);
    }
}
Ejemplo n.º 2
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);
}
Ejemplo n.º 3
0
/*  Ensures that Close frames received from peer conform to
    RFC 6455, section 7. */
static void nn_sws_validate_close_handshake (struct nn_sws *self)
{
    uint16_t close_code;

    /*  TODO: As per RFC 6455 7.1.6, the Close Reason following the Close Code
        must be well-formed UTF-8. Can we be liberal (as per Postel
        principle) and not check the validity of the UTF-8 here? */

    close_code = nn_gets (self->inmsg_current_chunk_buf);

    if (close_code == NN_SWS_CLOSE_NORMAL ||
        close_code == NN_SWS_CLOSE_GOING_AWAY ||
        close_code == NN_SWS_CLOSE_ERR_PROTO ||
        close_code == NN_SWS_CLOSE_ERR_WUT ||
        close_code == NN_SWS_CLOSE_ERR_INVALID_FRAME ||
        close_code == NN_SWS_CLOSE_ERR_POLICY ||
        close_code == NN_SWS_CLOSE_ERR_TOOBIG ||
        close_code == NN_SWS_CLOSE_ERR_EXTENSION ||
        close_code == NN_SWS_CLOSE_ERR_SERVER) {
        /*  RFC 6455 7.4.1 */
        self->instate = NN_SWS_INSTATE_RECVD_CONTROL;
        nn_pipebase_received (&self->pipebase);
    }
    else if (close_code >= 3000 && close_code <= 3999) {
        /*  RFC 6455 7.4.2 */
        self->instate = NN_SWS_INSTATE_RECVD_CONTROL;
        nn_pipebase_received (&self->pipebase);
    }
    else if (close_code >= 4000 && close_code <= 4999) {
        /*  RFC 6455 7.4.2 */
        self->instate = NN_SWS_INSTATE_RECVD_CONTROL;
        nn_pipebase_received (&self->pipebase);
    }
    else {
        nn_sws_fail_conn (self, NN_SWS_CLOSE_ERR_PROTO,
            "Unrecognized close code.");
    }
}
Ejemplo n.º 4
0
static void nn_stcp_handler(struct nn_fsm *self,int32_t src,int32_t type,NN_UNUSED void *srcptr)
{
    int32_t rc,opt; struct nn_stcp *stcp; uint64_t size; size_t opt_sz = sizeof(opt);
    stcp = nn_cont(self,struct nn_stcp,fsm);
    switch (stcp->state)
    {
/******************************************************************************/
/*  IDLE state.                                                               */
/******************************************************************************/
    case NN_STCP_STATE_IDLE:
        switch (src) {

        case NN_FSM_ACTION:
            switch (type) {
            case NN_FSM_START:
                //printf("streamhdr start\n");
                nn_streamhdr_start(&stcp->streamhdr,stcp->usock,&stcp->pipebase);
                stcp->state = NN_STCP_STATE_PROTOHDR;
                return;
            default: nn_fsm_bad_action (stcp->state,src,type);
            }
        default: nn_fsm_bad_source (stcp->state, src, type);
        }
/******************************************************************************/
/*  PROTOHDR state.                                                           */
/******************************************************************************/
    case NN_STCP_STATE_PROTOHDR:
        switch ( src )
        {
        case NN_STCP_SRC_STREAMHDR:
            switch ( type )
            {
            case NN_STREAMHDR_OK:
                // Before moving to the active state stop the streamhdr state machine
                nn_streamhdr_stop(&stcp->streamhdr);
                stcp->state = NN_STCP_STATE_STOPPING_STREAMHDR;
                return;
            case NN_STREAMHDR_ERROR:
                // Raise the error and move directly to the DONE state. streamhdr object will be stopped later on
                stcp->state = NN_STCP_STATE_DONE;
                nn_fsm_raise(&stcp->fsm,&stcp->done,NN_STCP_ERROR);
                return;
            default: nn_fsm_bad_action (stcp->state, src, type);
            }
        default: nn_fsm_bad_source(stcp->state,src,type);
        }
/******************************************************************************/
/*  STOPPING_STREAMHDR state.                                                 */
/******************************************************************************/
    case NN_STCP_STATE_STOPPING_STREAMHDR:
        switch ( src )
        {
        case NN_STCP_SRC_STREAMHDR:
            switch ( type )
            {
            case NN_STREAMHDR_STOPPED:
                 /*  Start the pipe. */
                 rc = nn_pipebase_start(&stcp->pipebase);
                 if ( nn_slow (rc < 0) )
                 {
                    stcp->state = NN_STCP_STATE_DONE;
                    nn_fsm_raise(&stcp->fsm, &stcp->done, NN_STCP_ERROR);
                    return;
                 }
                 // Start receiving a message in asynchronous manner
                 stcp->instate = NN_STCP_INSTATE_HDR;
                 nn_usock_recv(stcp->usock,&stcp->inhdr,sizeof(stcp->inhdr),NULL);
                 //printf("STCP recv.[%d %d %d %d]\n",stcp->inhdr[0],stcp->inhdr[1],stcp->inhdr[2],stcp->inhdr[3]);
                 // Mark the pipe as available for sending
                 stcp->outstate = NN_STCP_OUTSTATE_IDLE;
                 stcp->state = NN_STCP_STATE_ACTIVE;
                 return;
            default: nn_fsm_bad_action (stcp->state, src, type);
            }
        default: nn_fsm_bad_source (stcp->state, src, type);
        }

/******************************************************************************/
/*  ACTIVE state.                                                             */
/******************************************************************************/
    case NN_STCP_STATE_ACTIVE:
        switch ( src )
        {
        case NN_STCP_SRC_USOCK:
            switch ( type )
            {
            case NN_USOCK_SENT:
                // The message is now fully sent
                nn_assert (stcp->outstate == NN_STCP_OUTSTATE_SENDING);
                stcp->outstate = NN_STCP_OUTSTATE_IDLE;
                nn_msg_term(&stcp->outmsg);
                nn_msg_init(&stcp->outmsg,0);
                nn_pipebase_sent(&stcp->pipebase);
                return;
            case NN_USOCK_RECEIVED:
                switch ( stcp->instate )
                {
                case NN_STCP_INSTATE_HDR:
                    // Message header was received. Check that message size is acceptable by comparing with NN_RCVMAXSIZE; if it's too large, drop the connection
                    size = nn_getll(stcp->inhdr);
                    nn_pipebase_getopt(&stcp->pipebase,NN_SOL_SOCKET,NN_RCVMAXSIZE,&opt,&opt_sz);
                    if ( opt != -1 && size > opt )
                    {
                        stcp->state = NN_STCP_STATE_DONE;
                        nn_fsm_raise(&stcp->fsm,&stcp->done,NN_STCP_ERROR);
                        return;
                    }
                    // Allocate memory for the message
                    nn_msg_term(&stcp->inmsg);
                    nn_msg_init(&stcp->inmsg,(size_t)size);
                    // Special case when size of the message body is 0
                    if ( !size )
                    {
                        printf("STCP: zero size pipebase recv\n");
                        stcp->instate = NN_STCP_INSTATE_HASMSG;
                        nn_pipebase_received (&stcp->pipebase);
                        return;
                    }
                    // Start receiving the message body
                    stcp->instate = NN_STCP_INSTATE_BODY;
                    nn_usock_recv (stcp->usock,nn_chunkref_data(&stcp->inmsg.body),(size_t)size,NULL);
                    return;
                case NN_STCP_INSTATE_BODY:
                    // Message body was received. Notify the owner that it can receive it
                    stcp->instate = NN_STCP_INSTATE_HASMSG;
                    nn_pipebase_received(&stcp->pipebase);
                    return;
                default: nn_fsm_error("Unexpected socket instate",stcp->state,src,type);
                }
            case NN_USOCK_SHUTDOWN:
                nn_pipebase_stop(&stcp->pipebase);
                stcp->state = NN_STCP_STATE_SHUTTING_DOWN;
                return;
            case NN_USOCK_ERROR:
                nn_pipebase_stop(&stcp->pipebase);
                stcp->state = NN_STCP_STATE_DONE;
                nn_fsm_raise(&stcp->fsm,&stcp->done,NN_STCP_ERROR);
                return;
            default: nn_fsm_bad_action(stcp->state,src,type);
            }
        default: nn_fsm_bad_source(stcp->state,src,type);
        }
/******************************************************************************/
/*  SHUTTING_DOWN state.                                                      */
/*  The underlying connection is closed. We are just waiting that underlying  */
/*  usock being closed                                                        */
/******************************************************************************/
    case NN_STCP_STATE_SHUTTING_DOWN:
        switch (src) {

        case NN_STCP_SRC_USOCK:
            switch (type) {
            case NN_USOCK_ERROR:
                stcp->state = NN_STCP_STATE_DONE;
                nn_fsm_raise (&stcp->fsm, &stcp->done, NN_STCP_ERROR);
                return;
            default:
                nn_fsm_bad_action (stcp->state, src, type);
            }

        default:
            nn_fsm_bad_source (stcp->state, src, type);
        }


/******************************************************************************/
/*  DONE state.                                                               */
/*  The underlying connection is closed. There's nothing that can be done in  */
/*  this state except stopping the object.                                    */
/******************************************************************************/
    case NN_STCP_STATE_DONE:
        nn_fsm_bad_source (stcp->state, src, type);

/******************************************************************************/
/*  Invalid state.                                                            */
/******************************************************************************/
    default:
        nn_fsm_bad_state (stcp->state, src, type);
    }
}
Ejemplo n.º 5
0
static void nn_sws_handler (struct nn_fsm *self, int src, int type,
    NN_UNUSED void *srcptr)
{
    struct nn_sws *sws;
    int rc;

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

    switch (sws->state) {

/******************************************************************************/
/*  IDLE state.                                                               */
/******************************************************************************/
    case NN_SWS_STATE_IDLE:
        switch (src) {

        case NN_FSM_ACTION:
            switch (type) {
            case NN_FSM_START:
                nn_wshdr_start (&sws->wshdr, sws->usock,
                    &sws->pipebase, sws->mode, sws->remote_host);
                sws->state = NN_SWS_STATE_HANDSHAKE;
                return;
            default:
                nn_fsm_bad_action (sws->state, src, type);
            }

        default:
            nn_fsm_bad_source (sws->state, src, type);
        }

/******************************************************************************/
/*  HANDSHAKE state.                                                          */
/******************************************************************************/
    case NN_SWS_STATE_HANDSHAKE:
        switch (src) {

        case NN_SWS_SRC_HANDSHAKE:
            switch (type) {
            case NN_WSHDR_OK:

                /*  Before moving to the active state stop the handshake
                    state machine. */
                nn_wshdr_stop (&sws->wshdr);
                sws->state = NN_SWS_STATE_STOPPING_HANDSHAKE;
                return;

            case NN_WSHDR_ERROR:

                /* Raise the error and move directly to the DONE state.
                   wshdr object will be stopped later on. */
                sws->state = NN_SWS_STATE_DONE;
                nn_fsm_raise (&sws->fsm, &sws->done,
                    NN_SWS_RETURN_CLOSE_HANDSHAKE);
                return;

            default:
                nn_fsm_bad_action (sws->state, src, type);
            }

        default:
            nn_fsm_bad_source (sws->state, src, type);
        }

/******************************************************************************/
/*  STOPPING_HANDSHAKE state.                                                 */
/******************************************************************************/
    case NN_SWS_STATE_STOPPING_HANDSHAKE:
        switch (src) {

        case NN_SWS_SRC_HANDSHAKE:
            switch (type) {
            case NN_WSHDR_STOPPED:

                 /*  Start the pipe. */
                 rc = nn_pipebase_start (&sws->pipebase);
                 if (nn_slow (rc < 0)) {
                    sws->state = NN_SWS_STATE_DONE;
                    nn_fsm_raise (&sws->fsm, &sws->done, NN_SWS_RETURN_ERROR);
                    return;
                 }

                 /*  Start receiving a message in asynchronous manner. */
                 nn_sws_recv_hdr (sws);

                 /*  Mark the pipe as available for sending. */
                 sws->outstate = NN_SWS_OUTSTATE_IDLE;

                 sws->state = NN_SWS_STATE_ACTIVE;
                 return;

            default:
                nn_fsm_bad_action (sws->state, src, type);
            }

        default:
            nn_fsm_bad_source (sws->state, src, type);
        }

/******************************************************************************/
/*  ACTIVE state.                                                             */
/******************************************************************************/
    case NN_SWS_STATE_ACTIVE:
        switch (src) {

        case NN_SWS_SRC_USOCK:
            switch (type) {
            case NN_USOCK_SENT:

                /*  The message is now fully sent. */
                nn_assert (sws->outstate == NN_SWS_OUTSTATE_SENDING);
                sws->outstate = NN_SWS_OUTSTATE_IDLE;
                nn_msg_term (&sws->outmsg);
                nn_msg_init (&sws->outmsg, 0);
                nn_pipebase_sent (&sws->pipebase);
                return;

            case NN_USOCK_RECEIVED:

                switch (sws->instate) {
                case NN_SWS_INSTATE_RECV_HDR:

                    /*  Require RSV1, RSV2, and RSV3 bits to be unset for
                        as per RFC 6455 section 5.2. */
                    if (sws->inhdr [0] & NN_SWS_FRAME_BITMASK_RSV1 ||
                        sws->inhdr [0] & NN_SWS_FRAME_BITMASK_RSV2 ||
                        sws->inhdr [0] & NN_SWS_FRAME_BITMASK_RSV3) {
                        nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO,
                            "RSV1, RSV2, and RSV3 must be unset.");
                        return;
                    }

                    sws->is_final_frame = sws->inhdr [0] &
                        NN_SWS_FRAME_BITMASK_FIN;
     
                    /*  Communication from client to server must be masked.
                        Communication from server to client must be unmasked. */
                    if (sws->mode == NN_WS_SERVER) {
                        nn_assert (sws->inhdr [1] &
                            NN_SWS_FRAME_BITMASK_MASKED);
                        sws->ext_hdr_len = 4;
                    }
                    else {
                        nn_assert (!(sws->inhdr [1] &
                            NN_SWS_FRAME_BITMASK_MASKED));
                        sws->ext_hdr_len = 0;
                    }

                    sws->opcode = sws->inhdr [0] &
                        NN_SWS_FRAME_BITMASK_OPCODE;
                    sws->payload_ctl = sws->inhdr [1] &
                        NN_SWS_FRAME_BITMASK_LENGTH;

                    /*  Prevent unexpected continuation frame. */
                    if (!sws->continuing &&
                        sws->opcode == NN_WS_OPCODE_FRAGMENT) {
                        nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO,
                            "No message to continue.");
                        return;
                    }

                    /*  Preserve initial message opcode and RSV bits in case
                        this is a fragmented message. */
                    if (!sws->continuing)
                        sws->inmsg_hdr = sws->inhdr [0] |
                        NN_SWS_FRAME_BITMASK_FIN;

                    if (sws->payload_ctl <= 0x7d) {
                        sws->ext_hdr_len += NN_SWS_FRAME_SIZE_PAYLOAD_0;
                    }
                    else if (sws->payload_ctl <= 0xffff) {
                        sws->ext_hdr_len += NN_SWS_FRAME_SIZE_PAYLOAD_16;
                    }
                    else {
                        sws->ext_hdr_len += NN_SWS_FRAME_SIZE_PAYLOAD_63;
                    }

                    switch (sws->opcode) {

                    case NN_WS_OPCODE_BINARY:

                        sws->is_control_frame = 0;

                        if (sws->continuing) {
                            nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO,
                                "Expected continuation frame opcode.");
                            return;
                        }

                        if (!sws->is_final_frame)
                            sws->continuing = 1;

                        if (sws->ext_hdr_len == 0 && sws->payload_ctl == 0) {
                            /*  Only a remote server could send a 2-byte msg;
                                sanity-check that this endpoint is a client. */
                            nn_assert (sws->mode == NN_WS_CLIENT);

                            sws->inmsg_current_chunk_len = 0;

                            if (sws->continuing) {
                                /*  This frame was empty, but continue
                                    next frame in fragmented sequence. */
                                nn_sws_recv_hdr (sws);
                                return;
                            }
                            else {
                                /*  Special case when there is no payload,
                                    mask, or additional frames. */
                                sws->instate = NN_SWS_INSTATE_RECVD_CHUNKED;
                                nn_pipebase_received (&sws->pipebase);
                                return;
                            }
                            }
                        /*  Continue to receive extended header+payload. */
                        break;

                    case NN_WS_OPCODE_FRAGMENT:

                        sws->is_control_frame = 0;
                        sws->continuing = !sws->is_final_frame;

                        if (sws->ext_hdr_len == 0 && sws->payload_ctl == 0) {
                            /*  Only a remote server could send a 2-byte msg;
                                sanity-check that this endpoint is a client. */
                            nn_assert (sws->mode == NN_WS_CLIENT);

                            sws->inmsg_current_chunk_len = 0;

                            if (sws->continuing) {
                                /*  This frame was empty, but continue
                                    next frame in fragmented sequence. */
                                nn_sws_recv_hdr (sws);
                                return;
                            }
                            else {
                                /*  Special case when there is no payload,
                                    mask, or additional frames. */
                                sws->instate = NN_SWS_INSTATE_RECVD_CHUNKED;
                                nn_pipebase_received (&sws->pipebase);
                                return;
                            }
                        }
                        /*  Continue to receive extended header+payload. */
                        break;
                    
                    case NN_WS_OPCODE_CLOSE:
                        /*  RFC 6455 section 5.5.1. */
                        sws->is_control_frame = 1;
                        if (!sws->is_final_frame) {
                            /*  As per RFC 6455 section 5.4, fragmentation of
                                control frames is not allowed; on receipt the
                                endpoint MUST close connection immediately. */
                            nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO,
                                "Cannot fragment control message (FIN=0).");
                            return;
                        }

                        if (sws->payload_ctl > NN_SWS_MAX_SMALL_PAYLOAD) {
                            /*  As per RFC 6455 section 5.4, large payloads on
                                control frames is not allowed, and on receipt
                                the endpoint MUST close connection
                                immediately. */
                            nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO,
                                "Control frame payload exceeds allowable length.");
                            return;
                        }

                        if (sws->payload_ctl == 1) {
                            /*  As per RFC 6455 section 5.5.1, if a payload is
                                to accompany a close frame, the first two bytes
                                MUST be the close code. */
                            nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO,
                                "Expected 2byte close code.");
                            return;
                        }

                        if (sws->ext_hdr_len == 0 && sws->payload_ctl == 0) {
                            /*  Special case when there is no payload,
                                mask, or additional frames. */
                            sws->inmsg_current_chunk_len = 0;
                            sws->instate = NN_SWS_INSTATE_RECVD_CONTROL;
                            nn_pipebase_received (&sws->pipebase);
                            return;
                        }
                        /*  Continue to receive extended header+payload. */
                        break;
                    
                    default:
                        /*  Client sent an invalid opcode; as per RFC 6455
                            section 10.7, close connection with code. */
                        nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO,
                                "Invalid opcode.");
                        return;

                    }

                    if (sws->ext_hdr_len == 0) {
                        /*  Only a remote server could send a 2-byte msg;
                            sanity-check that this endpoint is a client. */
                        nn_assert (sws->mode == NN_WS_CLIENT);

                        /*  In the case of no additional header, the payload
                            is known to not exceed this threshold. */
                        nn_assert (sws->payload_ctl <= 0x7d);

                        /*  In the case of no additional header, the payload
                            is known to not exceed this threshold. */
                        nn_assert (sws->payload_ctl > 0);

                        sws->instate = NN_SWS_INSTATE_RECV_PAYLOAD;
                        sws->inmsg_current_chunk_len = sws->payload_ctl;


                        /*  Use scatter/gather array for application messages,
                            and a fixed-width buffer for control messages. This
                            is convenient since control messages can be
                            interspersed between chunked application msgs. */
                        if (sws->is_control_frame) {
                            sws->inmsg_current_chunk_buf = sws->inmsg_control;
                        }
                        else {
                            sws->inmsg_chunks++;
                            sws->inmsg_total_size += sws->inmsg_current_chunk_len;
                            sws->inmsg_current_chunk_buf =
                                nn_msg_chunk_new (sws->inmsg_current_chunk_len,
                                &sws->inmsg_array);
                        }

                        nn_usock_recv (sws->usock, sws->inmsg_current_chunk_buf,
                            sws->inmsg_current_chunk_len, NULL);
                        return;
                    }
                    else {
                        /*  Continue receiving the rest of the header frame. */
                        sws->instate = NN_SWS_INSTATE_RECV_HDREXT;
                        nn_usock_recv (sws->usock,
                            sws->inhdr + NN_SWS_FRAME_SIZE_INITIAL,
                            sws->ext_hdr_len,
                            NULL);
                        return;
                    }

                case NN_SWS_INSTATE_RECV_HDREXT:
                    nn_assert (sws->ext_hdr_len > 0);

                    if (sws->payload_ctl <= 0x7d) {
                        sws->inmsg_current_chunk_len = sws->payload_ctl;
                        if (sws->mode == NN_WS_SERVER) {
                            memcpy (sws->mask,
                               sws->inhdr + NN_SWS_FRAME_SIZE_INITIAL, 4);
                        }
                    }
                    else if (sws->payload_ctl == 0xffff) {
                        sws->inmsg_current_chunk_len =
                            nn_gets (sws->inhdr + NN_SWS_FRAME_SIZE_INITIAL);
                        if (sws->mode == NN_WS_SERVER) {
                            memcpy (sws->mask, sws->inhdr +
                                NN_SWS_FRAME_SIZE_INITIAL +
                                NN_SWS_FRAME_SIZE_PAYLOAD_16, 4);
                        }
                    }
                    else {
                        sws->inmsg_current_chunk_len =
                            (size_t) nn_getll (sws->inhdr +
                            NN_SWS_FRAME_SIZE_INITIAL);
                        if (sws->mode == NN_WS_SERVER) {
                            memcpy (sws->mask, sws->inhdr +
                                NN_SWS_FRAME_SIZE_INITIAL +
                                NN_SWS_FRAME_SIZE_PAYLOAD_63, 4);
                        }
                    }

                    /*  Handle zero-length message bodies. */
                    if (sws->inmsg_current_chunk_len == 0)
                    {
                        if (sws->is_final_frame) {
                            sws->instate = (sws->is_control_frame ?
                                NN_SWS_INSTATE_RECVD_CONTROL :
                                NN_SWS_INSTATE_RECVD_CHUNKED);
                            nn_pipebase_received (&sws->pipebase);
                            return;
                        }
                        else {
                            nn_sws_recv_hdr (sws);
                            return;
                        }
                    }

                    nn_assert (sws->inmsg_current_chunk_len > 0);

                    /*  Use scatter/gather array for application messages,
                        and a fixed-width buffer for control messages. This
                        is convenient since control messages can be
                        interspersed between chunked application msgs. */
                    if (sws->is_control_frame) {
                        sws->inmsg_current_chunk_buf = sws->inmsg_control;
                    }
                    else {
                        sws->inmsg_chunks++;
                        sws->inmsg_total_size += sws->inmsg_current_chunk_len;
                        sws->inmsg_current_chunk_buf =
                            nn_msg_chunk_new (sws->inmsg_current_chunk_len,
                            &sws->inmsg_array);
                    }

                    sws->instate = NN_SWS_INSTATE_RECV_PAYLOAD;
                    nn_usock_recv (sws->usock, sws->inmsg_current_chunk_buf,
                        sws->inmsg_current_chunk_len, NULL);
                    return;

                case NN_SWS_INSTATE_RECV_PAYLOAD:

                    /*  Unmask if necessary. */
                    if (sws->mode == NN_WS_SERVER) {
                        nn_sws_mask_payload (sws->inmsg_current_chunk_buf,
                            sws->inmsg_current_chunk_len, sws->mask, NULL);
                    }

                    switch (sws->opcode) {

                    case NN_WS_OPCODE_BINARY:
                    case NN_WS_OPCODE_FRAGMENT:
                        if (sws->is_final_frame) {
                            sws->instate = NN_SWS_INSTATE_RECVD_CHUNKED;
                            nn_pipebase_received (&sws->pipebase);
                        }
                        else {
                            nn_sws_recv_hdr (sws);
                        }
                        return;

                    case NN_WS_OPCODE_CLOSE:

                        /*  If the payload is not even long enough for the
                            required 2-octet Close Code, the connection
                            should have been failed upstream. */
                        nn_assert (sws->inmsg_current_chunk_len >= 2);
                        
                        nn_sws_validate_close_handshake (sws);
                        return;

                    default:
                        /*  This should have been prevented upstream. */
                        nn_assert (0);
                        return;
                    } 

                default:
                    nn_fsm_error ("Unexpected socket instate",
                        sws->state, src, type);
                }

            case NN_USOCK_SHUTDOWN:
                nn_pipebase_stop (&sws->pipebase);
                sws->state = NN_SWS_STATE_BROKEN_CONNECTION;
                return;

            case NN_USOCK_ERROR:
                nn_pipebase_stop (&sws->pipebase);
                sws->state = NN_SWS_STATE_DONE;
                nn_fsm_raise (&sws->fsm, &sws->done, NN_SWS_RETURN_ERROR);
                return;

            default:
                nn_fsm_bad_action (sws->state, src, type);
            }

            break;

        default:
            nn_fsm_bad_source (sws->state, src, type);
        }

/******************************************************************************/
/*  CLOSING_CONNECTION state.                                                 */
/*  Wait for acknowledgement closing handshake was successfully sent.         */
/******************************************************************************/
    case NN_SWS_STATE_CLOSING_CONNECTION:
        switch (src) {

        case NN_SWS_SRC_USOCK:
            switch (type) {
            case NN_USOCK_SENT:
                /*  Wait for acknowledgement closing handshake was sent
                    to peer. */
                nn_assert (sws->outstate == NN_SWS_OUTSTATE_SENDING);
                sws->outstate = NN_SWS_OUTSTATE_IDLE;
                sws->state = NN_SWS_STATE_DONE;
                nn_fsm_raise (&sws->fsm, &sws->done,
                    NN_SWS_RETURN_CLOSE_HANDSHAKE);
                return;
            case NN_USOCK_SHUTDOWN:
                return;
            case NN_USOCK_ERROR:
                sws->state = NN_SWS_STATE_DONE;
                nn_fsm_raise (&sws->fsm, &sws->done, NN_SWS_RETURN_ERROR);
                return;
            default:
                nn_fsm_bad_action (sws->state, src, type);
            }

        default:
            nn_fsm_bad_source (sws->state, src, type);
        }

/******************************************************************************/
/*  SHUTTING_DOWN state.                                                      */
/*  The underlying connection is closed. We are just waiting that underlying  */
/*  usock being closed                                                        */
/******************************************************************************/
    case NN_SWS_STATE_BROKEN_CONNECTION:
        switch (src) {

        case NN_SWS_SRC_USOCK:
            switch (type) {
            case NN_USOCK_ERROR:
                sws->state = NN_SWS_STATE_DONE;
                nn_fsm_raise (&sws->fsm, &sws->done, NN_SWS_RETURN_ERROR);
                return;
            default:
                nn_fsm_bad_action (sws->state, src, type);
            }

        default:
            nn_fsm_bad_source (sws->state, src, type);
        }

/******************************************************************************/
/*  DONE state.                                                               */
/*  The underlying connection is closed. There's nothing that can be done in  */
/*  this state except stopping the object.                                    */
/******************************************************************************/
    case NN_SWS_STATE_DONE:
        nn_fsm_bad_source (sws->state, src, type);

/******************************************************************************/
/*  Invalid state.                                                            */
/******************************************************************************/
    default:
        nn_fsm_bad_state (sws->state, src, type);
    }
}
Ejemplo n.º 6
0
static void nn_stcp_handler (struct nn_fsm *self, int src, int type,
    void *srcptr)
{
    int rc;
    struct nn_stcp *stcp;
    uint64_t size;

    stcp = nn_cont (self, struct nn_stcp, fsm);

    switch (stcp->state) {

/******************************************************************************/
/*  IDLE state.                                                               */
/******************************************************************************/
    case NN_STCP_STATE_IDLE:
        switch (src) {

        case NN_FSM_ACTION:
            switch (type) {
            case NN_FSM_START:
                nn_streamhdr_start (&stcp->streamhdr, stcp->usock,
                    &stcp->pipebase);
                stcp->state = NN_STCP_STATE_PROTOHDR;
                return;
            default:
                nn_fsm_bad_action (stcp->state, src, type);
            }

        default:
            nn_fsm_bad_source (stcp->state, src, type);
        }

/******************************************************************************/
/*  PROTOHDR state.                                                           */
/******************************************************************************/
    case NN_STCP_STATE_PROTOHDR:
        switch (src) {

        case NN_STCP_SRC_STREAMHDR:
            switch (type) {
            case NN_STREAMHDR_OK:

                /*  Before moving to the active state stop the streamhdr
                    state machine. */
                nn_streamhdr_stop (&stcp->streamhdr);
                stcp->state = NN_STCP_STATE_STOPPING_STREAMHDR;
                return;

            case NN_STREAMHDR_ERROR:

                /* Raise the error and move directly to the DONE state.
                   streamhdr object will be stopped later on. */
                stcp->state = NN_STCP_STATE_DONE;
                nn_fsm_raise (&stcp->fsm, &stcp->done, NN_STCP_ERROR);
                return;

            default:
                nn_fsm_bad_action (stcp->state, src, type);
            }

        default:
            nn_fsm_bad_source (stcp->state, src, type);
        }

/******************************************************************************/
/*  STOPPING_STREAMHDR state.                                                 */
/******************************************************************************/
    case NN_STCP_STATE_STOPPING_STREAMHDR:
        switch (src) {

        case NN_STCP_SRC_STREAMHDR:
            switch (type) {
            case NN_STREAMHDR_STOPPED:

                 /*  Start the pipe. */
                 rc = nn_pipebase_start (&stcp->pipebase);
                 errnum_assert (rc == 0, -rc);

                 /*  Start receiving a message in asynchronous manner. */
                 stcp->instate = NN_STCP_INSTATE_HDR;
                 nn_usock_recv (stcp->usock, &stcp->inhdr,
                     sizeof (stcp->inhdr));

                 /*  Mark the pipe as available for sending. */
                 stcp->outstate = NN_STCP_OUTSTATE_IDLE;

                 stcp->state = NN_STCP_STATE_ACTIVE;
                 return;

            default:
                nn_fsm_bad_action (stcp->state, src, type);
            }

        default:
            nn_fsm_bad_source (stcp->state, src, type);
        }

/******************************************************************************/
/*  ACTIVE state.                                                             */
/******************************************************************************/
    case NN_STCP_STATE_ACTIVE:
        switch (src) {

        case NN_STCP_SRC_USOCK:
            switch (type) {
            case NN_USOCK_SENT:

                /*  The message is now fully sent. */
                nn_assert (stcp->outstate == NN_STCP_OUTSTATE_SENDING);
                stcp->outstate = NN_STCP_OUTSTATE_IDLE;
                nn_msg_term (&stcp->outmsg);
                nn_msg_init (&stcp->outmsg, 0);
                nn_pipebase_sent (&stcp->pipebase);
                return;

            case NN_USOCK_RECEIVED:

                switch (stcp->instate) {
                case NN_STCP_INSTATE_HDR:

                    /*  Message header was received. Allocate memory for the
                        message. */
                    size = nn_getll (stcp->inhdr);
                    nn_msg_term (&stcp->inmsg);
                    nn_msg_init (&stcp->inmsg, (size_t) size);

                    /*  Special case when size of the message body is 0. */
                    if (!size) {
                        stcp->instate = NN_STCP_INSTATE_HASMSG;
                        nn_pipebase_received (&stcp->pipebase);
                        return;
                    }

                    /*  Start receiving the message body. */
                    stcp->instate = NN_STCP_INSTATE_BODY;
                    nn_usock_recv (stcp->usock,
                        nn_chunkref_data (&stcp->inmsg.body), (size_t) size);

                    return;

                case NN_STCP_INSTATE_BODY:

                    /*  Message body was received. Notify the owner that it
                        can receive it. */
                    stcp->instate = NN_STCP_INSTATE_HASMSG;
                    nn_pipebase_received (&stcp->pipebase);

                    return;

                default:
                    nn_fsm_error("Unexpected socket instate",
                        stcp->state, src, type);
                }

            case NN_USOCK_SHUTDOWN:
                nn_pipebase_stop (&stcp->pipebase);
                stcp->state = NN_STCP_STATE_SHUTTING_DOWN;
                return;

            case NN_USOCK_ERROR:
                nn_pipebase_stop (&stcp->pipebase);
                stcp->state = NN_STCP_STATE_DONE;
                nn_fsm_raise (&stcp->fsm, &stcp->done, NN_STCP_ERROR);
                return;

            default:
                nn_fsm_bad_action (stcp->state, src, type);
            }

        default:
            nn_fsm_bad_source (stcp->state, src, type);
        }

/******************************************************************************/
/*  SHUTTING_DOWN state.                                                      */
/*  The underlying connection is closed. We are just waiting that underlying  */
/*  usock being closed                                                        */
/******************************************************************************/
    case NN_STCP_STATE_SHUTTING_DOWN:
        switch (src) {

        case NN_STCP_SRC_USOCK:
            switch (type) {
            case NN_USOCK_ERROR:
                stcp->state = NN_STCP_STATE_DONE;
                nn_fsm_raise (&stcp->fsm, &stcp->done, NN_STCP_ERROR);
                return;
            default:
                nn_fsm_bad_action (stcp->state, src, type);
            }

        default:
            nn_fsm_bad_source (stcp->state, src, type);
        }


/******************************************************************************/
/*  DONE state.                                                               */
/*  The underlying connection is closed. There's nothing that can be done in  */
/*  this state except stopping the object.                                    */
/******************************************************************************/
    case NN_STCP_STATE_DONE:
        nn_fsm_bad_source (stcp->state, src, type);

/******************************************************************************/
/*  Invalid state.                                                            */
/******************************************************************************/
    default:
        nn_fsm_bad_state (stcp->state, src, type);
    }
}