use std::future::Future;
use std::sync::Arc;
use async_trait::async_trait;
use crate::Result;
pub use crate::rpc::{
NetworkAuthenticateBasicRequest, NetworkAuthenticateBasicResult,
NetworkAuthenticateNegotiateRequest, NetworkResolveProxyRequest, NetworkScope,
};
pub(crate) const RESOLVE_PROXY_METHOD: &str = "network.resolveProxy";
pub(crate) const AUTHENTICATE_NEGOTIATE_METHOD: &str = "network.authenticateNegotiate";
pub(crate) const AUTHENTICATE_BASIC_METHOD: &str = "network.authenticateBasic";
#[async_trait]
pub trait ResolveProxyProvider: Send + Sync + 'static {
async fn resolve_proxy(&self, request: NetworkResolveProxyRequest) -> Result<Option<String>>;
}
#[async_trait]
impl<F, Fut> ResolveProxyProvider for F
where
F: Fn(NetworkResolveProxyRequest) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Option<String>>> + Send,
{
async fn resolve_proxy(&self, request: NetworkResolveProxyRequest) -> Result<Option<String>> {
(self)(request).await
}
}
#[async_trait]
pub trait AuthenticateNegotiateProvider: Send + Sync + 'static {
async fn authenticate_negotiate(
&self,
request: NetworkAuthenticateNegotiateRequest,
) -> Result<Option<String>>;
}
#[async_trait]
impl<F, Fut> AuthenticateNegotiateProvider for F
where
F: Fn(NetworkAuthenticateNegotiateRequest) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Option<String>>> + Send,
{
async fn authenticate_negotiate(
&self,
request: NetworkAuthenticateNegotiateRequest,
) -> Result<Option<String>> {
(self)(request).await
}
}
#[async_trait]
pub trait AuthenticateBasicProvider: Send + Sync + 'static {
async fn authenticate_basic(
&self,
request: NetworkAuthenticateBasicRequest,
) -> Result<Option<NetworkAuthenticateBasicResult>>;
}
#[async_trait]
impl<F, Fut> AuthenticateBasicProvider for F
where
F: Fn(NetworkAuthenticateBasicRequest) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Option<NetworkAuthenticateBasicResult>>> + Send,
{
async fn authenticate_basic(
&self,
request: NetworkAuthenticateBasicRequest,
) -> Result<Option<NetworkAuthenticateBasicResult>> {
(self)(request).await
}
}
#[non_exhaustive]
#[derive(Clone, Default)]
pub struct NetworkConfig {
pub scope: Option<NetworkScope>,
pub resolve_proxy: Option<Arc<dyn ResolveProxyProvider>>,
pub authenticate_negotiate: Option<Arc<dyn AuthenticateNegotiateProvider>>,
pub authenticate_basic: Option<Arc<dyn AuthenticateBasicProvider>>,
}
impl NetworkConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_scope(mut self, scope: NetworkScope) -> Self {
self.scope = Some(scope);
self
}
pub fn with_resolve_proxy<P>(mut self, provider: P) -> Self
where
P: ResolveProxyProvider,
{
self.resolve_proxy = Some(Arc::new(provider));
self
}
pub fn with_authenticate_negotiate<P>(mut self, provider: P) -> Self
where
P: AuthenticateNegotiateProvider,
{
self.authenticate_negotiate = Some(Arc::new(provider));
self
}
pub fn with_authenticate_basic<P>(mut self, provider: P) -> Self
where
P: AuthenticateBasicProvider,
{
self.authenticate_basic = Some(Arc::new(provider));
self
}
}
impl std::fmt::Debug for NetworkConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NetworkConfig")
.field("scope", &self.scope)
.field(
"resolve_proxy",
&self.resolve_proxy.as_ref().map(|_| "<set>"),
)
.field(
"authenticate_negotiate",
&self.authenticate_negotiate.as_ref().map(|_| "<set>"),
)
.field(
"authenticate_basic",
&self.authenticate_basic.as_ref().map(|_| "<set>"),
)
.finish()
}
}
pub(crate) async fn register(client: &crate::Client, config: NetworkConfig) -> Result<()> {
let NetworkConfig {
scope,
resolve_proxy,
authenticate_negotiate,
authenticate_basic,
} = config;
if resolve_proxy.is_none() && authenticate_negotiate.is_none() && authenticate_basic.is_none() {
return Ok(());
}
let mut request = crate::rpc::NetworkSetProviderOptions::new();
if let Some(scope) = scope {
request = request.scope(scope);
}
if resolve_proxy.is_some() {
request = request.resolve_proxy(Default::default());
}
if authenticate_negotiate.is_some() {
request = request.authenticate_negotiate(Default::default());
}
if authenticate_basic.is_some() {
request = request.authenticate_basic(Default::default());
}
let mut installed = Vec::new();
let submitted = install(
client,
resolve_proxy,
authenticate_negotiate,
authenticate_basic,
&mut installed,
)
.and_then(|()| {
client.begin_call(
crate::generated::api_types::rpc_methods::NETWORK_SETPROVIDER,
Some(serde_json::to_value(request)?),
)
});
let response = match submitted {
Ok(response) => response,
Err(error) => {
uninstall(&client.inner, &installed);
return Err(error);
}
};
let owner = Arc::downgrade(&client.inner);
tokio::spawn(async move {
let result = response.await.map(drop);
if result.is_err()
&& let Some(inner) = owner.upgrade()
{
uninstall(&inner, &installed);
}
result
})
.await
.unwrap_or_else(|error| {
Err(crate::Error::with_message(
crate::ErrorKind::Protocol(crate::ProtocolErrorKind::RequestCancelled),
format!("Network registration did not complete: {error}"),
))
})
}
fn install(
client: &crate::Client,
resolve_proxy: Option<Arc<dyn ResolveProxyProvider>>,
authenticate_negotiate: Option<Arc<dyn AuthenticateNegotiateProvider>>,
authenticate_basic: Option<Arc<dyn AuthenticateBasicProvider>>,
installed: &mut Vec<&'static str>,
) -> Result<()> {
if let Some(provider) = resolve_proxy {
client.register_request_handler(RESOLVE_PROXY_METHOD, move |params, _client| {
let provider = provider.clone();
async move {
let proxy = provider
.resolve_proxy(serde_json::from_value(params)?)
.await?;
Ok(serde_json::to_value(
crate::rpc::NetworkResolveProxyResult { proxy },
)?)
}
})?;
installed.push(RESOLVE_PROXY_METHOD);
}
if let Some(provider) = authenticate_negotiate {
client.register_request_handler(
AUTHENTICATE_NEGOTIATE_METHOD,
move |params, _client| {
let provider = provider.clone();
async move {
let token = provider
.authenticate_negotiate(serde_json::from_value(params)?)
.await?;
Ok(serde_json::to_value(
crate::rpc::NetworkAuthenticateNegotiateResult { token },
)?)
}
},
)?;
installed.push(AUTHENTICATE_NEGOTIATE_METHOD);
}
if let Some(provider) = authenticate_basic {
client.register_request_handler(AUTHENTICATE_BASIC_METHOD, move |params, _client| {
let provider = provider.clone();
async move {
let credentials = provider
.authenticate_basic(serde_json::from_value(params)?)
.await?;
Ok(serde_json::to_value(credentials.unwrap_or_default())?)
}
})?;
installed.push(AUTHENTICATE_BASIC_METHOD);
}
Ok(())
}
fn uninstall(inner: &crate::ClientInner, installed: &[&'static str]) {
for method in installed {
inner.rpc.unregister_request_handler(method);
}
}