add HTTP proxy support (#264)

- allow extracting the http client from web api transport
- refactor most commands so that they can accept an external transport
- update confirmer to use transport's http client
- update remove command
- update TwoFactorClient so it doesn't need to be mutable
- update get_server_time so it requires a transport
- update remove_authenticator
- update login proceedure to use given transport
- update usages of do_login
- update setup
- update trade
- update code command
- update qr-login command
- make borrowcheck happy
- make WebApiTransport Clone and remove Default impl
- remove dead code
- fix lints

closes #177
This commit is contained in:
Carson McManus 2023-07-02 08:57:13 -04:00 committed by GitHub
parent 1d99bae469
commit fe663cf43f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 164 additions and 105 deletions

View file

@ -4,7 +4,7 @@ use clap::{clap_derive::ArgEnum, Parser};
use clap_complete::Shell;
use secrecy::SecretString;
use std::str::FromStr;
use steamguard::SteamGuardAccount;
use steamguard::{transport::Transport, SteamGuardAccount};
use crate::AccountManager;
@ -40,23 +40,33 @@ pub(crate) trait ConstCommand {
}
/// A command that operates the manifest as a whole
pub(crate) trait ManifestCommand {
fn execute(&self, manager: &mut AccountManager) -> anyhow::Result<()>;
pub(crate) trait ManifestCommand<T>
where
T: Transport,
{
fn execute(&self, transport: T, manager: &mut AccountManager) -> anyhow::Result<()>;
}
/// A command that operates on individual accounts.
pub(crate) trait AccountCommand {
pub(crate) trait AccountCommand<T>
where
T: Transport,
{
fn execute(
&self,
transport: T,
manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
) -> anyhow::Result<()>;
}
pub(crate) enum CommandType {
pub(crate) enum CommandType<T>
where
T: Transport,
{
Const(Box<dyn ConstCommand>),
Manifest(Box<dyn ManifestCommand>),
Account(Box<dyn AccountCommand>),
Manifest(Box<dyn ManifestCommand<T>>),
Account(Box<dyn AccountCommand<T>>),
}
#[derive(Debug, Clone, Parser)]
@ -114,6 +124,14 @@ pub(crate) struct GlobalArgs {
long_help = "Disable checking for updates. By default, steamguard-cli will check for updates every now and then. This can be disabled with this flag."
)]
pub no_update_check: bool,
#[clap(
long,
env = "HTTP_PROXY",
help = "Use a proxy for HTTP requests.",
long_help = "Use a proxy for HTTP requests. This is useful if you are behind a firewall and need to use a proxy to access the internet."
)]
pub http_proxy: Option<String>,
}
#[derive(Debug, Clone, Parser)]

View file

