steam_auth/
http_client.rs1use std::collections::HashMap;
6
7use crate::error::SessionError;
8
9#[derive(Debug, Clone)]
11pub struct HttpResponse {
12 pub status: u16,
14 pub headers: HashMap<String, String>,
16 pub body: Vec<u8>,
18}
19
20impl HttpResponse {
21 pub fn is_success(&self) -> bool {
23 (200..300).contains(&self.status)
24 }
25
26 pub fn json<T: serde::de::DeserializeOwned>(&self) -> Result<T, SessionError> {
28 serde_json::from_slice(&self.body).map_err(|e| SessionError::NetworkError(format!("JSON parse error: {}", e)))
29 }
30
31 pub fn get_header(&self, name: &str) -> Option<&String> {
33 let lower = name.to_lowercase();
34 self.headers.iter().find(|(k, _)| k.to_lowercase() == lower).map(|(_, v)| v)
35 }
36
37 pub fn get_all_headers(&self, name: &str) -> Vec<&String> {
39 let lower = name.to_lowercase();
40 self.headers.iter().filter(|(k, _)| k.to_lowercase() == lower).map(|(_, v)| v).collect()
41 }
42}
43
44#[derive(Debug, Clone)]
46pub struct FormField {
47 pub name: String,
49 pub value: String,
51}
52
53#[derive(Debug, Clone, Default)]
55pub struct MultipartForm {
56 pub fields: Vec<FormField>,
58}
59
60impl MultipartForm {
61 pub fn new() -> Self {
63 Self::default()
64 }
65
66 pub fn text(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
68 self.fields.push(FormField { name: name.into(), value: value.into() });
69 self
70 }
71}
72
73#[derive(Debug, Clone)]
75pub struct HttpClient {
76 client: reqwest::Client,
77}
78
79impl HttpClient {
80 pub fn new() -> Self {
82 Self { client: reqwest::Client::new() }
83 }
84
85 pub fn with_client(client: reqwest::Client) -> Self {
87 Self { client }
88 }
89
90 pub async fn post_multipart(&self, url: &str, form: MultipartForm, headers: HashMap<String, String>) -> Result<HttpResponse, SessionError> {
92 let mut req_form = reqwest::multipart::Form::new();
93 for field in form.fields {
94 req_form = req_form.text(field.name, field.value);
95 }
96
97 let mut req = self.client.post(url).multipart(req_form);
98
99 for (key, value) in &headers {
100 req = req.header(key, value);
101 }
102
103 let response = req.send().await?;
104 let status = response.status().as_u16();
105
106 let mut response_headers = HashMap::new();
108 for (name, value) in response.headers() {
109 if let Ok(v) = value.to_str() {
110 let key = name.to_string();
113 response_headers.entry(key).and_modify(|existing: &mut String| existing.push_str(&format!(", {}", v))).or_insert_with(|| v.to_string());
114 }
115 }
116
117 let body = response.bytes().await?.to_vec();
118
119 Ok(HttpResponse { status, headers: response_headers, body })
120 }
121
122 pub async fn post_json(&self, url: &str, body: &[u8], headers: HashMap<String, String>) -> Result<HttpResponse, SessionError> {
124 let mut req = self.client.post(url).body(body.to_vec());
125
126 for (key, value) in &headers {
127 req = req.header(key, value);
128 }
129
130 let response = req.send().await?;
131 let status = response.status().as_u16();
132
133 let mut response_headers = HashMap::new();
134 for (name, value) in response.headers() {
135 if let Ok(v) = value.to_str() {
136 response_headers.insert(name.to_string(), v.to_string());
137 }
138 }
139
140 let body = response.bytes().await?.to_vec();
141
142 Ok(HttpResponse { status, headers: response_headers, body })
143 }
144}
145
146impl Default for HttpClient {
147 fn default() -> Self {
148 Self::new()
149 }
150}