mirror of
https://github.com/edu4rdshl/fhc.git
synced 2026-07-18 07:34:52 +00:00
Compare commits
14 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ed011be8a6 | |||
| 70ee3429ae | |||
| 7b39d6ebc8 | |||
| e7917b1e47 | |||
| 06b99f9b5d | |||
| 12b42d9a05 | |||
| 6701590efd | |||
| cc37ccbcac | |||
| fa58e6aa7e | |||
| 291f8dfe9e | |||
| 99db1a4b29 | |||
| c0ba951955 | |||
| 002a612e70 | |||
| cdd2d778b4 |
8 changed files with 954 additions and 567 deletions
1327
Cargo.lock
generated
1327
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
19
Cargo.toml
19
Cargo.toml
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "fhc"
|
name = "fhc"
|
||||||
version = "0.8.2"
|
version = "0.9.1"
|
||||||
authors = ["Eduard Tolosa <edu4rdshl@protonmail.com>"]
|
authors = ["Eduard Tolosa <edu4rdshl@protonmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Fast HTTP Checker."
|
description = "Fast HTTP Checker."
|
||||||
|
|
@ -12,22 +12,21 @@ readme = "README.md"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.41.0", features = ["full", "io-util"] }
|
tokio = { version = "1.47.0", features = ["full", "io-util"] }
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
clap = { version = "4.5.20", features = ["derive"] }
|
clap = { version = "4.5.41", features = ["derive"] }
|
||||||
reqwest = { version = "0.12.8", features = [
|
reqwest = { version = "0.12.22", features = [
|
||||||
"hickory-dns",
|
"hickory-dns",
|
||||||
"rustls-tls",
|
"rustls-tls",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
] }
|
] }
|
||||||
openssl = { version = "0.10.68", features = ["vendored"] }
|
openssl = { version = "0.10.73", features = ["vendored"] }
|
||||||
rand = "0.8.5"
|
rand = "0.9.2"
|
||||||
scraper = "0.20.0"
|
scraper = "0.23.1"
|
||||||
async-recursion = "1.1.1"
|
async-recursion = "1.1.1"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = 'thin'
|
lto = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
panic = 'abort'
|
panic = 'abort'
|
||||||
incremental = false
|
strip = true
|
||||||
opt-level = "s"
|
|
||||||
|
|
|
||||||
31
builder.sh
31
builder.sh
|
|
@ -10,22 +10,17 @@ AARCH_TARGET="aarch64-unknown-linux-gnu"
|
||||||
MANPAGE_DIR="./$NAME.1"
|
MANPAGE_DIR="./$NAME.1"
|
||||||
BIN_OUTPUT_DIR="./ghbinaries"
|
BIN_OUTPUT_DIR="./ghbinaries"
|
||||||
|
|
||||||
if ! systemctl is-active docker >/dev/null 2>&1; then
|
rm -rf "$BIN_OUTPUT_DIR"
|
||||||
echo "Docker is not running. Starting docker."
|
mkdir -p "$BIN_OUTPUT_DIR"
|
||||||
if ! sudo systemctl start docker; then
|
|
||||||
echo "Failed to start docker."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Linux build
|
# Linux build
|
||||||
echo "Building Linux artifact."
|
echo "Building Linux artifact."
|
||||||
if cargo build -q --release --target="$LINUX_TARGET"; then
|
if cross build -q --release --target="$LINUX_TARGET"; then
|
||||||
echo "Linux artifact build: SUCCESS"
|
echo "Linux artifact build: SUCCESS"
|
||||||
cp "target/$LINUX_TARGET/release/$NAME" "target/$LINUX_TARGET/release/$NAME-linux"
|
cp "target/$LINUX_TARGET/release/$NAME" "target/$LINUX_TARGET/release/$NAME-linux"
|
||||||
strip "target/$LINUX_TARGET/release/$NAME-linux"
|
strip "target/$LINUX_TARGET/release/$NAME-linux"
|
||||||
sha512sum "target/$LINUX_TARGET/release/$NAME-linux" >"target/$LINUX_TARGET/release/$NAME-linux.sha512"
|
sha512sum "target/$LINUX_TARGET/release/$NAME-linux" >"$BIN_OUTPUT_DIR/$NAME-linux.sha512"
|
||||||
zip -q "$BIN_OUTPUT_DIR/$NAME-linux.zip" "target/$LINUX_TARGET/release/$NAME-linux"
|
zip -q -j "$BIN_OUTPUT_DIR/$NAME-linux-x64.zip" "target/$LINUX_TARGET/release/$NAME-linux"
|
||||||
else
|
else
|
||||||
echo "Linux artifact build: FAILED"
|
echo "Linux artifact build: FAILED"
|
||||||
fi
|
fi
|
||||||
|
|
@ -36,8 +31,8 @@ if cross build -q --release --target="$LINUX_X86_TARGET"; then
|
||||||
echo "Linux x86 artifact build: SUCCESS"
|
echo "Linux x86 artifact build: SUCCESS"
|
||||||
cp "target/$LINUX_X86_TARGET/release/$NAME" "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
|
cp "target/$LINUX_X86_TARGET/release/$NAME" "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
|
||||||
strip "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
|
strip "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
|
||||||
sha512sum "target/$LINUX_X86_TARGET/release/$NAME-linux-i386" >"target/$LINUX_X86_TARGET/release/$NAME-linux-i386.sha512"
|
sha512sum "target/$LINUX_X86_TARGET/release/$NAME-linux-i386" >"$BIN_OUTPUT_DIR/$NAME-linux-i386.sha512"
|
||||||
zip -q "$BIN_OUTPUT_DIR/$NAME-linux-i386.zip" "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
|
zip -q -j "$BIN_OUTPUT_DIR/$NAME-linux-i386.zip" "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
|
||||||
else
|
else
|
||||||
echo "Linux x86 artifact build: FAILED"
|
echo "Linux x86 artifact build: FAILED"
|
||||||
fi
|
fi
|
||||||
|
|
@ -48,8 +43,8 @@ if cross build -q --release --target="$WIN_TARGET"; then
|
||||||
echo "Windows artifact build: SUCCESS"
|
echo "Windows artifact build: SUCCESS"
|
||||||
cp "target/$WIN_TARGET/release/$NAME.exe" "target/$WIN_TARGET/release/$NAME-windows.exe"
|
cp "target/$WIN_TARGET/release/$NAME.exe" "target/$WIN_TARGET/release/$NAME-windows.exe"
|
||||||
strip "target/$WIN_TARGET/release/$NAME-windows.exe"
|
strip "target/$WIN_TARGET/release/$NAME-windows.exe"
|
||||||
sha512sum "target/$WIN_TARGET/release/$NAME-windows.exe" >"target/$WIN_TARGET/release/$NAME-windows.exe.sha512"
|
sha512sum "target/$WIN_TARGET/release/$NAME-windows.exe" >"$BIN_OUTPUT_DIR/$NAME-windows.exe.sha512"
|
||||||
zip -q "$BIN_OUTPUT_DIR/$NAME-windows.zip" "target/$WIN_TARGET/release/$NAME-windows.exe"
|
zip -q -j "$BIN_OUTPUT_DIR/$NAME-windows.zip" "target/$WIN_TARGET/release/$NAME-windows.exe"
|
||||||
else
|
else
|
||||||
echo "Windows artifact build: FAILED"
|
echo "Windows artifact build: FAILED"
|
||||||
fi
|
fi
|
||||||
|
|
@ -60,8 +55,8 @@ if cross build -q --release --target="$ARMV7_TARGET"; then
|
||||||
echo "ARMv7 artifact build: SUCCESS"
|
echo "ARMv7 artifact build: SUCCESS"
|
||||||
cp "target/$ARMV7_TARGET/release/$NAME" "target/$ARMV7_TARGET/release/$NAME-armv7"
|
cp "target/$ARMV7_TARGET/release/$NAME" "target/$ARMV7_TARGET/release/$NAME-armv7"
|
||||||
strip "target/$ARMV7_TARGET/release/$NAME-armv7"
|
strip "target/$ARMV7_TARGET/release/$NAME-armv7"
|
||||||
sha512sum "target/$ARMV7_TARGET/release/$NAME-armv7" >"target/$ARMV7_TARGET/release/$NAME-armv7.sha512"
|
sha512sum "target/$ARMV7_TARGET/release/$NAME-armv7" >"$BIN_OUTPUT_DIR/$NAME-armv7.sha512"
|
||||||
zip -q "$BIN_OUTPUT_DIR/$NAME-armv7.zip" "target/$ARMV7_TARGET/release/$NAME-armv7"
|
zip -q -j "$BIN_OUTPUT_DIR/$NAME-armv7.zip" "target/$ARMV7_TARGET/release/$NAME-armv7"
|
||||||
else
|
else
|
||||||
echo "ARMv7 artifact build: FAILED"
|
echo "ARMv7 artifact build: FAILED"
|
||||||
fi
|
fi
|
||||||
|
|
@ -72,8 +67,8 @@ if cross build -q --release --target="$AARCH_TARGET"; then
|
||||||
echo "Aarch64 artifact build: SUCCESS"
|
echo "Aarch64 artifact build: SUCCESS"
|
||||||
cp "target/$AARCH_TARGET/release/$NAME" "target/$AARCH_TARGET/release/$NAME-aarch64"
|
cp "target/$AARCH_TARGET/release/$NAME" "target/$AARCH_TARGET/release/$NAME-aarch64"
|
||||||
strip "target/$AARCH_TARGET/release/$NAME-aarch64"
|
strip "target/$AARCH_TARGET/release/$NAME-aarch64"
|
||||||
sha512sum "target/$AARCH_TARGET/release/$NAME-aarch64" >"target/$AARCH_TARGET/release/$NAME-aarch64.sha512"
|
sha512sum "target/$AARCH_TARGET/release/$NAME-aarch64" >"$BIN_OUTPUT_DIR/$NAME-aarch64.sha512"
|
||||||
zip -q "$BIN_OUTPUT_DIR/$NAME-aarch64.zip" "target/$AARCH_TARGET/release/$NAME-aarch64"
|
zip -q -j "$BIN_OUTPUT_DIR/$NAME-aarch64.zip" "target/$AARCH_TARGET/release/$NAME-aarch64"
|
||||||
else
|
else
|
||||||
echo "Aarch64 artifact build: FAILED"
|
echo "Aarch64 artifact build: FAILED"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
6
fhc.1
6
fhc.1
|
|
@ -1,7 +1,7 @@
|
||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||||
.TH FHC "1" "October 2024" "fhc 0.8.1" "User Commands"
|
.TH FHC "1" "August 2025" "fhc 0.9.1" "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
fhc \- manual page for fhc 0.8.1
|
fhc \- manual page for fhc 0.9.1
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B fhc
|
.B fhc
|
||||||
[\fI\,OPTIONS\/\fR]
|
[\fI\,OPTIONS\/\fR]
|
||||||
|
|
@ -25,7 +25,7 @@ Domain to check \- can be omitted if using stdin
|
||||||
Number of retries [default: 1]
|
Number of retries [default: 1]
|
||||||
.TP
|
.TP
|
||||||
\fB\-L\fR, \fB\-\-max\-redirects\fR <MAX_REDIRECTS>
|
\fB\-L\fR, \fB\-\-max\-redirects\fR <MAX_REDIRECTS>
|
||||||
Maximum number of redirects [default: 10]
|
Maximum number of redirects, disabled by default [default: 0]
|
||||||
.TP
|
.TP
|
||||||
\fB\-b\fR, \fB\-\-bruteforce\fR
|
\fB\-b\fR, \fB\-\-bruteforce\fR
|
||||||
Enable bruteforce mode
|
Enable bruteforce mode
|
||||||
|
|
|
||||||
114
src/httplib.rs
114
src/httplib.rs
|
|
@ -4,7 +4,7 @@ use {
|
||||||
utils,
|
utils,
|
||||||
},
|
},
|
||||||
futures::stream::StreamExt,
|
futures::stream::StreamExt,
|
||||||
rand::{distributions::Alphanumeric, thread_rng as rng, Rng},
|
rand::{distr::Alphanumeric, rng, Rng},
|
||||||
reqwest::{
|
reqwest::{
|
||||||
header::{CONTENT_LENGTH, CONTENT_TYPE, USER_AGENT},
|
header::{CONTENT_LENGTH, CONTENT_TYPE, USER_AGENT},
|
||||||
redirect::Policy,
|
redirect::Policy,
|
||||||
|
|
@ -15,37 +15,37 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[allow(clippy::similar_names)]
|
||||||
pub async fn return_http_data(options: &LibOptions, from_cli: bool) -> HashMap<String, HttpData> {
|
pub async fn return_http_data(options: &LibOptions, from_cli: bool) -> HashMap<String, HttpData> {
|
||||||
let threads = options.hosts.len().min(options.threads);
|
let threads = options.hosts.len().min(options.threads);
|
||||||
|
|
||||||
let filter_codes = options.filter_codes.as_deref().unwrap_or_default();
|
let filter_codes = options.filter_codes.as_deref().unwrap_or_default();
|
||||||
let exclude_codes = options.exclude_codes.as_deref().unwrap_or_default();
|
let exclude_codes = options.exclude_codes.as_deref().unwrap_or_default();
|
||||||
|
|
||||||
futures::stream::iter(options.hosts.clone().into_iter().map(|host| {
|
futures::stream::iter(options.hosts.iter().map(|host| {
|
||||||
let user_agent = utils::return_random_user_agent(&options.user_agents);
|
let user_agent = utils::return_random_user_agent(&options.user_agents);
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let mut http_data = HttpData {
|
let mut http_data = HttpData::new(host.clone());
|
||||||
checked_host: host.clone(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let mut response = None;
|
let mut response = None;
|
||||||
|
|
||||||
// Attempt both HTTPS and HTTP requests, retry if necessary
|
|
||||||
for _ in 0..options.retries {
|
for _ in 0..options.retries {
|
||||||
let https_req = options
|
let https_req = options
|
||||||
.client
|
.client
|
||||||
.get(format!("https://{}", host))
|
.get(format!("https://{host}"))
|
||||||
.header(USER_AGENT, &user_agent)
|
.header(USER_AGENT, &user_agent)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
let http_req = options
|
let http_req = options
|
||||||
.client
|
.client
|
||||||
.get(format!("http://{}", host))
|
.get(format!("http://{host}"))
|
||||||
.header(USER_AGENT, &user_agent)
|
.header(USER_AGENT, &user_agent)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
response = https_req.await.or(http_req.await).ok();
|
response = tokio::select! {
|
||||||
|
https_result = https_req => https_result.ok(),
|
||||||
|
http_result = http_req => http_result.ok(),
|
||||||
|
};
|
||||||
|
|
||||||
if response.is_some() {
|
if response.is_some() {
|
||||||
break;
|
break;
|
||||||
|
|
@ -72,16 +72,26 @@ pub async fn return_http_data(options: &LibOptions, from_cli: bool) -> HashMap<S
|
||||||
&& (exclude_codes.is_empty()
|
&& (exclude_codes.is_empty()
|
||||||
|| !exclude_codes.contains(&http_data.status_code.to_string()))
|
|| !exclude_codes.contains(&http_data.status_code.to_string()))
|
||||||
{
|
{
|
||||||
|
// Use faster I/O for high-throughput scenarios
|
||||||
|
use std::io::{self, Write};
|
||||||
|
let stdout = io::stdout();
|
||||||
|
let mut handle = stdout.lock();
|
||||||
|
|
||||||
if options.show_full_data {
|
if options.show_full_data {
|
||||||
println!(
|
let _ = writeln!(
|
||||||
|
handle,
|
||||||
"{},[{}],[{}]",
|
"{},[{}],[{}]",
|
||||||
http_data.checked_host, http_data.final_url, http_data.status_code
|
http_data.checked_host, http_data.final_url, http_data.status_code
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
println!("{}://{}", http_data.protocol, http_data.checked_host);
|
let _ = writeln!(
|
||||||
|
handle,
|
||||||
|
"{}://{}",
|
||||||
|
http_data.protocol, http_data.checked_host
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(host, http_data)
|
(host.clone(), http_data)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.buffer_unordered(threads)
|
.buffer_unordered(threads)
|
||||||
|
|
@ -102,6 +112,9 @@ pub fn return_http_client(timeout: u64, max_redirects: usize) -> Client {
|
||||||
.redirect(policy)
|
.redirect(policy)
|
||||||
.danger_accept_invalid_certs(true)
|
.danger_accept_invalid_certs(true)
|
||||||
.use_native_tls()
|
.use_native_tls()
|
||||||
|
.pool_max_idle_per_host(50)
|
||||||
|
.pool_idle_timeout(std::time::Duration::from_secs(30))
|
||||||
|
.tcp_keepalive(std::time::Duration::from_secs(60))
|
||||||
.build()
|
.build()
|
||||||
.expect("Failed to create HTTP client")
|
.expect("Failed to create HTTP client")
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +131,14 @@ pub async fn assign_response_data(http_data: &mut HttpData, resp: Response, retu
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let full_body = (resp.text().await).unwrap_or_default();
|
let full_body = {
|
||||||
|
const MAX_BODY_SIZE: usize = 1024 * 1024; // 1MB limit
|
||||||
|
match resp.text().await {
|
||||||
|
Ok(text) if text.len() <= MAX_BODY_SIZE => text,
|
||||||
|
Ok(text) => text.chars().take(MAX_BODY_SIZE).collect(),
|
||||||
|
Err(_) => String::new(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
http_data.content_length = headers
|
http_data.content_length = headers
|
||||||
.get(CONTENT_LENGTH)
|
.get(CONTENT_LENGTH)
|
||||||
|
|
@ -129,8 +149,11 @@ pub async fn assign_response_data(http_data: &mut HttpData, resp: Response, retu
|
||||||
|
|
||||||
return_title_and_body(http_data, &full_body);
|
return_title_and_body(http_data, &full_body);
|
||||||
|
|
||||||
http_data.words_count = full_body.split_whitespace().count();
|
let lines_count = full_body.lines().count();
|
||||||
http_data.lines = full_body.lines().count() + 1;
|
let words_count = full_body.split_whitespace().count();
|
||||||
|
|
||||||
|
http_data.words_count = words_count;
|
||||||
|
http_data.lines = lines_count + 1;
|
||||||
http_data.points_to_another_host = url.host_str() != Some(&http_data.checked_host);
|
http_data.points_to_another_host = url.host_str() != Some(&http_data.checked_host);
|
||||||
|
|
||||||
if return_filters {
|
if return_filters {
|
||||||
|
|
@ -144,35 +167,24 @@ pub async fn assign_response_data(http_data: &mut HttpData, resp: Response, retu
|
||||||
pub fn return_title_and_body(http_data: &mut HttpData, body: &str) {
|
pub fn return_title_and_body(http_data: &mut HttpData, body: &str) {
|
||||||
let document = Html::parse_document(body);
|
let document = Html::parse_document(body);
|
||||||
|
|
||||||
// Return title
|
let title_selector = Selector::parse("title").ok();
|
||||||
match Selector::parse("title") {
|
let body_selector = Selector::parse("body").ok();
|
||||||
Ok(selector) => {
|
|
||||||
if let Some(title_element) = document.select(&selector).next() {
|
if let Some(title_sel) = &title_selector {
|
||||||
http_data.title = title_element.inner_html();
|
http_data.title = document
|
||||||
} else {
|
.select(title_sel)
|
||||||
http_data.title = "NULL".to_string();
|
.next()
|
||||||
}
|
.map(|element| element.inner_html())
|
||||||
}
|
.unwrap_or_else(|| "NULL".to_string());
|
||||||
Err(err) => {
|
|
||||||
eprintln!("Failed to parse selector: {err:?}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return body
|
if let Some(body_sel) = &body_selector {
|
||||||
match Selector::parse("body") {
|
http_data.body = document
|
||||||
Ok(selector) => {
|
.select(body_sel)
|
||||||
if let Some(body_element) = document.select(&selector).next() {
|
.next()
|
||||||
http_data.body = body_element.inner_html();
|
.map(|element| element.inner_html())
|
||||||
} else {
|
.unwrap_or_else(|| "NULL".to_string());
|
||||||
http_data.body = "NULL".to_string();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
eprintln!("Failed to parse selector: {err:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drop(document);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn return_filters_data(
|
pub async fn return_filters_data(
|
||||||
|
|
@ -211,21 +223,17 @@ pub async fn return_filters_data(
|
||||||
|
|
||||||
let data = return_http_data(&lib_options, false).await;
|
let data = return_http_data(&lib_options, false).await;
|
||||||
|
|
||||||
data.values()
|
data.values().for_each(|http_data| {
|
||||||
.map(|http_data| {
|
|
||||||
http_filters
|
http_filters
|
||||||
.bad_http_lengths
|
.bad_http_lengths
|
||||||
.append(&mut vec![http_data.content_length.to_string()]);
|
.push(http_data.content_length.to_string());
|
||||||
http_filters.bad_words_numbers.append(&mut vec![http_data
|
http_filters
|
||||||
.body
|
.bad_words_numbers
|
||||||
.split(' ')
|
.push(http_data.words_count.to_string());
|
||||||
.count()
|
|
||||||
.to_string()]);
|
|
||||||
http_filters
|
http_filters
|
||||||
.bad_lines_numbers
|
.bad_lines_numbers
|
||||||
.append(&mut vec![http_data.lines.to_string()]);
|
.push(http_data.lines.to_string());
|
||||||
})
|
});
|
||||||
.for_each(drop);
|
|
||||||
|
|
||||||
http_filters
|
http_filters
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
} else {
|
} else {
|
||||||
io::stdin().read_to_string(&mut buffer).await?;
|
io::stdin().read_to_string(&mut buffer).await?;
|
||||||
hosts = buffer.lines().map(str::to_owned).collect();
|
hosts = buffer.lines().map(str::to_owned).collect();
|
||||||
};
|
}
|
||||||
|
|
||||||
let lib_options = LibOptions {
|
let lib_options = LibOptions {
|
||||||
hosts,
|
hosts,
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,16 @@ pub struct HttpData {
|
||||||
pub points_to_another_host: bool,
|
pub points_to_another_host: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HttpData {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(host: String) -> Self {
|
||||||
|
Self {
|
||||||
|
checked_host: host,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct LibOptions {
|
pub struct LibOptions {
|
||||||
pub hosts: HashSet<String>,
|
pub hosts: HashSet<String>,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{rng, seq::IndexedRandom};
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn user_agents() -> Vec<String> {
|
pub fn user_agents() -> Vec<String> {
|
||||||
|
|
@ -37,7 +37,7 @@ pub fn user_agents() -> Vec<String> {
|
||||||
pub fn return_random_user_agent(strings: &[String]) -> String {
|
pub fn return_random_user_agent(strings: &[String]) -> String {
|
||||||
let empty_string = String::new();
|
let empty_string = String::new();
|
||||||
strings
|
strings
|
||||||
.choose(&mut thread_rng())
|
.choose(&mut rng())
|
||||||
.unwrap_or(&empty_string)
|
.unwrap_or(&empty_string)
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue