websockets.rs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{
  19. net::{TcpStream, ToSocketAddrs},
  20. pin::Pin,
  21. task::{Context, Poll},
  22. };
  23. use async_tungstenite::{
  24. tungstenite::{handshake::client::Response, Message},
  25. WebSocketStream,
  26. };
  27. use futures::sink::Sink;
  28. use futures_rustls::{client::TlsStream, rustls::ServerName, TlsConnector};
  29. use smol::{prelude::*, Async};
  30. use url::Url;
  31. use crate::{Error, Result as DrkResult};
  32. #[allow(clippy::large_enum_variant)]
  33. pub enum WsStream {
  34. Tcp(WebSocketStream<Async<TcpStream>>),
  35. Tls(WebSocketStream<TlsStream<Async<TcpStream>>>),
  36. }
  37. impl Sink<Message> for WsStream {
  38. type Error = async_tungstenite::tungstenite::Error;
  39. fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
  40. match &mut *self {
  41. WsStream::Tcp(s) => Pin::new(s).poll_ready(cx),
  42. WsStream::Tls(s) => Pin::new(s).poll_ready(cx),
  43. }
  44. }
  45. fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
  46. match &mut *self {
  47. WsStream::Tcp(s) => Pin::new(s).start_send(item),
  48. WsStream::Tls(s) => Pin::new(s).start_send(item),
  49. }
  50. }
  51. fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
  52. match &mut *self {
  53. WsStream::Tcp(s) => Pin::new(s).poll_flush(cx),
  54. WsStream::Tls(s) => Pin::new(s).poll_flush(cx),
  55. }
  56. }
  57. fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
  58. match &mut *self {
  59. WsStream::Tcp(s) => Pin::new(s).poll_close(cx),
  60. WsStream::Tls(s) => Pin::new(s).poll_close(cx),
  61. }
  62. }
  63. }
  64. impl Stream for WsStream {
  65. type Item = async_tungstenite::tungstenite::Result<Message>;
  66. fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
  67. match &mut *self {
  68. WsStream::Tcp(s) => Pin::new(s).poll_next(cx),
  69. WsStream::Tls(s) => Pin::new(s).poll_next(cx),
  70. }
  71. }
  72. }
  73. /// Connects to a WebSocket address (optionally secured by TLS).
  74. pub async fn connect(addr: &str, tls: TlsConnector) -> DrkResult<(WsStream, Response)> {
  75. let url = Url::parse(addr)?;
  76. let host = url
  77. .host_str()
  78. .ok_or_else(|| Error::UrlParse(format!("Missing host in {}", url)))?
  79. .to_string();
  80. let port = url
  81. .port_or_known_default()
  82. .ok_or_else(|| Error::UrlParse(format!("Missing port in {}", url)))?;
  83. let socket_addr = {
  84. let host = host.clone();
  85. smol::unblock(move || (host.as_str(), port).to_socket_addrs())
  86. .await?
  87. .next()
  88. .ok_or(Error::NoUrlFound)?
  89. };
  90. match url.scheme() {
  91. "ws" => {
  92. let stream = Async::<TcpStream>::connect(socket_addr).await?;
  93. let (stream, resp) = async_tungstenite::client_async(addr, stream).await?;
  94. Ok((WsStream::Tcp(stream), resp))
  95. }
  96. "wss" => {
  97. let stream = Async::<TcpStream>::connect(socket_addr).await?;
  98. let stream = tls.connect(ServerName::try_from(host.as_str())?, stream).await?;
  99. let (stream, resp) = async_tungstenite::client_async(addr, stream).await?;
  100. Ok((WsStream::Tls(stream), resp))
  101. }
  102. scheme => Err(Error::UrlParse(format!("Invalid url scheme `{}`, in `{}`", scheme, url))),
  103. }
  104. }