예제 #1
0
파일: sws.c 프로젝트: threatstack/nanomsg
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);
    }
}
예제 #2
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);
    }
}
예제 #3
0
파일: stcp.c 프로젝트: 4Second2None/nanomsg
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);
    }
}