Skip to content

Commit fef4a77

Browse files
Expose error message for Async client
1 parent 8d974ff commit fef4a77

File tree

2 files changed

+151
-48
lines changed

2 files changed

+151
-48
lines changed

.github/workflows/cont_integration.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,12 @@ jobs:
5050
run: rustup update
5151
- name: pin dependencies
5252
if: matrix.rust.version == '1.57.0'
53-
run: cargo update -p log --precise 0.4.18 && cargo update -p rustls:0.21.2 --precise 0.21.1 && cargo update -p time:0.3.15 --precise 0.3.13
53+
run: |
54+
cargo update -p log --precise 0.4.18 &&
55+
cargo update -p rustls:0.21.5 --precise 0.21.1 &&
56+
cargo update -p time:0.3.15 --precise 0.3.13 &&
57+
cargo update -p hyper-rustls:0.24.1 --precise 0.24.0 &&
58+
cargo update -p tempfile:3.7.0 --precise 3.6.0
5459
- name: Build
5560
run: cargo build --features ${{ matrix.features }} --no-default-features
5661
- name: Clippy

src/async.rs

Lines changed: 145 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ use std::str::FromStr;
1717
use bitcoin::consensus::{deserialize, serialize};
1818
use bitcoin::hashes::hex::FromHex;
1919
use bitcoin::hashes::{sha256, Hash};
20-
use bitcoin::{Block, BlockHash, block::Header as BlockHeader, MerkleBlock, Script, Transaction, Txid};
20+
use bitcoin::{
21+
block::Header as BlockHeader, Block, BlockHash, MerkleBlock, Script, Transaction, Txid,
22+
};
2123
use bitcoin_internals::hex::display::DisplayHex;
2224

2325
#[allow(unused_imports)]
@@ -68,7 +70,14 @@ impl AsyncClient {
6870
return Ok(None);
6971
}
7072

71-
Ok(Some(deserialize(&resp.error_for_status()?.bytes().await?)?))
73+
if resp.status().is_server_error() || resp.status().is_client_error() {
74+
Err(Error::HttpResponse {
75+
status: resp.status().as_u16(),
76+
message: resp.text().await?,
77+
})
78+
} else {
79+
Ok(Some(deserialize(&resp.bytes().await?)?))
80+
}
7281
}
7382

7483
/// Get a [`Transaction`] given its [`Txid`].
@@ -96,7 +105,14 @@ impl AsyncClient {
96105
return Ok(None);
97106
}
98107

99-
Ok(Some(Txid::from_str(&resp.text().await?)?))
108+
if resp.status().is_server_error() || resp.status().is_client_error() {
109+
Err(Error::HttpResponse {
110+
status: resp.status().as_u16(),
111+
message: resp.text().await?,
112+
})
113+
} else {
114+
Ok(Some(Txid::from_str(&resp.text().await?)?))
115+
}
100116
}
101117

102118
/// Get the status of a [`Transaction`] given its [`Txid`].
@@ -106,8 +122,14 @@ impl AsyncClient {
106122
.get(&format!("{}/tx/{}/status", self.url, txid))
107123
.send()
108124
.await?;
109-
110-
Ok(resp.error_for_status()?.json().await?)
125+
if resp.status().is_server_error() || resp.status().is_client_error() {
126+
Err(Error::HttpResponse {
127+
status: resp.status().as_u16(),
128+
message: resp.text().await?,
129+
})
130+
} else {
131+
Ok(resp.json().await?)
132+
}
111133
}
112134

113135
#[deprecated(
@@ -128,9 +150,15 @@ impl AsyncClient {
128150
.send()
129151
.await?;
130152

131-
let header = deserialize(&Vec::from_hex(&resp.text().await?)?)?;
132-
133-
Ok(header)
153+
if resp.status().is_server_error() || resp.status().is_client_error() {
154+
Err(Error::HttpResponse {
155+
status: resp.status().as_u16(),
156+
message: resp.text().await?,
157+
})
158+
} else {
159+
let header = deserialize(&Vec::from_hex(&resp.text().await?)?)?;
160+
Ok(header)
161+
}
134162
}
135163

