Skip to content

Commit

Permalink
feat: 支持设置代理。
Browse files Browse the repository at this point in the history
  • Loading branch information
hamflx committed Feb 14, 2023
1 parent 02e8c53 commit f2dfc0a
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 35 deletions.
39 changes: 39 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ anyhow = "1.0.68"
bytes = "1.4.0"
clap = { version = "4.1.1", features = ["derive"] }
compress-tools = "0.14.0"
reqwest = { version = "0.11.13", features = ["blocking"] }
reqwest = { version = "0.11.13", features = ["blocking", "socks"] }
select = "0.6.0"
serde = { version = "1.0.152", features = ["serde_derive"] }
serde_json = "1.0"
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ Download `Firefox 98`:
fb --firefox 98
```

使用 socks5 代理:

Using SOCKS5 proxy:

```powershell
# 使用 socks5h 以使 DNS 通过代理解析。
# Use socks5h to resolve DNS through the proxy.
fb --proxy socks5h://127.0.0.1:10801 98
# 或仅使用 socks5 代理。
# Or simply use socks5 proxy.
fb --proxy socks5://127.0.0.1:10801 98
```

## 许可(License)

MIT @ 2023 hamflx
21 changes: 15 additions & 6 deletions src/chromium/builds.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
use std::{fs::File, io::BufReader};

use anyhow::{anyhow, Result};
use reqwest::blocking::Client;
use serde::{Deserialize, Serialize};

use crate::{platform::Platform, utils::get_cached_file_path};

pub(crate) struct ChromiumBuilds(Vec<String>);

impl ChromiumBuilds {
pub(crate) fn init(platform: Platform) -> Result<Self> {
pub(crate) fn init(platform: Platform, client: Client) -> Result<Self> {
let prefix = platform.prefix();
let builds_json_path = get_cached_file_path(&format!("builds-{prefix}.json"))?;
let build_list = if std::fs::try_exists(&builds_json_path).unwrap_or_default() {
println!("==> using cached builds.");
serde_json::from_reader(BufReader::new(File::open(&builds_json_path)?))?
} else {
println!("==> retrieving builds ...");
let pages = ChromiumBuildsPage::new(prefix)?;
let pages = ChromiumBuildsPage::new(prefix, client)?;
let mut unwrapped_page_list = Vec::new();
for page in pages {
unwrapped_page_list.push(page?);
Expand Down Expand Up @@ -54,14 +55,16 @@ pub(crate) struct ChromiumBuildsPage {
prefix: &'static str,
next_page_token: Option<String>,
done: bool,
client: Client,
}

impl ChromiumBuildsPage {
pub fn new(prefix: &'static str) -> Result<Self> {
pub fn new(prefix: &'static str, client: Client) -> Result<Self> {
Ok(Self {
next_page_token: None,
done: false,
prefix,
client,
})
}
}
Expand All @@ -80,7 +83,10 @@ impl Iterator for ChromiumBuildsPage {
.unwrap_or_default();
let url = format!("https://www.googleapis.com/storage/v1/b/chromium-browser-snapshots/o?delimiter=/&prefix={}/&fields=items(kind,mediaLink,metadata,name,size,updated),kind,prefixes,nextPageToken{}", self.prefix, next_page_token);

let prefixes = reqwest::blocking::get(&url)
let prefixes = self
.client
.get(&url)
.send()
.map_err(|err| anyhow!("请求 {} 时出错:{:?}", url, err))
.and_then(|response| {
let page: ChromiumBuildPage = serde_json::from_reader(response)?;
Expand All @@ -96,10 +102,13 @@ impl Iterator for ChromiumBuildsPage {
}
}

pub(crate) fn fetch_build_detail(prefix: &str) -> Result<Vec<GoogleApiStorageObject>> {
pub(crate) fn fetch_build_detail(
prefix: &str,
client: &Client,
) -> Result<Vec<GoogleApiStorageObject>> {
let url = format!("https://www.googleapis.com/storage/v1/b/chromium-browser-snapshots/o?delimiter=/&prefix={prefix}&fields=items(kind,mediaLink,metadata,name,size,updated),kind,prefixes,nextPageToken");
println!("==> fetching history {url} ...");
let response = reqwest::blocking::get(url)?;
let response = client.get(url).send()?;
let build_detail: ChromiumBuildPage = serde_json::from_reader(response)?;
println!("==> files:");
for file in &build_detail.items {
Expand Down
4 changes: 3 additions & 1 deletion src/chromium/download.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
use std::{fs::OpenOptions, io::copy, path::Path};

use anyhow::anyhow;
use reqwest::blocking::Client;
use zip::read::read_zipfile_from_stream;

use super::builds::GoogleApiStorageObject;

pub(crate) fn download_chromium_zip_file(
zip_file: &GoogleApiStorageObject,
base_path: &Path,
client: &Client,
) -> std::result::Result<(), anyhow::Error> {
// 开始下载压缩文件。
println!("==> downloading {}", zip_file.media_link);
let mut win_zip_response = reqwest::blocking::get(&zip_file.media_link)?;
let mut win_zip_response = client.get(&zip_file.media_link).send()?;

loop {
let mut zip = match read_zipfile_from_stream(&mut win_zip_response) {
Expand Down
9 changes: 5 additions & 4 deletions src/chromium/history.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::{fs::File, io::BufReader};

use anyhow::Result;
use reqwest::blocking::Client;
use serde::{Deserialize, Serialize};

use crate::{platform::Platform, utils::get_cached_file_path};

pub(crate) struct ChromiumHistory(Vec<ChromiumHistoryInfo>);

impl ChromiumHistory {
pub(crate) fn init(platform: Platform) -> Result<Self> {
pub(crate) fn init(platform: Platform, client: Client) -> Result<Self> {
let os_arg = platform.arg_name();
let history_json_path = get_cached_file_path(&format!("history-{os_arg}.json"))?;
let history_list = if std::fs::try_exists(&history_json_path).unwrap_or_default() {
Expand All @@ -18,7 +19,7 @@ impl ChromiumHistory {
println!("==> retrieving history.json ...");
let url =
format!("https://omahaproxy.appspot.com/history.json?os={os_arg}&channel=stable");
let response = reqwest::blocking::get(url)?;
let response = client.get(url).send()?;
let history_list: Vec<ChromiumHistoryInfo> = serde_json::from_reader(response)?;
std::fs::write(&history_json_path, serde_json::to_string(&history_list)?)?;
history_list
Expand Down Expand Up @@ -48,13 +49,13 @@ pub(crate) struct ChromiumHistoryInfo {
}

impl ChromiumHistoryInfo {
pub(crate) fn deps(&self) -> Result<ChromiumDepsInfo> {
pub(crate) fn deps(&self, client: &Client) -> Result<ChromiumDepsInfo> {
let url = format!(
"https://omahaproxy.appspot.com/deps.json?version={}",
self.version
);
println!("==> fetching deps {url} ...");
let response = reqwest::blocking::get(url)?;
let response = client.get(url).send()?;
Ok(serde_json::from_reader(response)?)
}
}
Expand Down
17 changes: 11 additions & 6 deletions src/chromium/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::vec::IntoIter;

use anyhow::{anyhow, Result};
use reqwest::blocking::Client;

use crate::{
common::{BrowserReleaseItem, BrowserReleases},
Expand All @@ -22,24 +23,26 @@ pub(crate) struct ChromiumReleases {
platform: Platform,
history: ChromiumHistory,
builds: ChromiumBuilds,
client: Client,
}

impl BrowserReleases for ChromiumReleases {
type ReleaseItem = ChromiumReleaseItem;
type Matches<'r> = ChromiumReleaseMatches<'r>;

fn init(platform: Platform) -> anyhow::Result<Self>
fn init(platform: Platform, client: Client) -> anyhow::Result<Self>
where
Self: Sized,
{
// history.json 包含了 base_position 和版本号。
let history = ChromiumHistory::init(platform)?;
let history = ChromiumHistory::init(platform, client.clone())?;
// builds 包含了所有可下载的 position 信息。
let builds = ChromiumBuilds::init(platform)?;
let builds = ChromiumBuilds::init(platform, client.clone())?;
Ok(Self {
platform,
history,
builds,
client,
})
}

Expand Down Expand Up @@ -70,7 +73,7 @@ impl<'r> Iterator for ChromiumReleaseMatches<'r> {

fn next(&mut self) -> Option<Self::Item> {
for history in self.iter.by_ref() {
let deps = match history.deps() {
let deps = match history.deps(&self.releases.client) {
Ok(deps) => deps,
Err(err) => return Some(Err(err)),
};
Expand All @@ -81,6 +84,7 @@ impl<'r> Iterator for ChromiumReleaseMatches<'r> {
return Some(Ok(ChromiumReleaseItem {
rev_prefix: rev_prefix.clone(),
version: deps.chromium_version,
client: self.releases.client.clone(),
}))
}
None => println!("==> no build found for rev: {pos}"),
Expand All @@ -103,12 +107,13 @@ impl<'r> Iterator for ChromiumReleaseMatches<'r> {
pub(crate) struct ChromiumReleaseItem {
rev_prefix: String,
version: String,
client: Client,
}

impl BrowserReleaseItem for ChromiumReleaseItem {
fn download(&self) -> Result<()> {
// 根据 prefix 找到该版本文件列表,以及 chrome-win.zip 文件信息。
let build_files = fetch_build_detail(&self.rev_prefix)?;
let build_files = fetch_build_detail(&self.rev_prefix, &self.client)?;
let zip_file = [
"chrome-win.zip",
"chrome-win32.zip",
Expand All @@ -127,6 +132,6 @@ impl BrowserReleaseItem for ChromiumReleaseItem {
// 先保存到临时目录里面,待解压的时候,找到里面的版本信息,再重命名一下文件夹。
let base_path = std::env::current_dir()?.join(format!("chromium-{}", self.version));
std::fs::create_dir_all(&base_path)?;
download_chromium_zip_file(zip_file, &base_path)
download_chromium_zip_file(zip_file, &base_path, &self.client)
}
}
3 changes: 2 additions & 1 deletion src/common.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::Result;
use reqwest::blocking::Client;

use crate::platform::Platform;

Expand All @@ -8,7 +9,7 @@ pub(crate) trait BrowserReleases {
where
Self: 'r;

fn init(platform: Platform) -> Result<Self>
fn init(platform: Platform, client: Client) -> Result<Self>
where
Self: Sized;

Expand Down
21 changes: 12 additions & 9 deletions src/firefox/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,26 @@ use std::{cmp::Ordering, env::current_dir, fs::create_dir_all, io::Cursor};
use anyhow::{anyhow, Result};
use bytes::Bytes;
use compress_tools::{uncompress_archive, Ownership};
use reqwest::blocking::Client;
use select::{
document::Document,
predicate::{self, Predicate},
};

use crate::utils::{find_sequence, get_cached_file_path};

pub(crate) fn download_firefox(version: &str) -> Result<()> {
pub(crate) fn download_firefox(version: &str, client: &Client) -> Result<()> {
let cur_dir = current_dir()?;

let spider = FirefoxVersionSpider::init()?;
let spider = FirefoxVersionSpider::init(client)?;
let matched_version_list = spider.find(version);
let matched_version = matched_version_list
.first()
.ok_or_else(|| anyhow!("No matched version found"))?;

let zip_content = download_firefox_zip(matched_version, "win64").or_else(|err| {
let zip_content = download_firefox_zip(matched_version, "win64", client).or_else(|err| {
println!("==> download firefox win64 failed: {err}, trying win32 ...");
download_firefox_zip(matched_version, "win32")
download_firefox_zip(matched_version, "win32", client)
})?;

let base_path = cur_dir.join(format!(".tmp-firefox-{matched_version}"));
Expand All @@ -46,13 +47,13 @@ pub(crate) fn download_firefox(version: &str) -> Result<()> {
Ok(())
}

fn download_firefox_zip(version: &str, arch: &str) -> Result<Bytes> {
fn download_firefox_zip(version: &str, arch: &str, client: &Client) -> Result<Bytes> {
let cur_dir = current_dir()?;
let url = format!(
"https://ftp.mozilla.org/pub/firefox/releases/{version}/{arch}/zh-CN/Firefox%20Setup%20{version}.exe"
);
println!("==> download firefox: {url}");
let response = reqwest::blocking::get(url)?;
let response = client.get(url).send()?;
if !response.status().is_success() {
return Err(anyhow!("Download firefox failed: {}", response.status()));
}
Expand All @@ -75,16 +76,18 @@ fn download_firefox_zip(version: &str, arch: &str) -> Result<Bytes> {
struct FirefoxVersionSpider(Vec<String>);

impl FirefoxVersionSpider {
fn init() -> Result<Self> {
fn init(client: &Client) -> Result<Self> {
let cached_releases_path = get_cached_file_path("firefox-releases.json")?;
if cached_releases_path.exists() {
println!("==> using cached firefox releases");
let releases = serde_json::from_reader(std::fs::File::open(cached_releases_path)?)?;
Ok(Self(releases))
} else {
println!("==> fetching firefox releases from ftp.mozilla.org ...");
let response =
reqwest::blocking::get("https://ftp.mozilla.org/pub/firefox/releases/")?.text()?;
let response = client
.get("https://ftp.mozilla.org/pub/firefox/releases/")
.send()?
.text()?;
let doc = Document::from(response.as_str());
let releases = doc
.find(
Expand Down
Loading

0 comments on commit f2dfc0a

Please sign in to comment.