186 lines
4.7 KiB
Rust
186 lines
4.7 KiB
Rust
use std::error;
|
|
use std::fmt;
|
|
use std::io;
|
|
use std::thread;
|
|
use std::time::Duration;
|
|
use std::{env, sync::Arc};
|
|
|
|
use rustls::{
|
|
Certificate, ClientConfig, RootCertStore, ServerCertVerified, ServerCertVerifier, TLSError,
|
|
};
|
|
use ureq;
|
|
use webpki::DNSNameRef;
|
|
|
|
#[derive(Debug)]
|
|
struct StringError(String);
|
|
|
|
impl error::Error for StringError {}
|
|
|
|
impl fmt::Display for StringError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl From<String> for StringError {
|
|
fn from(source: String) -> Self {
|
|
Self(source)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Error {
|
|
source: Box<dyn error::Error>,
|
|
}
|
|
|
|
impl error::Error for Error {}
|
|
|
|
impl fmt::Display for Error {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", self.source)
|
|
}
|
|
}
|
|
|
|
impl From<StringError> for Error {
|
|
fn from(source: StringError) -> Self {
|
|
Error {
|
|
source: source.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<ureq::Error> for Error {
|
|
fn from(source: ureq::Error) -> Self {
|
|
Error {
|
|
source: source.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<io::Error> for Error {
|
|
fn from(source: io::Error) -> Self {
|
|
Error {
|
|
source: source.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn perform(
|
|
agent: &ureq::Agent,
|
|
method: &str,
|
|
url: &str,
|
|
data: &[u8],
|
|
print_headers: bool,
|
|
) -> Result<(), Error> {
|
|
let req = agent.request(method, url);
|
|
let response = if method == "GET" && data.len() == 0 {
|
|
req.call()?
|
|
} else {
|
|
req.send_bytes(data)?
|
|
};
|
|
if print_headers {
|
|
println!(
|
|
"{} {} {}",
|
|
response.http_version(),
|
|
response.status(),
|
|
response.status_text()
|
|
);
|
|
for h in response.headers_names() {
|
|
println!("{}: {}", h, response.header(&h).unwrap_or_default());
|
|
}
|
|
println!();
|
|
}
|
|
let mut reader = response.into_reader();
|
|
io::copy(&mut reader, &mut io::stdout())?;
|
|
Ok(())
|
|
}
|
|
|
|
struct AcceptAll {}
|
|
|
|
impl ServerCertVerifier for AcceptAll {
|
|
fn verify_server_cert(
|
|
&self,
|
|
_roots: &RootCertStore,
|
|
_presented_certs: &[Certificate],
|
|
_dns_name: DNSNameRef<'_>,
|
|
_ocsp_response: &[u8],
|
|
) -> Result<ServerCertVerified, TLSError> {
|
|
Ok(ServerCertVerified::assertion())
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
match main2() {
|
|
Ok(()) => {}
|
|
Err(e) => {
|
|
eprintln!("{}", e);
|
|
std::process::exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main2() -> Result<(), Error> {
|
|
let mut args = env::args();
|
|
if args.next().is_none() {
|
|
println!(
|
|
r##"Usage: {:#?} url [url ...]
|
|
|
|
-i Include headers when printing response
|
|
-X <method> Use the given request method (GET, POST, etc)
|
|
-d <string> Use the given data as the request body (useful for POST)
|
|
--wait <n> Wait n seconds between requests
|
|
-k Ignore certificate errors
|
|
|
|
Fetch url and copy it to stdout.
|
|
"##,
|
|
env::current_exe()?
|
|
);
|
|
return Ok(());
|
|
}
|
|
env_logger::init();
|
|
let mut builder = ureq::builder()
|
|
.timeout_connect(Duration::from_secs(30))
|
|
.timeout(Duration::from_secs(300));
|
|
let mut nonflags: Vec<String> = vec![];
|
|
|
|
let mut print_headers: bool = false;
|
|
let mut method: String = "GET".into();
|
|
let mut data: Vec<u8> = vec![];
|
|
let mut wait = Duration::new(0, 0);
|
|
while let Some(arg) = args.next() {
|
|
match arg.as_ref() {
|
|
"-i" => print_headers = true,
|
|
"-X" => method = args.next().expect("flag -X requires a value"),
|
|
"-d" => data = args.next().expect("flag -d requires a value").into(),
|
|
"--wait" => {
|
|
let wait_string: String = args.next().expect("flag --wait requires a value").into();
|
|
let wait_seconds: u64 = wait_string.parse().expect("invalid --wait flag");
|
|
wait = Duration::from_secs(wait_seconds);
|
|
}
|
|
"-k" => {
|
|
let mut client_config = ClientConfig::new();
|
|
client_config
|
|
.dangerous()
|
|
.set_certificate_verifier(Arc::new(AcceptAll {}));
|
|
builder = builder.tls_config(Arc::new(client_config));
|
|
}
|
|
arg => {
|
|
if arg.starts_with("-") {
|
|
Err(StringError(format!("unrecognized flag '{}'", arg)))?;
|
|
}
|
|
nonflags.push(arg.to_owned());
|
|
}
|
|
}
|
|
}
|
|
|
|
let agent = builder.build();
|
|
|
|
for (i, url) in nonflags.iter().enumerate() {
|
|
perform(&agent, &method, &url, &data, print_headers)?;
|
|
if i != nonflags.len() - 1 {
|
|
thread::sleep(wait);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|