136164
/// Get the [`BlockStatus`] given a particular [`BlockHash`].
@@ -141,7 +169,14 @@ impl AsyncClient {
141169
.send()
142170
.await?;
143171

144-
Ok(resp.error_for_status()?.json().await?)
172+
if resp.status().is_server_error() || resp.status().is_client_error() {
173+
Err(Error::HttpResponse {
174+
status: resp.status().as_u16(),
175+
message: resp.text().await?,
176+
})
177+
} else {
178+
Ok(resp.json().await?)
179+
}
145180
}
146181

147182
/// Get a [`Block`] given a particular [`BlockHash`].
@@ -155,7 +190,15 @@ impl AsyncClient {
155190
if let StatusCode::NOT_FOUND = resp.status() {
156191
return Ok(None);
157192
}
158-
Ok(Some(deserialize(&resp.error_for_status()?.bytes().await?)?))
193+
194+
if resp.status().is_server_error() || resp.status().is_client_error() {
195+
Err(Error::HttpResponse {
196+
status: resp.status().as_u16(),
197+
message: resp.text().await?,
198+
})
199+
} else {
200+
Ok(Some(deserialize(&resp.bytes().await?)?))
201+
}
159202
}
160203

161204
/// Get a merkle inclusion proof for a [`Transaction`] with the given [`Txid`].
@@ -170,7 +213,14 @@ impl AsyncClient {
170213
return Ok(None);
171214
}
172215

173-
Ok(Some(resp.error_for_status()?.json().await?))
216+
if resp.status().is_server_error() || resp.status().is_client_error() {
217+
Err(Error::HttpResponse {
218+
status: resp.status().as_u16(),
219+
message: resp.text().await?,
220+
})
221+
} else {
222+
Ok(Some(resp.json().await?))
223+
}
174224
}
175225

176226
/// Get a [`MerkleBlock`] inclusion proof for a [`Transaction`] with the given [`Txid`].
@@ -185,9 +235,15 @@ impl AsyncClient {
185235
return Ok(None);
186236
}
187237

188-
let merkle_block = deserialize(&Vec::from_hex(&resp.text().await?)?)?;
189-
190-
Ok(Some(merkle_block))
238+
if resp.status().is_server_error() || resp.status().is_client_error() {
239+
Err(Error::HttpResponse {
240+
status: resp.status().as_u16(),
241+
message: resp.text().await?,
242+
})
243+
} else {
244+
let merkle_block = deserialize(&Vec::from_hex(&resp.text().await?)?)?;
245+
Ok(Some(merkle_block))
246+
}
191247
}
192248

193249
/// Get the spending status of an output given a [`Txid`] and the output index.
@@ -206,19 +262,33 @@ impl AsyncClient {
206262
return Ok(None);
207263
}
208264

209-
Ok(Some(resp.error_for_status()?.json().await?))
265+
if resp.status().is_server_error() || resp.status().is_client_error() {
266+
Err(Error::HttpResponse {
267+
status: resp.status().as_u16(),
268+
message: resp.text().await?,
269+
})
270+
} else {
271+
Ok(Some(resp.json().await?))
272+
}
210273
}
211274

212275
/// Broadcast a [`Transaction`] to Esplora
213276
pub async fn broadcast(&self, transaction: &Transaction) -> Result<(), Error> {
214-
self.client
277+
let resp = self
278+
.client
215279
.post(&format!("{}/tx", self.url))
216280
.body(serialize(transaction).to_lower_hex_string())
217281
.send()
218-
.await?
219-
.error_for_status()?;
282+
.await?;
220283

221-
Ok(())
284+
if resp.status().is_server_error() || resp.status().is_client_error() {
285+
Err(Error::HttpResponse {
286+
status: resp.status().as_u16(),
287+
message: resp.text().await?,
288+
})
289+
} else {
290+
Ok(())
291+
}
222292
}
223293

