static void nn_bws_handler (struct nn_fsm *self, int src, int type, void *srcptr) { struct nn_bws *bws; struct nn_aws *aws; bws = nn_cont (self, struct nn_bws, fsm); switch (bws->state) { /******************************************************************************/ /* IDLE state. */ /******************************************************************************/ case NN_BWS_STATE_IDLE: nn_assert (src == NN_FSM_ACTION); nn_assert (type == NN_FSM_START); bws->state = NN_BWS_STATE_ACTIVE; return; /******************************************************************************/ /* ACTIVE state. */ /* The execution is yielded to the aws state machine in this state. */ /******************************************************************************/ case NN_BWS_STATE_ACTIVE: if (src == NN_BWS_SRC_USOCK) { nn_assert (type == NN_USOCK_SHUTDOWN || type == NN_USOCK_STOPPED); return; } /* For all remaining events we'll assume they are coming from one of remaining child aws objects. */ nn_assert (src == NN_BWS_SRC_AWS); aws = (struct nn_aws*) srcptr; switch (type) { case NN_AWS_ACCEPTED: /* Move the newly created connection to the list of existing connections. */ nn_list_insert (&bws->awss, &bws->aws->item, nn_list_end (&bws->awss)); bws->aws = NULL; /* Start waiting for a new incoming connection. */ nn_bws_start_accepting (bws); return; case NN_AWS_ERROR: nn_aws_stop (aws); return; case NN_AWS_STOPPED: nn_list_erase (&bws->awss, &aws->item); nn_aws_term (aws); nn_free (aws); return; default: nn_fsm_bad_action (bws->state, src, type); } /******************************************************************************/ /* Invalid state. */ /******************************************************************************/ default: nn_fsm_bad_state (bws->state, src, type); } }
static void nn_aws_handler (struct nn_fsm *self, int src, int type, NN_UNUSED void *srcptr) { struct nn_aws *aws; int val; size_t sz; aws = nn_cont (self, struct nn_aws, fsm); nn_assert (aws); switch (aws->state) { /******************************************************************************/ /* IDLE state. */ /* The state machine wasn't yet started. */ /******************************************************************************/ case NN_AWS_STATE_IDLE: switch (src) { case NN_FSM_ACTION: switch (type) { case NN_FSM_START: nn_usock_accept (&aws->usock, aws->listener); aws->state = NN_AWS_STATE_ACCEPTING; return; default: nn_fsm_bad_action (aws->state, src, type); } default: nn_fsm_bad_source (aws->state, src, type); } /******************************************************************************/ /* ACCEPTING state. */ /* Waiting for incoming connection. */ /******************************************************************************/ case NN_AWS_STATE_ACCEPTING: switch (src) { case NN_AWS_SRC_USOCK: switch (type) { case NN_USOCK_ACCEPTED: nn_epbase_clear_error (aws->epbase); /* Set the relevant socket options. */ sz = sizeof (val); nn_epbase_getopt (aws->epbase, NN_SOL_SOCKET, NN_SNDBUF, &val, &sz); nn_assert (sz == sizeof (val)); nn_usock_setsockopt (&aws->usock, SOL_SOCKET, SO_SNDBUF, &val, sizeof (val)); sz = sizeof (val); nn_epbase_getopt (aws->epbase, NN_SOL_SOCKET, NN_RCVBUF, &val, &sz); nn_assert (sz == sizeof (val)); nn_usock_setsockopt (&aws->usock, SOL_SOCKET, SO_RCVBUF, &val, sizeof (val)); /* Since the WebSocket handshake must poll, the receive timeout is set to zero. Later, it will be set again to the value specified by the socket option. */ val = 0; sz = sizeof (val); nn_usock_setsockopt (&aws->usock, SOL_SOCKET, SO_RCVTIMEO, &val, sizeof (val)); /* Return ownership of the listening socket to the parent. */ nn_usock_swap_owner (aws->listener, &aws->listener_owner); aws->listener = NULL; aws->listener_owner.src = -1; aws->listener_owner.fsm = NULL; nn_fsm_raise (&aws->fsm, &aws->accepted, NN_AWS_ACCEPTED); /* Start the sws state machine. */ nn_usock_activate (&aws->usock); nn_sws_start (&aws->sws, &aws->usock, NN_WS_SERVER, NULL, NULL); aws->state = NN_AWS_STATE_ACTIVE; nn_epbase_stat_increment (aws->epbase, NN_STAT_ACCEPTED_CONNECTIONS, 1); return; default: nn_fsm_bad_action (aws->state, src, type); } case NN_AWS_SRC_LISTENER: switch (type) { case NN_USOCK_ACCEPT_ERROR: nn_epbase_set_error (aws->epbase, nn_usock_geterrno (aws->listener)); nn_epbase_stat_increment (aws->epbase, NN_STAT_ACCEPT_ERRORS, 1); nn_usock_accept (&aws->usock, aws->listener); return; default: nn_fsm_bad_action (aws->state, src, type); } default: nn_fsm_bad_source (aws->state, src, type); } /******************************************************************************/ /* ACTIVE state. */ /******************************************************************************/ case NN_AWS_STATE_ACTIVE: switch (src) { case NN_AWS_SRC_SWS: switch (type) { case NN_SWS_RETURN_CLOSE_HANDSHAKE: /* Peer closed connection without intention to reconnect, or local endpoint failed remote because of invalid data. */ nn_sws_stop (&aws->sws); aws->state = NN_AWS_STATE_STOPPING_SWS; return; case NN_SWS_RETURN_ERROR: nn_sws_stop (&aws->sws); aws->state = NN_AWS_STATE_STOPPING_SWS; nn_epbase_stat_increment (aws->epbase, NN_STAT_BROKEN_CONNECTIONS, 1); return; default: nn_fsm_bad_action (aws->state, src, type); } default: nn_fsm_bad_source (aws->state, src, type); } /******************************************************************************/ /* STOPPING_SWS state. */ /******************************************************************************/ case NN_AWS_STATE_STOPPING_SWS: switch (src) { case NN_AWS_SRC_SWS: switch (type) { case NN_USOCK_SHUTDOWN: return; case NN_SWS_RETURN_STOPPED: nn_usock_stop (&aws->usock); aws->state = NN_AWS_STATE_STOPPING_USOCK; return; default: nn_fsm_bad_action (aws->state, src, type); } default: nn_fsm_bad_source (aws->state, src, type); } /******************************************************************************/ /* STOPPING_USOCK state. */ /******************************************************************************/ case NN_AWS_STATE_STOPPING_USOCK: switch (src) { case NN_AWS_SRC_USOCK: switch (type) { case NN_USOCK_SHUTDOWN: return; case NN_USOCK_STOPPED: nn_aws_stop (aws); return; default: nn_fsm_bad_action (aws->state, src, type); } default: nn_fsm_bad_source (aws->state, src, type); } /******************************************************************************/ /* Invalid state. */ /******************************************************************************/ default: nn_fsm_bad_state (aws->state, src, type); } }
static void nn_bws_shutdown (struct nn_fsm *self, int src, int type, void *srcptr) { struct nn_bws *bws; struct nn_list_item *it; struct nn_aws *aws; bws = nn_cont (self, struct nn_bws, fsm); if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { if (bws->aws) { nn_aws_stop (bws->aws); bws->state = NN_BWS_STATE_STOPPING_AWS; } else { bws->state = NN_BWS_STATE_STOPPING_USOCK; } } if (nn_slow (bws->state == NN_BWS_STATE_STOPPING_AWS)) { if (!nn_aws_isidle (bws->aws)) return; nn_aws_term (bws->aws); nn_free (bws->aws); bws->aws = NULL; nn_usock_stop (&bws->usock); bws->state = NN_BWS_STATE_STOPPING_USOCK; } if (nn_slow (bws->state == NN_BWS_STATE_STOPPING_USOCK)) { if (!nn_usock_isidle (&bws->usock)) return; for (it = nn_list_begin (&bws->awss); it != nn_list_end (&bws->awss); it = nn_list_next (&bws->awss, it)) { aws = nn_cont (it, struct nn_aws, item); nn_aws_stop (aws); } bws->state = NN_BWS_STATE_STOPPING_AWSS; goto awss_stopping; } if (nn_slow (bws->state == NN_BWS_STATE_STOPPING_AWSS)) { nn_assert (src == NN_BWS_SRC_AWS && type == NN_AWS_STOPPED); aws = (struct nn_aws *) srcptr; nn_list_erase (&bws->awss, &aws->item); nn_aws_term (aws); nn_free (aws); /* If there are no more aws state machines, we can stop the whole bws object. */ awss_stopping: if (nn_list_empty (&bws->awss)) { bws->state = NN_BWS_STATE_IDLE; nn_fsm_stopped_noevent (&bws->fsm); nn_epbase_stopped (&bws->epbase); return; } return; } nn_fsm_bad_action (bws->state, src, type); }