Example #1
0
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);
}
Example #2
0
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);
}
Example #3
0
HIDDEN void ws_output(struct transaction_t *txn)
{
    struct ws_context *ctx = (struct ws_context *) txn->ws_ctx;
    wslay_event_context_ptr ev = ctx->event;
    int want_write = wslay_event_want_write(ev);

    syslog(LOG_DEBUG, "ws_output()  eof: %d, want write: %d",
           txn->conn->pin->eof, want_write);

    if (want_write) {
        /* Send queued frame(s) */
        int r = wslay_event_send(ev);
        if (r) {
            syslog(LOG_ERR, "wslay_event_send: %s", wslay_strerror(r));
            txn->flags.conn = CONN_CLOSE;
        }
    }
}
Example #4
0
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;
}
Example #5
0
/*
 * 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;
}
Example #6
0
static mrb_value
mrb_wslay_event_want_write(mrb_state *mrb, mrb_value self)
{
  return mrb_bool_value(wslay_event_want_write(((mrb_wslay_user_data *) DATA_PTR(self))->ctx));
}
Example #7
0
 bool want_write()
 {
   return wslay_event_want_write(ctx_);
 }
Example #8
0
/**
 * \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
	};

	vsession->flags |= VRS_TP_WEBSOCKET;

	/* Set socket non-blocking */
	flags = fcntl(io_ctx->sockfd, F_GETFL, 0);
	if( fcntl(io_ctx->sockfd, F_SETFL, flags | O_NONBLOCK) == -1) {
		v_print_log(VRS_PRINT_ERROR, "fcntl(): %s\n", strerror(errno));
		goto end;
	}

	/* Listen for HTTP request from web client and try to do
	 * WebSocket handshake */
	if(http_handshake(io_ctx->sockfd) != 0 ) {
		goto end;
	}

	/* Try to get size of TCP buffer */
	int_size = sizeof(int_size);
	if( getsockopt(io_ctx->sockfd, SOL_SOCKET, SO_RCVBUF,
			(void *)&stream_conn->socket_buffer_size, &int_size) != 0)
	{
		v_print_log(VRS_PRINT_ERROR,
				"Unable to get TCP buffer size of WebSocket connection.\n");
		goto end;
	}

	r_message = (struct VMessage*)calloc(1, sizeof(struct VMessage));
	s_message = (struct VMessage*)calloc(1, sizeof(struct VMessage));

	if(r_message == NULL || s_message == NULL) {
		v_print_log(VRS_PRINT_ERROR, "Out of memory\n");
		goto end;
	}

	CTX_r_message_set(C, r_message);
	CTX_s_message_set(C, s_message);

	/* Try to initialize WebSocket server context */
	if(wslay_event_context_server_init(&wslay_ctx, &callbacks, C) != 0) {
		v_print_log(VRS_PRINT_ERROR,
				"Unable to initialize WebSocket server context\n");
		goto end;
	}

	/* Set initial state */
	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 WebSocket state: RESPOND_methods\n");
		printf("%c[%dm", 27, 0);
	}

	/* "Never ending" loop */
	while(vsession->stream_conn->host_state != TCP_SERVER_STATE_CLOSED &&
			( wslay_event_want_read(wslay_ctx) == 1 ||
					wslay_event_want_write(wslay_ctx) == 1) )
	{

		/* When server is going to stop, then close connection with WebSocket
		 * client */
		if(vs_ctx->state != SERVER_STATE_READY) {

			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Closing WebSocket connection: Server shutdown\n");

			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);
		if(wslay_event_want_read(wslay_ctx) == 1) {
			/*
			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Waiting for WebSocket message ...\n");
			*/
			FD_SET(io_ctx->sockfd, &read_set);
		}

		/* Initialize write set */
		FD_ZERO(&write_set);
		if(wslay_event_want_write(wslay_ctx) == 1) {
#if DEBUG_WEB_SOCKET
			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Going to write message to WebSocket ...\n");
#endif
			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,			/* Don't care about exception */
				&tv)) == -1) {
			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;
				}
			}
			if (FD_ISSET(io_ctx->sockfd, &write_set)) {
				if( wslay_event_send(wslay_ctx) != 0 ) {
					goto end;
				}
			}
		} else if(ret == 0 && stream_conn->host_state != TCP_SERVER_STATE_STREAM_OPEN) {
			/* When handshake is not finished during VRS_TIMEOT seconds, then
			 * close connection with WebSocket client. */

			v_print_log(VRS_PRINT_DEBUG_MSG,
					"Closing WebSocket connection: Handshake timed-out\n");

			stream_conn->host_state = TCP_SERVER_STATE_CLOSING;

			wslay_event_queue_close(wslay_ctx,
					WSLAY_CODE_PROTOCOL_ERROR,
					(uint8_t*)"Handshake timed-out",	/* Close message */
					19);	/* The length of close message */
		}

		if(stream_conn->host_state == TCP_SERVER_STATE_STREAM_OPEN) {
			/* Check if there is any command in outgoing 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 WebSocket state: CLOSING\n");
		printf("%c[%dm", 27, 0);
	}

	/* Set up TCP CLOSING state (non-blocking) */
	vs_CLOSING(C);

	/* Receive and Send messages are not necessary 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 WebSocket 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 WebSocket state: LISTEN\n");
		printf("%c[%dm", 27, 0);
	}

	free(C);
	C = NULL;

	pthread_exit(NULL);
	return NULL;
}
Example #9
0
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;
}