Skip to content

Commit a8cde52

Browse files
authored
More robust and better error reporting of errors in fetch (#1438)
Potentially fixes #1437 although the cause of the failure is unknown. This patch should at least give us better insight to what is the cause. EDIT: with several successful runs, it seems that the fix was being stricter on the status code. Nevertheless, the rest of the code helps better tracing potential issues that might arise.
1 parent a872e53 commit a8cde52

File tree

2 files changed

+59
-16
lines changed

2 files changed

+59
-16
lines changed

crates/solidity/testing/perf/cargo/src/dataset.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fs;
33
use std::path::Path;
44
use std::sync::OnceLock;
55

6-
use anyhow::{anyhow, Result};
6+
use anyhow::{anyhow, bail, Result};
77
use semver::{BuildMetadata, Prerelease};
88
use serde::Deserialize;
99
use slang_solidity::compilation::{CompilationBuilder, CompilationBuilderConfig, CompilationUnit};
@@ -142,8 +142,14 @@ fn import_resolver_from<'a, T: Iterator<Item = &'a String>>(
142142

143143
impl SolidityProject {
144144
pub fn build(json_file: &Path) -> Result<Self> {
145-
let json_str = fs::read_to_string(json_file)?;
146-
let metadata: Metadata = serde_json::from_str(&json_str)?;
145+
let json_str = match fs::read_to_string(json_file) {
146+
Ok(json_str) => json_str,
147+
Err(e) => bail!("Error reading file {json_file:?}: {e}"),
148+
};
149+
let metadata = match serde_json::from_str::<Metadata>(&json_str) {
150+
Ok(metadata) => metadata,
151+
Err(e) => bail!("Error parsing file {json_file:?}: {e}\nWhile parsing:\n{json_str}"),
152+
};
147153
let sself: Self = Self::try_from(metadata)?;
148154
Ok(sself)
149155
}

crates/solidity/testing/utils/src/fetch.rs

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ use std::path::Path;
22
use std::time::Duration;
33
use std::{fs, thread};
44

5-
use anyhow::{Ok, Result};
6-
use reqwest::blocking::get;
5+
use anyhow::{bail, Result};
6+
use reqwest::header::CONTENT_TYPE;
77
use serde_json::Value;
88

9+
const MAX_RETRIES: u64 = 6;
10+
911
// Given an address and a path, it downloads the json file from sourcify,
1012
// and stores it in the path.
1113
pub fn fetch(address: &str, base_path: &Path) -> Result<()> {
@@ -20,21 +22,56 @@ pub fn fetch(address: &str, base_path: &Path) -> Result<()> {
2022
let url =
2123
format!("https://sourcify.dev/server/v2/contract/1/{address}/?fields=sources,compilation");
2224

23-
let mut body = get(&url);
24-
2525
// Try with exponential backoff
26-
let mut tries = 0;
27-
while body.is_err() && tries < 6 {
28-
tries += 1;
29-
thread::sleep(Duration::from_secs(2 ^ tries));
30-
body = get(&url);
31-
}
26+
let mut retries = 0;
27+
let project = loop {
28+
retries += 1;
3229

33-
let project_json: Value = body?.json()?;
30+
match try_fetch_project(&url) {
31+
Ok(project) => break project,
32+
Err(err) => {
33+
println!("Error fetching {url} (attempt {retries}/{MAX_RETRIES}): {err}");
34+
if retries <= MAX_RETRIES {
35+
thread::sleep(Duration::from_secs(2 ^ retries));
36+
} else {
37+
bail!("Giving up after {retries} attempts.");
38+
}
39+
}
40+
}
41+
};
3442

35-
let content = serde_json::to_string_pretty(&project_json)?;
43+
// write project to disk
3644
fs::create_dir_all(base_path)?;
37-
fs::write(project_file_path, content)?;
45+
fs::write(project_file_path, project)?;
3846

3947
Ok(())
4048
}
49+
50+
fn try_fetch_project(url: &str) -> Result<String> {
51+
let response = reqwest::blocking::get(url)?;
52+
53+
if response.status() != reqwest::StatusCode::OK {
54+
bail!("Status code is {status}", status = response.status());
55+
}
56+
57+
let Some(content_type) = response.headers().get(CONTENT_TYPE) else {
58+
bail!("No content-type header")
59+
};
60+
61+
let content_type_str = content_type.to_str().unwrap_or("");
62+
if !content_type_str
63+
.trim()
64+
.to_ascii_lowercase()
65+
.starts_with("application/json")
66+
{
67+
bail!("content-type is not json: {content_type_str:?}");
68+
}
69+
70+
if response.content_length().unwrap_or_default() == 0 {
71+
bail!("body is empty");
72+
}
73+
74+
let project_json: Value = response.json()?;
75+
76+
Ok(serde_json::to_string_pretty(&project_json)?)
77+
}

0 commit comments

Comments
 (0)