int communicate(const char *host, const char *service, const char *path, const struct wslay_event_callbacks *callbacks) { struct wslay_event_callbacks cb = *callbacks; cb.recv_callback = feed_body_callback; int fd = connect_to(host, service); if(fd == -1) { std::cerr << "Could not connect to the host" << std::endl; return -1; } std::string body; if(http_handshake(fd, host, service, path, body) == -1) { std::cerr << "Failed handshake" << std::endl; close(fd); return -1; } make_non_block(fd); int val = 1; if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)) == -1) { perror("setsockopt: TCP_NODELAY"); return -1; } WebSocketClient ws(fd, &cb, body); if(ws.on_read_event() == -1) { return -1; } cb.recv_callback = callbacks->recv_callback; ws.set_callbacks(&cb); int epollfd = epoll_create(1); if(epollfd == -1) { perror("epoll_create"); return -1; } ctl_epollev(epollfd, EPOLL_CTL_ADD, ws); static const size_t MAX_EVENTS = 1; epoll_event events[MAX_EVENTS]; bool ok = true; while(ws.want_read() || ws.want_write()) { int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if(nfds == -1) { perror("epoll_wait"); return -1; } for(int n = 0; n < nfds; ++n) { if(((events[n].events & EPOLLIN) && ws.on_read_event() != 0) || ((events[n].events & EPOLLOUT) && ws.on_write_event() != 0)) { ok = false; break; } } if(!ok) { break; } ctl_epollev(epollfd, EPOLL_CTL_MOD, ws); } return ok ? 0 : -1; }
/* * 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; }
/* * Fetches the resource denoted by |uri|. */ static void fetch_uri(const struct URI *uri) { spdylay_session_callbacks callbacks; int fd; SSL_CTX *ssl_ctx; SSL *ssl; struct Request req; struct Connection connection; int rv; nfds_t npollfds = 1; struct pollfd pollfds[1]; uint16_t spdy_proto_version; request_init(&req, uri); setup_spdylay_callbacks(&callbacks); /* Establish connection and setup SSL */ fd = connect_to(req.host, req.port); ssl_ctx = SSL_CTX_new(SSLv23_client_method()); if(ssl_ctx == NULL) { dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL)); } init_ssl_ctx(ssl_ctx, &spdy_proto_version); ssl = SSL_new(ssl_ctx); if(ssl == NULL) { dief("SSL_new", ERR_error_string(ERR_get_error(), NULL)); } /* To simplify the program, we perform SSL/TLS handshake in blocking I/O. */ ssl_handshake(ssl, fd); connection.ssl = ssl; connection.want_io = IO_NONE; /* Here make file descriptor non-block */ make_non_block(fd); set_tcp_nodelay(fd); printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version); rv = spdylay_session_client_new(&connection.session, spdy_proto_version, &callbacks, &connection); if(rv != 0) { diec("spdylay_session_client_new", rv); } /* Submit the HTTP request to the outbound queue. */ submit_request(&connection, &req); pollfds[0].fd = fd; ctl_poll(pollfds, &connection); /* Event loop */ while(spdylay_session_want_read(connection.session) || spdylay_session_want_write(connection.session)) { int nfds = poll(pollfds, npollfds, -1); if(nfds == -1) { dief("poll", strerror(errno)); } if(pollfds[0].revents & (POLLIN | POLLOUT)) { exec_io(&connection); } if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) { die("Connection error"); } ctl_poll(pollfds, &connection); } /* Resource cleanup */ spdylay_session_del(connection.session); SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ssl_ctx); shutdown(fd, SHUT_WR); close(fd); request_free(&req); }
/* * Fetches the resource denoted by |uri|. */ static void fetch_uri(const struct URI *uri) { spdylay_session_callbacks callbacks; int fd; struct Request req; struct Connection connection; int rv; nfds_t npollfds = 1; struct pollfd pollfds[1]; uint16_t spdy_proto_version = 3; request_init(&req, uri); setup_spdylay_callbacks(&callbacks); /* Establish connection and setup SSL */ fd = connect_to(req.host, req.port); if (-1 == fd) abort (); connection.fd = fd; connection.want_io = IO_NONE; /* Here make file descriptor non-block */ make_non_block(fd); set_tcp_nodelay(fd); printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version); rv = spdylay_session_client_new(&connection.session, spdy_proto_version, &callbacks, &connection); if(rv != 0) { diec("spdylay_session_client_new", rv); } /* Submit the HTTP request to the outbound queue. */ submit_request(&connection, &req); pollfds[0].fd = fd; ctl_poll(pollfds, &connection); /* Event loop */ while(spdylay_session_want_read(connection.session) || spdylay_session_want_write(connection.session)) { int nfds = poll(pollfds, npollfds, -1); if(nfds == -1) { dief("poll", strerror(errno)); } if(pollfds[0].revents & (POLLIN | POLLOUT)) { exec_io(&connection); } if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) { die("Connection error"); } ctl_poll(pollfds, &connection); } /* Resource cleanup */ spdylay_session_del(connection.session); shutdown(fd, SHUT_WR); MHD_socket_close_(fd); request_free(&req); }