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); } }
/* 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); }
/* 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."); } }
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); } }
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); } }
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); } }