diff --git a/Cargo.lock b/Cargo.lock index cf1455226f2a..72a3aa548327 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2308,6 +2308,7 @@ dependencies = [ "tokio", "tokio-rustls 0.26.0", "tokio-socks", + "tower 0.5.1", "uuid", ] diff --git a/mullvad-api/Cargo.toml b/mullvad-api/Cargo.toml index 7799903cd7e4..ebb456ad6fca 100644 --- a/mullvad-api/Cargo.toml +++ b/mullvad-api/Cargo.toml @@ -23,6 +23,7 @@ http = "1.1.0" hyper = { version = "1.4.1", features = ["client", "http1"] } hyper-util = { workspace = true} http-body-util = "0.1.2" +tower = { workspace = true } ipnetwork = { workspace = true } log = { workspace = true } serde = { workspace = true } diff --git a/mullvad-api/src/abortable_stream.rs b/mullvad-api/src/abortable_stream.rs index 0258a7e650a0..331505de5dbe 100644 --- a/mullvad-api/src/abortable_stream.rs +++ b/mullvad-api/src/abortable_stream.rs @@ -2,7 +2,7 @@ //! immediately instead of after the socket times out. use futures::{channel::oneshot, future::Fuse, FutureExt}; -use hyper::client::connect::{Connected, Connection}; +use hyper_util::client::legacy::connect::{Connected, Connection}; use std::{ future::Future, io, diff --git a/mullvad-api/src/https_client_with_sni.rs b/mullvad-api/src/https_client_with_sni.rs index afd044aa30bd..574d0a63098a 100644 --- a/mullvad-api/src/https_client_with_sni.rs +++ b/mullvad-api/src/https_client_with_sni.rs @@ -8,10 +8,10 @@ use futures::{channel::mpsc, future, pin_mut, StreamExt}; #[cfg(target_os = "android")] use futures::{channel::oneshot, sink::SinkExt}; use http::uri::Scheme; -use hyper::{ - client::connect::dns::{GaiResolver, Name}, - service::Service, - Uri, +use hyper::Uri; +use hyper_util::{ + client::legacy::connect::dns::{GaiResolver, Name}, + rt::TokioIo, }; use shadowsocks::{ config::ServerType, @@ -39,6 +39,7 @@ use tokio::{ net::{TcpSocket, TcpStream}, time::timeout, }; +use tower::Service; #[cfg(feature = "api-override")] use crate::{proxy::ConnectionDecorator, API}; @@ -407,7 +408,7 @@ impl fmt::Debug for HttpsConnectorWithSni { } impl Service for HttpsConnectorWithSni { - type Response = AbortableStream; + type Response = TokioIo>; type Error = io::Error; type Future = Pin> + Send + 'static>>; @@ -472,7 +473,7 @@ impl Service for HttpsConnectorWithSni { inner.stream_handles.push(socket_handle); } - Ok(stream) + Ok(TokioIo::new(stream)) }; Box::pin(fut) diff --git a/mullvad-api/src/proxy.rs b/mullvad-api/src/proxy.rs index 0915d1d23ca6..2fb2c4bf357e 100644 --- a/mullvad-api/src/proxy.rs +++ b/mullvad-api/src/proxy.rs @@ -1,4 +1,4 @@ -use hyper::client::connect::Connected; +use hyper_util::client::legacy::connect::{Connected, Connection}; use serde::{Deserialize, Serialize}; use std::{ fmt, io, @@ -192,7 +192,7 @@ impl ApiConnectionMode { } } -/// Implements `hyper::client::connect::Connection` by wrapping a type. +/// Implements `Connection` by wrapping a type. pub struct ConnectionDecorator(pub T); impl AsyncRead for ConnectionDecorator { @@ -223,26 +223,21 @@ impl AsyncWrite for ConnectionDecorator { } } -impl hyper::client::connect::Connection for ConnectionDecorator { +impl Connection for ConnectionDecorator { fn connected(&self) -> Connected { Connected::new() } } -trait Connection: AsyncRead + AsyncWrite + Unpin + hyper::client::connect::Connection + Send {} +trait ConnectionMullvad: AsyncRead + AsyncWrite + Unpin + Connection + Send {} -impl Connection - for T -{ -} +impl ConnectionMullvad for T {} /// Stream that represents a Mullvad API connection -pub struct ApiConnection(Box); +pub struct ApiConnection(Box); impl ApiConnection { - pub fn new< - T: AsyncRead + AsyncWrite + Unpin + hyper::client::connect::Connection + Send + 'static, - >( + pub fn new( conn: Box, ) -> Self { Self(conn) @@ -277,7 +272,7 @@ impl AsyncWrite for ApiConnection { } } -impl hyper::client::connect::Connection for ApiConnection { +impl Connection for ApiConnection { fn connected(&self) -> Connected { self.0.connected() } diff --git a/mullvad-api/src/rest.rs b/mullvad-api/src/rest.rs index 43a0ecec8e5b..6f2718ca7fe2 100644 --- a/mullvad-api/src/rest.rs +++ b/mullvad-api/src/rest.rs @@ -19,6 +19,7 @@ use hyper::{ Method, Uri, }; +use hyper_util::client::legacy::connect::Connect; use mullvad_types::account::AccountNumber; use std::{ borrow::Cow, @@ -46,6 +47,9 @@ pub enum Error { #[error("Request cancelled")] Aborted, + #[error("Legacy hyper error")] + LegacyHyperError(#[from] Arc), + #[error("Hyper error")] HyperError(#[from] Arc), @@ -87,7 +91,7 @@ impl Error { /// Return true if there was no route to the destination pub fn is_offline(&self) -> bool { match self { - Error::HyperError(error) if error.is_connect() => { + Error::LegacyHyperError(error) if error.is_connect() => { if let Some(cause) = error.source() { if let Some(err) = cause.downcast_ref::() { return err.raw_os_error() == Some(libc::ENETUNREACH); @@ -95,6 +99,7 @@ impl Error { } false } + // TODO: Match on `Error::HyperError` too? _ => false, } } @@ -129,11 +134,7 @@ pub(crate) struct RequestService { command_tx: Weak>, command_rx: mpsc::UnboundedReceiver, connector_handle: HttpsConnectorWithSniHandle, - // client: hyper_util::client::legacy::Client< - // HttpsConnectorWithSni, - // BoxBody, - // >, - client: HttpsConnectorWithSni, + client: hyper_util::client::legacy::Client>, connection_mode_provider: T, connection_mode_generation: usize, api_availability: ApiAvailability, @@ -158,8 +159,9 @@ impl RequestService { connector_handle.set_connection_mode(connection_mode_provider.initial()); let (command_tx, command_rx) = mpsc::unbounded(); - // let client = - // hyper_util::client::legacy::Client::builder(TokioExecutor::new()).build(connector); + let client = + hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()) + .build(connector); let command_tx = Arc::new(command_tx); @@ -167,7 +169,7 @@ impl RequestService { command_tx: Arc::downgrade(&command_tx), command_rx, connector_handle, - client: connector, + client, connection_mode_provider, connection_mode_generation: 0, api_availability, @@ -299,24 +301,24 @@ pub struct Request { } // TODO: merge with `RequestFactory::get` - /// Constructs a GET request with the given URI. Returns an error if the URI is not valid. +/// Constructs a GET request with the given URI. Returns an error if the URI is not valid. pub fn get(uri: &str) -> Result>> { - let uri = hyper::Uri::from_str(uri)?; - - let mut builder = http::request::Builder::new() - .method(Method::GET) - .header(header::USER_AGENT, HeaderValue::from_static(USER_AGENT)) - .header(header::ACCEPT, HeaderValue::from_static("application/json")); - if let Some(host) = uri.host() { - builder = builder.header( - header::HOST, - HeaderValue::from_str(host).map_err(|_e| Error::InvalidHeaderError)?, - ); - }; + let uri = hyper::Uri::from_str(uri)?; + + let mut builder = http::request::Builder::new() + .method(Method::GET) + .header(header::USER_AGENT, HeaderValue::from_static(USER_AGENT)) + .header(header::ACCEPT, HeaderValue::from_static("application/json")); + if let Some(host) = uri.host() { + builder = builder.header( + header::HOST, + HeaderValue::from_str(host).map_err(|_e| Error::InvalidHeaderError)?, + ); + }; - let request = builder.uri(uri).body(Empty::::new())?; - Ok(Request::new(request, None)) - } + let request = builder.uri(uri).body(Empty::::new())?; + Ok(Request::new(request, None)) +} impl Request { fn new(request: hyper::Request, access_token_store: Option) -> Self { @@ -724,6 +726,7 @@ macro_rules! impl_into_arc_err { } impl_into_arc_err!(hyper::Error); +impl_into_arc_err!(hyper_util::client::legacy::Error); impl_into_arc_err!(serde_json::Error); impl_into_arc_err!(http::Error); impl_into_arc_err!(http::uri::InvalidUri); diff --git a/test/Cargo.lock b/test/Cargo.lock index 1ded38758fa3..143cacd383e6 100644 --- a/test/Cargo.lock +++ b/test/Cargo.lock @@ -1867,6 +1867,7 @@ dependencies = [ "tokio", "tokio-rustls", "tokio-socks", + "tower 0.5.1", "uuid", ]