@ -20,16 +20,20 @@ pub struct CodeCommand {
pub offline: bool,
}
impl AccountCommand for CodeCommand {
impl<T> AccountCommand<T> for CodeCommand
where
T: Transport,
{
fn execute(
&self,
transport: T,
_manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
) -> anyhow::Result<()> {
let server_time = if self.offline {
SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs()
} else {
steamapi::get_server_time()?.server_time()
steamapi::get_server_time(transport)?.server_time()
};
debug!("Time used to generate codes: {}", server_time);

View file

@ -8,8 +8,11 @@ use super::*;
#[clap(about = "Decrypt all maFiles")]
pub struct DecryptCommand;
impl ManifestCommand for DecryptCommand {
fn execute(&self, manager: &mut AccountManager) -> anyhow::Result<()> {
impl<T> ManifestCommand<T> for DecryptCommand
where
T: Transport,
{
fn execute(&self, _transport: T, manager: &mut AccountManager) -> anyhow::Result<()> {
load_accounts_with_prompts(manager)?;
for mut entry in manager.iter_mut() {
entry.encryption = None;

View file

@ -8,8 +8,11 @@ use super::*;
#[clap(about = "Encrypt all maFiles")]
pub struct EncryptCommand;
impl ManifestCommand for EncryptCommand {
fn execute(&self, manager: &mut AccountManager) -> anyhow::Result<()> {
impl<T> ManifestCommand<T> for EncryptCommand
where
T: Transport,
{
fn execute(&self, _transport: T, manager: &mut AccountManager) -> anyhow::Result<()> {
if !manager.has_passkey() {
let mut passkey;
loop {

View file

@ -18,8 +18,11 @@ pub struct ImportCommand {
pub files: Vec<String>,
}
impl ManifestCommand for ImportCommand {
fn execute(&self, manager: &mut AccountManager) -> anyhow::Result<()> {
impl<T> ManifestCommand<T> for ImportCommand
where
T: Transport,
{
fn execute(&self, _transport: T, manager: &mut AccountManager) -> anyhow::Result<()> {
for file_path in self.files.iter() {
if self.sda {
let path = Path::new(&file_path);

View file

@ -18,9 +18,13 @@ pub struct QrCommand {
pub ascii: bool,
}
impl AccountCommand for QrCommand {
impl<T> AccountCommand<T> for QrCommand
where
T: Transport,
{
fn execute(
&self,
_transport: T,
_manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
) -> anyhow::Result<()> {

View file

@ -1,7 +1,7 @@
use std::sync::{Arc, Mutex};
use log::*;
use steamguard::{transport::WebApiTransport, QrApprover, QrApproverError};
use steamguard::{QrApprover, QrApproverError};
use crate::AccountManager;
@ -17,9 +17,13 @@ pub struct QrLoginCommand {
pub url: String,
}
impl AccountCommand for QrLoginCommand {
impl<T> AccountCommand<T> for QrLoginCommand
where
T: Transport + Clone,
{
fn execute(
&self,
transport: T,
_manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
) -> anyhow::Result<()> {
@ -33,7 +37,7 @@ impl AccountCommand for QrLoginCommand {
info!("Approving login to {}", account.account_name);
if account.tokens.is_none() {
crate::do_login(&mut account)?;
crate::do_login(transport.clone(), &mut account)?;
}
loop {
@ -42,7 +46,7 @@ impl AccountCommand for QrLoginCommand {
return Err(anyhow!("No tokens found for {}", account.account_name));
};
let mut approver = QrApprover::new(WebApiTransport::default(), tokens);
let mut approver = QrApprover::new(transport.clone(), tokens);
match approver.approve(&account, &self.url) {
Ok(_) => {
info!("Login approved.");
@ -50,7 +54,7 @@ impl AccountCommand for QrLoginCommand {
}
Err(QrApproverError::Unauthorized) => {
warn!("tokens are invalid. Attempting to log in again.");
crate::do_login(&mut account)?;
crate::do_login(transport.clone(), &mut account)?;
}
Err(e) => {
error!("Failed to approve login: {}", e);

View file

@ -1,7 +1,7 @@
use std::sync::{Arc, Mutex};
use log::*;
use steamguard::{transport::TransportError, RemoveAuthenticatorError};
use steamguard::{steamapi::TwoFactorClient, transport::TransportError, RemoveAuthenticatorError};
use crate::{errors::UserError, tui, AccountManager};
@ -11,9 +11,13 @@ use super::*;
#[clap(about = "Remove the authenticator from an account.")]
pub struct RemoveCommand;
impl AccountCommand for RemoveCommand {
impl<T> AccountCommand<T> for RemoveCommand
where
T: Transport + Clone,
{
fn execute(
&self,
transport: T,
manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
) -> anyhow::Result<()> {
@ -38,10 +42,11 @@ impl AccountCommand for RemoveCommand {
let mut successful = vec![];
for a in accounts {
let mut account = a.lock().unwrap();
let client = TwoFactorClient::new(transport.clone());
let mut revocation: Option<String> = None;
loop {
match account.remove_authenticator(revocation.as_ref()) {
match account.remove_authenticator(&client, revocation.as_ref()) {
Ok(_) => {
info!("Removed authenticator from {}", account.account_name);
successful.push(account.account_name.clone());
@ -49,7 +54,7 @@ impl AccountCommand for RemoveCommand {
}
Err(RemoveAuthenticatorError::TransportError(TransportError::Unauthorized)) => {
error!("Account {} is not logged in", account.account_name);
crate::do_login(&mut account)?;
crate::do_login(transport.clone(), &mut account)?;
continue;
}
Err(RemoveAuthenticatorError::IncorrectRevocationCode {

View file

@ -3,7 +3,7 @@ use phonenumber::PhoneNumber;
use secrecy::ExposeSecret;
use steamguard::{
accountlinker::AccountLinkSuccess, phonelinker::PhoneLinker, steamapi::PhoneClient,
token::Tokens, transport::WebApiTransport, AccountLinkError, AccountLinker, FinalizeLinkError,
token::Tokens, AccountLinkError, AccountLinker, FinalizeLinkError,
};
use crate::{tui, AccountManager};
@ -14,8 +14,11 @@ use super::*;
#[clap(about = "Set up a new account with steamguard-cli")]
pub struct SetupCommand;
impl ManifestCommand for SetupCommand {
fn execute(&self, manager: &mut AccountManager) -> anyhow::Result<()> {
impl<T> ManifestCommand<T> for SetupCommand
where
T: Transport + Clone,
{
fn execute(&self, transport: T, manager: &mut AccountManager) -> anyhow::Result<()> {
eprintln!("Log in to the account that you want to link to steamguard-cli");
eprint!("Username: ");
let username = tui::prompt().to_lowercase();
@ -27,11 +30,11 @@ impl ManifestCommand for SetupCommand {
);
}
info!("Logging in to {}", username);
let tokens =
crate::do_login_raw(username).expect("Failed to log in. Account has not been linked.");
let tokens = crate::do_login_raw(transport.clone(), username)
.expect("Failed to log in. Account has not been linked.");
info!("Adding authenticator...");
let mut linker = AccountLinker::new(WebApiTransport::default(), tokens);
let mut linker = AccountLinker::new(transport.clone(), tokens);
let link: AccountLinkSuccess;
loop {
match linker.link() {
@ -41,7 +44,7 @@ impl ManifestCommand for SetupCommand {
}
Err(AccountLinkError::MustProvidePhoneNumber) => {
eprintln!("Looks like you don't have a phone number on this account.");
do_add_phone_number(linker.tokens())?;
do_add_phone_number(transport.clone(), linker.tokens())?;
}
Err(AccountLinkError::MustConfirmEmail) => {
println!("Check your email and click the link.");
@ -129,8 +132,8 @@ impl ManifestCommand for SetupCommand {
}
}
pub fn do_add_phone_number(tokens: &Tokens) -> anyhow::Result<()> {
let client = PhoneClient::new(WebApiTransport::default());
pub fn do_add_phone_number<T: Transport>(transport: T, tokens: &Tokens) -> anyhow::Result<()> {
let client = PhoneClient::new(transport);
let linker = PhoneLinker::new(client, tokens.clone());

View file

@ -25,9 +25,13 @@ pub struct TradeCommand {
pub fail_fast: bool,
}
impl AccountCommand for TradeCommand {
impl<T> AccountCommand<T> for TradeCommand
where
T: Transport + Clone,
{
fn execute(
&self,
transport: T,
manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
) -> anyhow::Result<()> {
@ -36,13 +40,13 @@ impl AccountCommand for TradeCommand {
if !account.is_logged_in() {
info!("Account does not have tokens, logging in");
crate::do_login(&mut account)?;
crate::do_login(transport.clone(), &mut account)?;
}
info!("{}: Checking for trade confirmations", account.account_name);
let confirmations: Vec<Confirmation>;
loop {
let confirmer = Confirmer::new(&account);
let confirmer = Confirmer::new(transport.clone(), &account);
match confirmer.get_trade_confirmations() {
Ok(confs) => {
@ -51,7 +55,7 @@ impl AccountCommand for TradeCommand {
}
Err(ConfirmerError::InvalidTokens) => {
info!("obtaining new tokens");
crate::do_login(&mut account)?;
crate::do_login(transport.clone(), &mut account)?;
}
Err(err) => {
error!("Failed to get trade confirmations: {}", err);
@ -65,7 +69,7 @@ impl AccountCommand for TradeCommand {
continue;
}
let confirmer = Confirmer::new(&account);
let confirmer = Confirmer::new(transport.clone(), &account);
let mut any_failed = false;
if self.accept_all {
info!("accepting all confirmations");

View file

@ -10,7 +10,7 @@ use std::{
use steamguard::{
protobufs::steammessages_auth_steamclient::{EAuthSessionGuardType, EAuthTokenPlatformType},
refresher::TokenRefresher,
transport::WebApiTransport,
transport::{Transport, WebApiTransport},
};
use steamguard::{steamapi, DeviceDetails, LoginError, SteamGuardAccount, UserLogin};
use steamguard::{steamapi::AuthenticationClient, token::Tokens};
@ -83,7 +83,7 @@ fn main() {
fn run(args: commands::Args) -> anyhow::Result<()> {
let globalargs = args.global;
let cmd: CommandType = match args.sub.unwrap_or(Subcommands::Code(args.code)) {
let cmd: CommandType<WebApiTransport> = match args.sub.unwrap_or(Subcommands::Code(args.code)) {
Subcommands::Debug(args) => CommandType::Const(Box::new(args)),
Subcommands::Completion(args) => CommandType::Const(Box::new(args)),
Subcommands::Setup(args) => CommandType::Manifest(Box::new(args)),
@ -198,8 +198,16 @@ fn run(args: commands::Args) -> anyhow::Result<()> {
}
}
let mut http_client = reqwest::blocking::Client::builder();
if let Some(proxy) = &globalargs.http_proxy {
let proxy = reqwest::Proxy::all(proxy)?;
http_client = http_client.proxy(proxy);
}
let http_client = http_client.build()?;
let transport = WebApiTransport::new(http_client);
if let CommandType::Manifest(cmd) = cmd {
cmd.execute(&mut manager)?;
cmd.execute(transport, &mut manager)?;
return Ok(());
}
@ -237,7 +245,7 @@ fn run(args: commands::Args) -> anyhow::Result<()> {
);
if let CommandType::Account(cmd) = cmd {
return cmd.execute(&mut manager, selected_accounts);
return cmd.execute(transport, &mut manager, selected_accounts);
}
Ok(())
@ -271,10 +279,13 @@ fn get_selected_accounts(
Ok(selected_accounts)
}
fn do_login(account: &mut SteamGuardAccount) -> anyhow::Result<()> {
fn do_login<T: Transport + Clone>(
transport: T,
account: &mut SteamGuardAccount,
) -> anyhow::Result<()> {
if let Some(tokens) = account.tokens.as_mut() {
info!("Refreshing access token...");
let client = AuthenticationClient::new(WebApiTransport::default());
let client = AuthenticationClient::new(transport.clone());
let mut refresher = TokenRefresher::new(client);
match refresher.refresh(account.steam_id, tokens) {
Ok(token) => {
@ -304,14 +315,19 @@ fn do_login(account: &mut SteamGuardAccount) -> anyhow::Result<()> {
} else {
debug!("password is empty");
}
let tokens = do_login_impl(account.account_name.clone(), password, Some(account))?;
let tokens = do_login_impl(
transport,
account.account_name.clone(),
password,
Some(account),
)?;
let steam_id = tokens.access_token().decode()?.steam_id();
account.set_tokens(tokens);
account.steam_id = steam_id;
Ok(())
}
fn do_login_raw(username: String) -> anyhow::Result<Tokens> {
fn do_login_raw<T: Transport + Clone>(transport: T, username: String) -> anyhow::Result<Tokens> {
let _ = std::io::stdout().flush();
let password = rpassword::prompt_password_stdout("Password: ").unwrap();
if !password.is_empty() {
@ -319,15 +335,16 @@ fn do_login_raw(username: String) -> anyhow::Result<Tokens> {
} else {
debug!("password is empty");
}
do_login_impl(username, password, None)
do_login_impl(transport, username, password, None)
}
fn do_login_impl(
fn do_login_impl<T: Transport + Clone>(
transport: T,
username: String,
password: String,
account: Option<&SteamGuardAccount>,
) -> anyhow::Result<Tokens> {
let mut login = UserLogin::new(WebApiTransport::default(), build_device_details());
let mut login = UserLogin::new(transport.clone(), build_device_details());
let mut password = password;
let confirmation_methods;
@ -375,7 +392,7 @@ fn do_login_impl(
EAuthSessionGuardType::k_EAuthSessionGuardType_DeviceCode => {
let code = if let Some(account) = account {
debug!("Generating 2fa code...");
let time = steamapi::get_server_time()?.server_time();
let time = steamapi::get_server_time(transport)?.server_time();
account.generate_code(time)
} else {
eprint!("Enter the 2fa code from your device: ");

View file

@ -10,20 +10,30 @@ use reqwest::{
use secrecy::ExposeSecret;
use serde::Deserialize;
use crate::{steamapi, SteamGuardAccount};
use crate::{
steamapi::{self},
transport::Transport,
SteamGuardAccount,
};
lazy_static! {
static ref STEAM_COOKIE_URL: Url = "https://steamcommunity.com".parse::<Url>().unwrap();
}
/// Provides an interface that wraps the Steam mobile confirmation API.
pub struct Confirmer<'a> {
///
/// Only compatible with WebApiTransport.
pub struct Confirmer<'a, T> {
account: &'a SteamGuardAccount,
transport: T,
}
impl<'a> Confirmer<'a> {
pub fn new(account: &'a SteamGuardAccount) -> Self {
Self { account }
impl<'a, T> Confirmer<'a, T>
where
T: Transport + Clone,
{
pub fn new(transport: T, account: &'a SteamGuardAccount) -> Self {
Self { account, transport }
}
fn get_confirmation_query_params<'q>(
@ -72,11 +82,9 @@ impl<'a> Confirmer<'a> {
pub fn get_trade_confirmations(&self) -> Result<Vec<Confirmation>, ConfirmerError> {
let cookies = self.build_cookie_jar();
let client = reqwest::blocking::ClientBuilder::new()
.cookie_store(true)
.build()?;
let client = self.transport.innner_http_client()?;
let time = steamapi::get_server_time()?.server_time();
let time = steamapi::get_server_time(self.transport.clone())?.server_time();
let resp = client
.get(
"https://steamcommunity.com/mobileconf/getlist"
@ -117,11 +125,9 @@ impl<'a> Confirmer<'a> {
let operation = action.to_operation();
let cookies = self.build_cookie_jar();
let client = reqwest::blocking::ClientBuilder::new()
.cookie_store(true)
.build()?;
let client = self.transport.innner_http_client()?;
let time = steamapi::get_server_time()?.server_time();
let time = steamapi::get_server_time(self.transport.clone())?.server_time();
let mut query_params = self.get_confirmation_query_params("conf", time);
query_params.push(("op", operation.into()));
query_params.push(("cid", Cow::Borrowed(&conf.id)));
@ -185,11 +191,9 @@ impl<'a> Confirmer<'a> {
let operation = action.to_operation();
let cookies = self.build_cookie_jar();
let client = reqwest::blocking::ClientBuilder::new()
.cookie_store(true)
.build()?;
let client = self.transport.innner_http_client()?;
let time = steamapi::get_server_time()?.server_time();
let time = steamapi::get_server_time(self.transport.clone())?.server_time();
let mut query_params = self.get_confirmation_query_params("conf", time);
query_params.push(("op", operation.into()));
for conf in confs.iter() {
@ -262,11 +266,9 @@ impl<'a> Confirmer<'a> {
}
let cookies = self.build_cookie_jar();
let client = reqwest::blocking::ClientBuilder::new()
.cookie_store(true)
.build()?;
let client = self.transport.innner_http_client()?;
let time = steamapi::get_server_time()?.server_time();
let time = steamapi::get_server_time(self.transport.clone())?.server_time();
let query_params = self.get_confirmation_query_params("details", time);
let resp = client

View file

@ -1,8 +1,6 @@
use crate::protobufs::service_twofactor::CTwoFactor_RemoveAuthenticator_Request;
use crate::steamapi::EResult;
use crate::{
steamapi::twofactor::TwoFactorClient, token::TwoFactorSecret, transport::WebApiTransport,
};
use crate::{steamapi::twofactor::TwoFactorClient, token::TwoFactorSecret};
pub use accountlinker::{AccountLinkError, AccountLinker, FinalizeLinkError};
pub use confirmation::*;
pub use qrapprover::{QrApprover, QrApproverError};
@ -10,7 +8,7 @@ pub use secrecy::{ExposeSecret, SecretString};
use serde::{Deserialize, Serialize};
use std::io::Read;
use token::Tokens;
use transport::TransportError;
use transport::{Transport, TransportError};
pub use userlogin::{DeviceDetails, LoginError, UserLogin};
#[macro_use]
@ -99,8 +97,9 @@ impl SteamGuardAccount {
/// Removes the mobile authenticator from the steam account. If this operation succeeds, this object can no longer be considered valid.
/// Returns whether or not the operation was successful.
pub fn remove_authenticator(
pub fn remove_authenticator<T: Transport>(
&self,
client: &TwoFactorClient<T>,
revocation_code: Option<&String>,
) -> Result<(), RemoveAuthenticatorError> {
if !matches!(revocation_code, Some(_)) && self.revocation_code.expose_secret().is_empty() {
@ -109,7 +108,6 @@ impl SteamGuardAccount {
let Some(tokens) = &self.tokens else {
return Err(RemoveAuthenticatorError::TransportError(TransportError::Unauthorized));
};
let mut client = TwoFactorClient::new(WebApiTransport::default());
let mut req = CTwoFactor_RemoveAuthenticator_Request::new();
req.set_revocation_code(
revocation_code

View file

@ -2,9 +2,8 @@ pub mod authentication;
pub mod phone;
pub mod twofactor;
use crate::{
protobufs::service_twofactor::CTwoFactor_Time_Response, token::Jwt, transport::WebApiTransport,
};
use crate::transport::Transport;
use crate::{protobufs::service_twofactor::CTwoFactor_Time_Response, token::Jwt};
use reqwest::Url;
use serde::Deserialize;
@ -20,8 +19,8 @@ lazy_static! {
/// Queries Steam for the current time. A convenience function around TwoFactorClient.
///
/// Endpoint: `/ITwoFactorService/QueryTime/v0001`
pub fn get_server_time() -> anyhow::Result<CTwoFactor_Time_Response> {
let mut client = TwoFactorClient::new(WebApiTransport::default());
pub fn get_server_time<T: Transport>(client: T) -> anyhow::Result<CTwoFactor_Time_Response> {
let client = TwoFactorClient::new(client);
let resp = client.query_time()?;
if resp.result != EResult::OK {
return Err(anyhow::anyhow!("QueryTime failed: {:?}", resp));

View file

@ -26,7 +26,7 @@ where
}
pub fn add_authenticator(
&mut self,
&self,
req: CTwoFactor_AddAuthenticator_Request,
access_token: &Jwt,
) -> anyhow::Result<ApiResponse<CTwoFactor_AddAuthenticator_Response>> {
@ -41,7 +41,7 @@ where
}
pub fn finalize_authenticator(
&mut self,
&self,
req: CTwoFactor_FinalizeAddAuthenticator_Request,
access_token: &Jwt,
) -> anyhow::Result<ApiResponse<CTwoFactor_FinalizeAddAuthenticator_Response>> {
@ -56,7 +56,7 @@ where
}
pub fn remove_authenticator(
&mut self,
&self,
req: CTwoFactor_RemoveAuthenticator_Request,
access_token: &Jwt,
) -> Result<ApiResponse<CTwoFactor_RemoveAuthenticator_Response>, TransportError> {
@ -71,7 +71,7 @@ where
}
pub fn query_status(
&mut self,
&self,
req: CTwoFactor_Status_Request,
access_token: &Jwt,
) -> anyhow::Result<ApiResponse<CTwoFactor_Status_Response>> {
@ -83,7 +83,7 @@ where
Ok(resp)
}
pub fn query_time(&mut self) -> anyhow::Result<ApiResponse<CTwoFactor_Time_Response>> {
pub fn query_time(&self) -> anyhow::Result<ApiResponse<CTwoFactor_Time_Response>> {
let req = ApiRequest::new(SERVICE_NAME, "QueryTime", 1, CTwoFactor_Time_Request::new());
let resp = self
.transport

View file

@ -12,6 +12,10 @@ pub trait Transport {
) -> Result<ApiResponse<Res>, TransportError>;
fn close(&mut self);
fn innner_http_client(&self) -> anyhow::Result<reqwest::blocking::Client> {
bail!("Transport does not support extracting HTTP client")
}
}
#[derive(Debug, thiserror::Error)]

View file

@ -10,31 +10,15 @@ lazy_static! {
static ref STEAM_API_BASE: String = "https://api.steampowered.com".into();
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct WebApiTransport {
client: reqwest::blocking::Client,
}
impl Default for WebApiTransport {
fn default() -> Self {
Self::new(reqwest::blocking::Client::new())
}
}
impl WebApiTransport {
pub fn new(client: reqwest::blocking::Client) -> Self {
Self { client }
}
// pub fn new_with_proxy(proxy: &str) -> Self {
// Self {
// client: reqwest::blocking::Client::builder()
// // .danger_accept_invalid_certs(true)
// .proxy(reqwest::Proxy::all(proxy).unwrap())
// .build()
// .unwrap(),
// }
// }
}
impl Transport for WebApiTransport {
@ -129,6 +113,10 @@ impl Transport for WebApiTransport {
}
fn close(&mut self) {}
fn innner_http_client(&self) -> anyhow::Result<reqwest::blocking::Client> {
Ok(self.client.clone())
}
}
fn encode_msg<T: MessageFull>(msg: &T, config: base64::Config) -> anyhow::Result<String> {