224294
/// Get the current height of the blockchain tip
@@ -229,7 +299,14 @@ impl AsyncClient {
229299
.send()
230300
.await?;
231301

232-
Ok(resp.error_for_status()?.text().await?.parse()?)
302+
if resp.status().is_server_error() || resp.status().is_client_error() {
303+
Err(Error::HttpResponse {
304+
status: resp.status().as_u16(),
305+
message: resp.text().await?,
306+
})
307+
} else {
308+
Ok(resp.text().await?.parse()?)
309+
}
233310
}
234311

235312
/// Get the [`BlockHash`] of the current blockchain tip.
@@ -240,9 +317,14 @@ impl AsyncClient {
240317
.send()
241318
.await?;
242319

243-
Ok(BlockHash::from_str(
244-
&resp.error_for_status()?.text().await?,
245-
)?)
320+
if resp.status().is_server_error() || resp.status().is_client_error() {
321+
Err(Error::HttpResponse {
322+
status: resp.status().as_u16(),
323+
message: resp.text().await?,
324+
})
325+
} else {
326+
Ok(BlockHash::from_str(&resp.text().await?)?)
327+
}
246328
}
247329

248330
/// Get the [`BlockHash`] of a specific block height
@@ -257,9 +339,14 @@ impl AsyncClient {
257339
return Err(Error::HeaderHeightNotFound(block_height));
258340
}
259341

260-
Ok(BlockHash::from_str(
261-
&resp.error_for_status()?.text().await?,
262-
)?)
342+
if resp.status().is_server_error() || resp.status().is_client_error() {
343+
Err(Error::HttpResponse {
344+
status: resp.status().as_u16(),
345+
message: resp.text().await?,
346+
})
347+
} else {
348+
Ok(BlockHash::from_str(&resp.text().await?)?)
349+
}
263350
}
264351

265352
/// Get confirmed transaction history for the specified address/scripthash,
@@ -278,27 +365,36 @@ impl AsyncClient {
278365
),
279366
None => format!("{}/scripthash/{:x}/txs", self.url, script_hash),
280367
};
281-
Ok(self
282-
.client
283-
.get(url)
284-
.send()
285-
.await?
286-
.error_for_status()?
287-
.json::<Vec<Tx>>()
288-
.await?)
368+
369+
let resp = self.client.get(url).send().await?;
370+
371+
if resp.status().is_server_error() || resp.status().is_client_error() {
372+
Err(Error::HttpResponse {
373+
status: resp.status().as_u16(),
374+
message: resp.text().await?,
375+
})
376+
} else {
377+
Ok(resp.json::<Vec<Tx>>().await?)
378+
}
289379
}
290380

291381
/// Get an map where the key is the confirmation target (in number of blocks)
292382
/// and the value is the estimated feerate (in sat/vB).
293383
pub async fn get_fee_estimates(&self) -> Result<HashMap<String, f64>, Error> {
294-
Ok(self
384+
let resp = self
295385
.client
296386
.get(&format!("{}/fee-estimates", self.url,))
297387
.send()
298-
.await?
299-
.error_for_status()?
300-
.json::<HashMap<String, f64>>()
301-
.await?)
388+
.await?;
389+
390+
if resp.status().is_server_error() || resp.status().is_client_error() {
391+
Err(Error::HttpResponse {
392+
status: resp.status().as_u16(),
393+
message: resp.text().await?,
394+
})
395+
} else {
396+
Ok(resp.json::<HashMap<String, f64>>().await?)
397+
}
302398
}
303399

304400
/// Gets some recent block summaries starting at the tip or at `height` if provided.
@@ -311,14 +407,16 @@ impl AsyncClient {
311407
None => format!("{}/blocks", self.url),
312408
};
313409

314-
Ok(self
315-
.client
316-
.get(&url)
317-
.send()
318-
.await?
319-
.error_for_status()?
320-
.json()
321-
.await?)
410+
let resp = self.client.get(&url).send().await?;
411+
412+
if resp.status().is_server_error() || resp.status().is_client_error() {
413+
Err(Error::HttpResponse {
414+
status: resp.status().as_u16(),
415+
message: resp.text().await?,
416+
})
417+
} else {
418+
Ok(resp.json::<Vec<BlockSummary>>().await?)
419+
}
322420
}
323421

324422
/// Get the underlying base URL.

0 commit comments

Comments
 (0)