Skip to content

Commit

Permalink
Add TransportError type (#219)
Browse files Browse the repository at this point in the history
- add TransportError type
- adjust Transport trait
  • Loading branch information
dyc3 authored Jun 24, 2023
1 parent 2dc6376 commit 7e04a42
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 13 deletions.
22 changes: 20 additions & 2 deletions steamguard/src/transport/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,27 @@ use crate::steamapi::{ApiRequest, ApiResponse, BuildableRequest};

pub trait Transport {
fn send_request<Req: BuildableRequest + MessageFull, Res: MessageFull>(
&mut self,
&self,
req: ApiRequest<Req>,
) -> anyhow::Result<ApiResponse<Res>>;
) -> Result<ApiResponse<Res>, TransportError>;

fn close(&mut self);
}

#[derive(Debug, thiserror::Error)]
pub enum TransportError {
#[error("Transport failed to parse response headers")]
HeaderParseFailure {
header: String,
#[source]
source: anyhow::Error,
},
#[error("Transport failed to parse response body")]
ProtobufError(#[from] protobuf::Error),
#[error("Unauthorized: Access token is missing or invalid")]
Unauthorized,
#[error("NetworkFailure: Transport failed to make request: {0}")]
NetworkFailure(#[from] reqwest::Error),
#[error("Unexpected error when transport was making request: {0}")]
Unknown(#[from] anyhow::Error),
}
42 changes: 31 additions & 11 deletions steamguard/src/transport/webapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use log::{debug, trace};
use protobuf::MessageFull;
use reqwest::{blocking::multipart::Form, Url};

use super::Transport;
use super::{Transport, TransportError};
use crate::steamapi::{ApiRequest, ApiResponse, BuildableRequest, EResult};

lazy_static! {
Expand Down Expand Up @@ -36,9 +36,9 @@ impl WebApiTransport {

impl Transport for WebApiTransport {
fn send_request<Req: BuildableRequest + MessageFull, Res: MessageFull>(
&mut self,
&self,
apireq: ApiRequest<Req>,
) -> anyhow::Result<ApiResponse<Res>> {
) -> anyhow::Result<ApiResponse<Res>, TransportError> {
// All the API endpoints accept 2 data formats: json and protobuf.
// Depending on the http method for the request, the data can go in 2 places:
// - GET: query string, with the key `input_protobuf_encoded` or `input_json`
Expand All @@ -47,7 +47,7 @@ impl Transport for WebApiTransport {
// input protobuf data is always encoded in base64

if Req::requires_access_token() && apireq.access_token().is_none() {
return Err(anyhow::anyhow!("Access token required for this request"));
return Err(TransportError::Unauthorized);
}

let url = apireq.build_url();
Expand Down Expand Up @@ -76,21 +76,42 @@ impl Transport for WebApiTransport {
debug!("Response HTTP status: {}", status);

let eresult = if let Some(eresult) = resp.headers().get("x-eresult") {
debug!("HTTP Header x-eresult: {}", eresult.to_str()?);
eresult.to_str()?.parse::<i32>()?.into()
let s = eresult
.to_str()
.map_err(|err| TransportError::HeaderParseFailure {
header: "x-eresult".to_owned(),
source: err.into(),
})?;
debug!("HTTP Header x-eresult: {}", s);
s.parse::<i32>()
.map_err(|err| TransportError::HeaderParseFailure {
header: "x-eresult".to_owned(),
source: err.into(),
})?
.into()
} else {
EResult::Invalid
};
let error_msg = if let Some(error_message) = resp.headers().get("x-error_message") {
debug!("HTTP Header x-error_message: {}", error_message.to_str()?);
Some(error_message.to_str()?.to_owned())
let s = error_message
.to_str()
.map_err(|err| TransportError::HeaderParseFailure {
header: "x-error_message".to_owned(),
source: err.into(),
})?;
debug!("HTTP Header x-error_message: {}", s);
Some(s.to_owned())
} else {
None
};

let bytes = resp.bytes()?;
if !status.is_success() {
trace!("Response body (raw): {:?}", bytes);

if status == reqwest::StatusCode::UNAUTHORIZED {
return Err(TransportError::Unauthorized);
}
}

let res = decode_msg::<Res>(bytes.as_ref())?;
Expand All @@ -113,9 +134,8 @@ fn encode_msg<T: MessageFull>(msg: &T, config: base64::Config) -> anyhow::Result
Ok(b64)
}

fn decode_msg<T: MessageFull>(bytes: &[u8]) -> anyhow::Result<T> {
let msg = T::parse_from_bytes(bytes)?;
Ok(msg)
fn decode_msg<T: MessageFull>(bytes: &[u8]) -> Result<T, protobuf::Error> {
T::parse_from_bytes(bytes)
}

#[cfg(test)]
Expand Down

0 comments on commit 7e04a42

Please sign in to comment.