traffic-relay
This commit is contained in:
commit
fcf104a6aa
4 changed files with 1970 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
1865
Cargo.lock
generated
Normal file
1865
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
name = "traffic-relay"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Stefan Schwarz <ssz@bitsbeats.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ansi_term = "0.12"
|
||||||
|
anyhow = "1.0"
|
||||||
|
argh = "0.1.3"
|
||||||
|
async-std = "1.6"
|
||||||
|
openssl = { version = "0.10", features = ["vendored"] }
|
||||||
|
surf = "2.0.0-alpha"
|
89
src/main.rs
Normal file
89
src/main.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use ansi_term::Colour;
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use argh::FromArgs;
|
||||||
|
use async_std::io::{self, BufReader};
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use async_std::sync::Arc;
|
||||||
|
use async_std::task;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
/// traffic-relay
|
||||||
|
#[derive(FromArgs)]
|
||||||
|
struct App {
|
||||||
|
/// url to prepend
|
||||||
|
#[argh(option)]
|
||||||
|
base_url: String,
|
||||||
|
|
||||||
|
/// column in stdin that contains the url
|
||||||
|
#[argh(option)]
|
||||||
|
column: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// App is the application
|
||||||
|
impl App {
|
||||||
|
async fn run(self) -> Result<()> {
|
||||||
|
let downloader = Arc::new(Box::new(Downloader {}));
|
||||||
|
let stdin = io::stdin();
|
||||||
|
let reader = BufReader::new(stdin);
|
||||||
|
let mut lines = reader.lines();
|
||||||
|
while let Some(line) = lines.next().await {
|
||||||
|
let line = line?;
|
||||||
|
let url = match self.parse_line(&line) {
|
||||||
|
Some(url) => url,
|
||||||
|
None => {
|
||||||
|
eprintln!("unable to parse line: {}", &line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let downloader = downloader.clone();
|
||||||
|
task::spawn(async move { downloader.log_download(url).await });
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_line(&self, line: &str) -> Option<String> {
|
||||||
|
let fields = line.split_whitespace();
|
||||||
|
let url = fields.skip(self.column - 1).next()?;
|
||||||
|
Some(format!("{}{}", self.base_url, url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downloader is a wrapper to track a state for downloads
|
||||||
|
struct Downloader {}
|
||||||
|
|
||||||
|
impl Downloader {
|
||||||
|
/// runs download but captures errors to stderr
|
||||||
|
async fn log_download(&self, url: String) {
|
||||||
|
match self.download(url).await {
|
||||||
|
Err(err) => eprintln!("unable to download: {}", err),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// download a file
|
||||||
|
async fn download(&self, url: String) -> Result<()> {
|
||||||
|
let start = Instant::now();
|
||||||
|
let mut res = surf::get(&url).await.map_err(|err| anyhow!(err))?;
|
||||||
|
res.body_bytes().await.map_err(|err| anyhow!(err))?;
|
||||||
|
let status = match res.status() as u32 {
|
||||||
|
200..=399 => Colour::Green.paint(res.status().to_string()),
|
||||||
|
400..=499 => Colour::Yellow.paint(res.status().to_string()),
|
||||||
|
_ => Colour::Red.paint(res.status().to_string()),
|
||||||
|
};
|
||||||
|
let elapsed = start.elapsed().as_secs_f32();
|
||||||
|
let elapsed = if elapsed < 0.2 {
|
||||||
|
Colour::Green.paint(elapsed.to_string())
|
||||||
|
} else if elapsed < 0.5 {
|
||||||
|
Colour::Yellow.paint(elapsed.to_string())
|
||||||
|
} else {
|
||||||
|
Colour::Yellow.paint(elapsed.to_string())
|
||||||
|
};
|
||||||
|
println!("{} {} {}", status, elapsed, url);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let app: App = argh::from_env();
|
||||||
|
task::block_on(app.run())
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue