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