traffic-relay

This commit is contained in:
Stefan Schwarz 2020-09-18 11:08:32 +02:00
commit fcf104a6aa
4 changed files with 1970 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

1865
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

15
Cargo.toml Normal file
View 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
View 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())
}