websockets.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. use std::{
  2. net::{TcpStream, ToSocketAddrs},
  3. pin::Pin,
  4. task::{Context, Poll},
  5. };
  6. use async_native_tls::{TlsConnector, TlsStream};
  7. use async_tungstenite::WebSocketStream;
  8. use futures::sink::Sink;
  9. use smol::{prelude::*, Async};
  10. use tungstenite::{handshake::client::Response, Message};
  11. use url::Url;
  12. use crate::{Error, Result as DrkResult};
  13. pub enum WsStream {
  14. Tcp(WebSocketStream<Async<TcpStream>>),
  15. Tls(WebSocketStream<TlsStream<Async<TcpStream>>>),
  16. }
  17. impl Sink<Message> for WsStream {
  18. type Error = tungstenite::Error;
  19. fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
  20. match &mut *self {
  21. WsStream::Tcp(s) => Pin::new(s).poll_ready(cx),
  22. WsStream::Tls(s) => Pin::new(s).poll_ready(cx),
  23. }
  24. }
  25. fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
  26. match &mut *self {
  27. WsStream::Tcp(s) => Pin::new(s).start_send(item),
  28. WsStream::Tls(s) => Pin::new(s).start_send(item),
  29. }
  30. }
  31. fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
  32. match &mut *self {
  33. WsStream::Tcp(s) => Pin::new(s).poll_flush(cx),
  34. WsStream::Tls(s) => Pin::new(s).poll_flush(cx),
  35. }
  36. }
  37. fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
  38. match &mut *self {
  39. WsStream::Tcp(s) => Pin::new(s).poll_close(cx),
  40. WsStream::Tls(s) => Pin::new(s).poll_close(cx),
  41. }
  42. }
  43. }
  44. impl Stream for WsStream {
  45. type Item = tungstenite::Result<Message>;
  46. fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
  47. match &mut *self {
  48. WsStream::Tcp(s) => Pin::new(s).poll_next(cx),
  49. WsStream::Tls(s) => Pin::new(s).poll_next(cx),
  50. }
  51. }
  52. }
  53. /// Connects to a WebSocket address (optionally secured by TLS).
  54. pub async fn connect(addr: &str, tls: TlsConnector) -> DrkResult<(WsStream, Response)> {
  55. let url = Url::parse(addr)?;
  56. let host = url
  57. .host_str()
  58. .ok_or_else(|| Error::UrlParseError(format!("Missing host in {}", url)))?
  59. .to_string();
  60. let port = url
  61. .port_or_known_default()
  62. .ok_or_else(|| Error::UrlParseError(format!("Missing port in {}", url)))?;
  63. let socket_addr = {
  64. let host = host.clone();
  65. smol::unblock(move || (host.as_str(), port).to_socket_addrs())
  66. .await?
  67. .next()
  68. .ok_or(Error::NoUrlFound)?
  69. };
  70. match url.scheme() {
  71. "ws" => {
  72. let stream = Async::<TcpStream>::connect(socket_addr).await?;
  73. let (stream, resp) = async_tungstenite::client_async(addr, stream).await?;
  74. Ok((WsStream::Tcp(stream), resp))
  75. }
  76. "wss" => {
  77. let stream = Async::<TcpStream>::connect(socket_addr).await?;
  78. let stream = tls.connect(host, stream).await?;
  79. let (stream, resp) = async_tungstenite::client_async(addr, stream).await?;
  80. Ok((WsStream::Tls(stream), resp))
  81. }
  82. scheme => {
  83. Err(Error::UrlParseError(format!("Invalid url scheme `{}`, in `{}`", scheme, url)))
  84. }
  85. }
  86. }