void h2o_websocket_proceed(h2o_websocket_conn_t *conn) { int handled; /* run the loop until getting to a point where no more progress can be achieved */ do { handled = 0; if (!h2o_socket_is_writing(conn->sock) && wslay_event_want_write(conn->ws_ctx)) { if (wslay_event_send(conn->ws_ctx) != 0) { goto Close; } handled = 1; } if (conn->sock->input->size != 0 && wslay_event_want_read(conn->ws_ctx)) { if (wslay_event_recv(conn->ws_ctx) != 0) { goto Close; } handled = 1; } } while (handled); if (wslay_event_want_read(conn->ws_ctx)) { h2o_socket_read_start(conn->sock, on_recv); } else if (h2o_socket_is_writing(conn->sock) || wslay_event_want_write(conn->ws_ctx)) { h2o_socket_read_stop(conn->sock); } else { /* nothing is going on... close the socket */ goto Close; } return; Close: on_close(conn); }
WebSocket::WebSocket(const SocketClient::SharedPtr& client) : mClient(client) { wslay_event_callbacks callbacks = { wslayRecvCallback, wslaySendCallback, 0, // genmask_callback 0, // on_frame_recv_start_callback 0, // on_frame_recv_callback 0, // on_frame_recv_end_callback wslayOnMsgRecvCallback }; wslay_event_context_server_init(&mCtx, &callbacks, this); client->readyRead().connect([this](const SocketClient::SharedPtr& client, Buffer&& buf) { if (buf.isEmpty()) return; mBuffers.push_back(std::move(buf)); if (wslay_event_recv(mCtx) < 0) { // close socket client->close(); mClient.reset(); mError(this); } }); client->disconnected().connect([this](const SocketClient::SharedPtr& client) { mClient.reset(); mDisconnected(this); }); }
static void io_handler(void *data, EventLoop *loop, uint32_t delay) { (void) loop; DSLink *link = data; link->_delay = delay; int stat = wslay_event_recv(link->_ws); if (stat == 0 && (link->_ws->error == WSLAY_ERR_NO_MORE_MSG || link->_ws->error == 0)) { loop->shutdown = 1; } }
void broker_on_data_callback(Client *client, void *data) { Broker *broker = data; RemoteDSLink *link = client->sock_data; if (link) { link->ws->read_enabled = 1; wslay_event_recv(link->ws); if (link->pendingClose) { broker_close_link(link); } return; } HttpRequest req; char buf[1024]; { int read = dslink_socket_read(client->sock, buf, sizeof(buf) - 1); buf[read] = '\0'; broker_http_parse_req(&req, buf); } if (strcmp(req.uri.resource, "/conn") == 0) { if (strcmp(req.method, "POST") != 0) { log_info("invalid method on /conn \n"); broker_send_bad_request(client->sock); goto exit; } handle_conn(broker, &req, client->sock); } else if (strcmp(req.uri.resource, "/ws") == 0) { if (strcmp(req.method, "GET") != 0) { log_info("invalid method on /ws \n"); broker_send_bad_request(client->sock); goto exit; } handle_ws(broker, &req, client); return; } else { broker_send_not_found_error(client->sock); } exit: dslink_socket_close_nofree(client->sock); }
static mrb_value mrb_wslay_event_recv(mrb_state *mrb, mrb_value self) { mrb_wslay_user_data *data = (mrb_wslay_user_data *) DATA_PTR(self); mrb_assert(data); int err = wslay_event_recv(data->ctx); if (err == WSLAY_ERR_NOMEM) { mrb_sys_fail(mrb, "wslay_event_recv"); } else if (err == WSLAY_ERR_CALLBACK_FAILURE) { mrb_exc_raise(mrb, mrb_obj_value(mrb->exc)); } else if (err != 0) { return MRB_WSLAY_ERROR(mrb_fixnum_value(err)); } return self; }
void h2o_websocket_proceed(h2o_websocket_conn_t *conn) { int handled; /* run the loop until getting to a point where no more progress can be achieved */ do { handled = 0; if (!h2o_socket_is_writing(conn->sock) && wslay_event_want_write(conn->ws_ctx)) { if (wslay_event_send(conn->ws_ctx) != 0) { goto Close; } /* avoid infinite loop when user want send more bufers count than ours in on_msg_callback() */ if (conn->_write_buf.cnt < sizeof(conn->_write_buf.bufs) / sizeof(conn->_write_buf.bufs[0])) { handled = 1; } } if (conn->sock->input->size != 0 && wslay_event_want_read(conn->ws_ctx)) { if (wslay_event_recv(conn->ws_ctx) != 0) { goto Close; } handled = 1; } } while (handled); if (!h2o_socket_is_writing(conn->sock) && conn->_write_buf.cnt > 0) { /* write */ h2o_socket_write(conn->sock, conn->_write_buf.bufs, conn->_write_buf.cnt, on_write_complete); } if (wslay_event_want_read(conn->ws_ctx)) { h2o_socket_read_start(conn->sock, on_recv); } else if (h2o_socket_is_writing(conn->sock) || wslay_event_want_write(conn->ws_ctx)) { h2o_socket_read_stop(conn->sock); } else { /* nothing is going on... close the socket */ goto Close; } return; Close: on_close(conn); }
int websocket_handler(websocket_t *websocket) { int r; int fd = websocket->fd; int timeout = 0; fd_set read_fds; fd_set write_fds; wslay_event_context_ptr ctx = (wslay_event_context_ptr) websocket->ctx; struct timeval tv; while (websocket->state != WEBSOCKET_STOP) { FD_ZERO(&read_fds); FD_ZERO(&write_fds); if (wslay_event_want_read(ctx)) { FD_SET(fd, &read_fds); } if (wslay_event_want_write(ctx)) { FD_SET(fd, &write_fds); } tv.tv_sec = (WEBSOCKET_HANDLER_TIMEOUT / 1000); tv.tv_usec = ((WEBSOCKET_HANDLER_TIMEOUT % 1000) * 1000); r = select(fd + 1, &read_fds, &write_fds, NULL, &tv); if (r < 0) { if (errno == EAGAIN || errno == EBUSY || errno == EINTR) { continue; } WEBSOCKET_DEBUG("select function returned errno == %d\n", errno); return WEBSOCKET_SOCKET_ERROR; } else if (r == 0) { if (WEBSOCKET_HANDLER_TIMEOUT != 0) { timeout++; if ((WEBSOCKET_HANDLER_TIMEOUT * timeout) >= (WEBSOCKET_PING_INTERVAL * 10)) { timeout = 0; if (websocket_ping_counter(websocket) != WEBSOCKET_SUCCESS) { return WEBSOCKET_SOCKET_ERROR; } } } continue; } else { timeout = 0; if (FD_ISSET(fd, &read_fds)) { r = wslay_event_recv(ctx); if (r != WEBSOCKET_SUCCESS) { WEBSOCKET_DEBUG("fail to process recv event, result : %d\n", r); websocket_update_state(websocket, WEBSOCKET_ERROR); return WEBSOCKET_SOCKET_ERROR; } } if (FD_ISSET(fd, &write_fds)) { r = wslay_event_send(ctx); if (r != WEBSOCKET_SUCCESS) { WEBSOCKET_DEBUG("fail to process send event, result : %d\n", r); websocket_update_state(websocket, WEBSOCKET_ERROR); return WEBSOCKET_SOCKET_ERROR; } } } } return WEBSOCKET_SUCCESS; }
/* * Communicate with the client. This function performs HTTP handshake * and WebSocket data transfer until close handshake is done or an * error occurs. *fd* is the file descriptor of the connection to the * client. This function returns 0 if it succeeds, or returns 0. */ int communicate(int fd) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks = { recv_callback, send_callback, NULL, NULL, NULL, NULL, on_msg_recv_callback }; struct Session session = { fd }; int val = 1; struct pollfd event; int res = 0; if(http_handshake(fd) == -1) { return -1; } if(make_non_block(fd) == -1) { return -1; } if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)) == -1) { perror("setsockopt: TCP_NODELAY"); return -1; } memset(&event, 0, sizeof(struct pollfd)); event.fd = fd; event.events = POLLIN; wslay_event_context_server_init(&ctx, &callbacks, &session); /* * Event loop: basically loop until both wslay_event_want_read(ctx) * and wslay_event_want_write(ctx) return 0. */ while(wslay_event_want_read(ctx) || wslay_event_want_write(ctx)) { int r; while((r = poll(&event, 1, -1)) == -1 && errno == EINTR); if(r == -1) { perror("poll"); res = -1; break; } if(((event.revents & POLLIN) && wslay_event_recv(ctx) != 0) || ((event.revents & POLLOUT) && wslay_event_send(ctx) != 0) || (event.revents & (POLLERR | POLLHUP | POLLNVAL))) { /* * If either wslay_event_recv() or wslay_event_send() return * non-zero value, it means serious error which prevents wslay * library from processing further data, so WebSocket connection * must be closed. */ res = -1; break; } event.events = 0; if(wslay_event_want_read(ctx)) { event.events |= POLLIN; } if(wslay_event_want_write(ctx)) { event.events |= POLLOUT; } } return res; }
/** * \brief The function with websocket infinite loop */ void *vs_websocket_loop(void *arg) { /* The vContext is passed as *user_data* in callback functions. */ struct vContext *C = (struct vContext*)arg; struct VS_CTX *vs_ctx = CTX_server_ctx(C); struct IO_CTX *io_ctx = CTX_io_ctx(C); struct VStreamConn *stream_conn = CTX_current_stream_conn(C); struct VSession *vsession = CTX_current_session(C); struct VMessage *r_message=NULL, *s_message=NULL; wslay_event_context_ptr wslay_ctx; fd_set read_set, write_set; struct timeval tv; int ret, flags; unsigned int int_size; struct wslay_event_callbacks callbacks = { vs_recv_ws_callback_data, vs_send_ws_callback_data, NULL, NULL, NULL, NULL, vs_ws_recv_msg_callback }; /* Set socket blocking */ flags = fcntl(io_ctx->sockfd, F_GETFL, 0); fcntl(io_ctx->sockfd, F_SETFL, flags & ~O_NONBLOCK); http_handshake(io_ctx->sockfd); /* Try to get size of TCP buffer */ int_size = sizeof(int_size); getsockopt(io_ctx->sockfd, SOL_SOCKET, SO_RCVBUF, (void *)&stream_conn->socket_buffer_size, &int_size); r_message = (struct VMessage*)calloc(1, sizeof(struct VMessage)); s_message = (struct VMessage*)calloc(1, sizeof(struct VMessage)); CTX_r_message_set(C, r_message); CTX_s_message_set(C, s_message); stream_conn->host_state = TCP_SERVER_STATE_RESPOND_METHODS; if(is_log_level(VRS_PRINT_DEBUG_MSG)) { printf("%c[%d;%dm", 27, 1, 31); v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: RESPOND_methods\n"); printf("%c[%dm", 27, 0); } /* Set non-blocking */ flags = fcntl(io_ctx->sockfd, F_GETFL, 0); fcntl(io_ctx->sockfd, F_SETFL, flags | O_NONBLOCK); wslay_event_context_server_init(&wslay_ctx, &callbacks, C); /* "Never ending" loop */ while(vsession->stream_conn->host_state != TCP_SERVER_STATE_CLOSED) { if(vs_ctx->state != SERVER_STATE_READY) { vsession->stream_conn->host_state = TCP_SERVER_STATE_CLOSING; /* Try to close connection with websocket client */ wslay_event_queue_close(wslay_ctx, WSLAY_CODE_GOING_AWAY, (uint8_t*)"Server shutdown", /* Close message */ 15); /* The length of close message s*/ } /* Initialize read set */ FD_ZERO(&read_set); FD_SET(io_ctx->sockfd, &read_set); /* Initialize write set */ FD_ZERO(&write_set); FD_SET(io_ctx->sockfd, &write_set); /* Set timeout for select() */ if(stream_conn->host_state == TCP_SERVER_STATE_STREAM_OPEN) { /* Use negotiated FPS */ tv.tv_sec = 0; tv.tv_usec = 1000000/vsession->fps_host; } else { /* User have to send something in 30 seconds */ tv.tv_sec = VRS_TIMEOUT; tv.tv_usec = 0; } if( (ret = select(io_ctx->sockfd+1, &read_set, &write_set, NULL, /* Exception*/ &tv)) == -1) { if(is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "%s:%s():%d select(): %s\n", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); goto end; /* Was event on the listen socket */ } else if(ret>0){ if(FD_ISSET(io_ctx->sockfd, &read_set)) { if( wslay_event_recv(wslay_ctx) != 0 ) { goto end; } } else if (FD_ISSET(io_ctx->sockfd, &write_set)) { if( wslay_event_send(wslay_ctx) != 0 ) { goto end; } } } if(stream_conn->host_state == TCP_SERVER_STATE_STREAM_OPEN) { /* Check if there is any command in outgouing queue * and eventually pack these commands to buffer */ if((ret = v_STREAM_pack_message(C)) == 0 ) { goto end; } /* When at least one command was packed to buffer, then * queue this buffer to WebSocket layer */ if(ret == 1) { struct wslay_event_msg msgarg; msgarg.opcode = WSLAY_BINARY_FRAME; msgarg.msg = (uint8_t*)io_ctx->buf; msgarg.msg_length = io_ctx->buf_size; wslay_event_queue_msg(wslay_ctx, &msgarg); } } } end: if(is_log_level(VRS_PRINT_DEBUG_MSG)) { printf("%c[%d;%dm", 27, 1, 31); v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: CLOSING\n"); printf("%c[%dm", 27, 0); } /* Set up TCP CLOSING state (non-blocking) */ vs_CLOSING(C); /* Receive and Send messages are not neccessary any more */ if(r_message!=NULL) { free(r_message); r_message = NULL; CTX_r_message_set(C, NULL); } if(s_message!=NULL) { free(s_message); s_message = NULL; CTX_s_message_set(C, NULL); } /* TCP connection is considered as CLOSED, but it is not possible to use * this connection for other client */ stream_conn->host_state = TCP_SERVER_STATE_CLOSED; /* NULL pointer at stream connection */ CTX_current_stream_conn_set(C, NULL); /* Set TCP connection to CLOSED */ if(is_log_level(VRS_PRINT_DEBUG_MSG)) { printf("%c[%d;%dm", 27, 1, 31); v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: CLOSED\n"); printf("%c[%dm", 27, 0); } pthread_mutex_lock(&vs_ctx->data.mutex); /* Unsubscribe this session (this avatar) from all nodes */ vs_node_free_avatar_reference(vs_ctx, vsession); /* Try to destroy avatar node */ vs_node_destroy_avatar_node(vs_ctx, vsession); pthread_mutex_unlock(&vs_ctx->data.mutex); /* This session could be used again for authentication */ stream_conn->host_state=TCP_SERVER_STATE_LISTEN; /* Clear session flags */ vsession->flags = 0; if(is_log_level(VRS_PRINT_DEBUG_MSG)) { printf("%c[%d;%dm", 27, 1, 31); v_print_log(VRS_PRINT_DEBUG_MSG, "Server TCP state: LISTEN\n"); printf("%c[%dm", 27, 0); } free(C); C = NULL; pthread_exit(NULL); return NULL; }
int on_read_event() { return wslay_event_recv(ctx_); }
HIDDEN void ws_input(struct transaction_t *txn) { struct ws_context *ctx = (struct ws_context *) txn->ws_ctx; wslay_event_context_ptr ev = ctx->event; int want_read = wslay_event_want_read(ev); int want_write = wslay_event_want_write(ev); int goaway = txn->flags.conn & CONN_CLOSE; syslog(LOG_DEBUG, "ws_input() eof: %d, want read: %d, want write: %d", txn->conn->pin->eof, want_read, want_write); if (want_read && !goaway) { /* Read frame(s) */ if (txn->conn->sess_ctx) { /* Data has been read into request body */ ctx->h2_pin = prot_readmap(buf_base(&txn->req_body.payload), buf_len(&txn->req_body.payload)); } int r = wslay_event_recv(ev); if (txn->conn->sess_ctx) { buf_reset(&txn->req_body.payload); prot_free(ctx->h2_pin); } if (!r) { /* Successfully received frames */ syslog(LOG_DEBUG, "ws_event_recv: success"); } else if (r == WSLAY_ERR_NO_MORE_MSG) { /* Client closed connection */ syslog(LOG_DEBUG, "client closed connection"); txn->flags.conn = CONN_CLOSE; } else { /* Failure */ syslog(LOG_DEBUG, "ws_event_recv: %s (%s)", wslay_strerror(r), prot_error(txn->conn->pin)); goaway = 1; if (r == WSLAY_ERR_CALLBACK_FAILURE) { /* Client timeout */ txn->error.desc = prot_error(txn->conn->pin); } else { txn->error.desc = wslay_strerror(r); } } } else if (!want_write) { /* Connection is done */ syslog(LOG_DEBUG, "connection closed"); txn->flags.conn = CONN_CLOSE; } if (goaway) { /* Tell client we are closing session */ syslog(LOG_WARNING, "%s, closing connection", txn->error.desc); syslog(LOG_DEBUG, "wslay_event_queue_close()"); int r = wslay_event_queue_close(ev, WSLAY_CODE_GOING_AWAY, (uint8_t *) txn->error.desc, strlen(txn->error.desc)); if (r) { syslog(LOG_ERR, "wslay_event_queue_close: %s", wslay_strerror(r)); } txn->flags.conn = CONN_CLOSE; } /* Write frame(s) */ ws_output(txn); return; }