initial commit
This commit is contained in:
commit
a9614f934d
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
1590
Cargo.lock
generated
Normal file
1590
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
Cargo.toml
Normal file
21
Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "imposter"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
adblock = "0.8.10"
|
||||||
|
async-compat = "0.2.4"
|
||||||
|
async-io = "2.3.3"
|
||||||
|
async-std = { version = "1.12.0", features = ["attributes"] }
|
||||||
|
base64 = "0.22.1"
|
||||||
|
clap = { version = "4.5.4", features = ["derive"] }
|
||||||
|
env_logger = "0.11.3"
|
||||||
|
http = "1.1.0"
|
||||||
|
httparse = "1.8.0"
|
||||||
|
log = "0.4.21"
|
||||||
|
native-tls = "0.2.12"
|
||||||
|
socks = "0.3.4"
|
||||||
|
tokio = { version = "1.38.0", features = ["io-util"] }
|
||||||
|
ureq = { version = "2.9.7", default-features = false, features = ["native-tls"] }
|
||||||
|
url = "2.5.0"
|
||||||
9
build.rs
Normal file
9
build.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
fn main() {
|
||||||
|
aarch64_windows_linker_setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn aarch64_windows_linker_setup() {
|
||||||
|
println!(r"cargo:rustc-link-search=/mnt/c/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/arm64/");
|
||||||
|
println!(r"cargo:rustc-link-search=/mnt/c/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/ucrt/arm64");
|
||||||
|
println!(r"cargo:rustc-link-search=/mnt/d/Program Files/Microsoft Visual Studio/2022/BuildTools/VC/Tools/MSVC/14.40.33807/lib/arm64/");
|
||||||
|
}
|
||||||
267
src/agent.rs
Normal file
267
src/agent.rs
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
use async_std::io::{Read, Write, ReadExt, WriteExt};
|
||||||
|
use async_std::net::TcpStream;
|
||||||
|
|
||||||
|
use crate::connection::ConnectionBuilder;
|
||||||
|
use crate::error::{Result, Error, BuildError, BuildResult};
|
||||||
|
|
||||||
|
pub struct AgentBuilder {
|
||||||
|
filter_url: Option<url::Url>,
|
||||||
|
buf_size: Option<usize>,
|
||||||
|
timeout: Option<u64>,
|
||||||
|
decode: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AgentBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
filter_url: None,
|
||||||
|
buf_size: None,
|
||||||
|
timeout: None,
|
||||||
|
decode: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn filter(mut self, url: url::Url) -> Self {
|
||||||
|
let _ = self.filter_url.insert(url);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn timeout(mut self, timeout: u64) -> Self {
|
||||||
|
let _ = self.timeout.insert(timeout);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buffer(mut self, size: usize) -> Self {
|
||||||
|
let _ = self.buf_size.insert(size);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode(mut self, decode: bool) -> Self {
|
||||||
|
self.decode = decode;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self, remote: url::Url) -> BuildResult<Agent> {
|
||||||
|
use ConnectionBuilder as CB;
|
||||||
|
let builder = match remote.scheme() {
|
||||||
|
"http" | "" => CB::Http(remote.authority().to_string()),
|
||||||
|
"socks" | "socks5" => CB::Socks5(remote.authority().to_string()),
|
||||||
|
other => return Err(BuildError::Unsupported(other.to_string()))
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut ruleset = None;
|
||||||
|
let time = self.timeout.unwrap_or(u64::MAX);
|
||||||
|
|
||||||
|
let config = AgentConfig {
|
||||||
|
buf_size: self.buf_size.unwrap_or(1024),
|
||||||
|
timeout: std::time::Duration::from_secs(time)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(ref url) = self.filter_url {
|
||||||
|
log::info!(target: "builder", "Try downloading rule list from '{}'", url);
|
||||||
|
let https = native_tls::TlsConnector::new()?;
|
||||||
|
|
||||||
|
let client = ureq::AgentBuilder::new()
|
||||||
|
.proxy(ureq::Proxy::new(remote)?)
|
||||||
|
.tls_connector(https.into())
|
||||||
|
.timeout(config.timeout)
|
||||||
|
.build();
|
||||||
|
let resp = client.get(url.as_str()).call()?;
|
||||||
|
let text = resp.into_string()?;
|
||||||
|
let kbs = text.len() as f32 / 1000f32;
|
||||||
|
|
||||||
|
log::info!(target: "builder", "Successfully downloaded data ({}/kB transmitted)", kbs);
|
||||||
|
ruleset = Some(self.build_rules(text)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Agent { builder, ruleset, config })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_rules(&self, mut text: String) -> BuildResult<adblock::Engine> {
|
||||||
|
if self.decode {
|
||||||
|
log::info!(target: "builder", "Try decoding raw textual data (base64 encoded)");
|
||||||
|
use base64::{Engine, engine::general_purpose::STANDARD};
|
||||||
|
let line = text.split_whitespace().collect::<String>();
|
||||||
|
let decoded = STANDARD.decode(line)?;
|
||||||
|
|
||||||
|
text = String::from_utf8(decoded)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut filters = adblock::FilterSet::new(false);
|
||||||
|
let opts = adblock::lists::ParseOptions::default();
|
||||||
|
filters.add_filter_list(&text, opts);
|
||||||
|
|
||||||
|
log::info!(target: "builder", "Rule data parsed successfully");
|
||||||
|
Ok(adblock::Engine::from_filter_set(filters, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AgentConfig {
|
||||||
|
pub buf_size: usize,
|
||||||
|
pub timeout: std::time::Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Agent {
|
||||||
|
ruleset: Option<adblock::Engine>,
|
||||||
|
builder: ConnectionBuilder,
|
||||||
|
config: AgentConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for Agent {}
|
||||||
|
unsafe impl Sync for Agent {}
|
||||||
|
|
||||||
|
impl Agent {
|
||||||
|
pub async fn handle<S>(&self, mut conn: S) -> Result<()>
|
||||||
|
where
|
||||||
|
S: Read + Write + Send + Sync + Unpin + 'static
|
||||||
|
{
|
||||||
|
let (request, payload) = self.read(&mut conn)?;
|
||||||
|
|
||||||
|
let value = request.headers.get("host").unwrap();
|
||||||
|
let mut host = value.to_str()?.to_string();
|
||||||
|
|
||||||
|
if ! host.ends_with(char::is_numeric) {
|
||||||
|
// append a port number when without one
|
||||||
|
host += ":80";
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("CLIENT --> {} ({}/bit request intercepted)",
|
||||||
|
host, payload.len());
|
||||||
|
|
||||||
|
if self.check_request_blocked(&request.uri.to_string()) {
|
||||||
|
log::info!("CLIENT --> PROXY --> {}", host);
|
||||||
|
let mut outbound = self.io(self.builder.connect(&host))?;
|
||||||
|
|
||||||
|
// forward intercepted request
|
||||||
|
outbound.write_all(&payload).await?;
|
||||||
|
outbound.flush().await?;
|
||||||
|
|
||||||
|
log::info!("CLIENT <-> PROXY (connection established)");
|
||||||
|
self.tunnel(conn, outbound).await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let target = self.io(TcpStream::connect(host))?;
|
||||||
|
log::info!("CLIENT <-> TARGET (direct)");
|
||||||
|
|
||||||
|
if let http::Method::CONNECT = request.method {
|
||||||
|
let resp = b"HTTP/1.1 200 OK\r\n\r\n";
|
||||||
|
// send response to client with code 200 and an EMPTY body
|
||||||
|
conn.write_all(resp).await?;
|
||||||
|
conn.flush().await?;
|
||||||
|
log::debug!("Received CONNECT (200 OK)");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tunnel(conn, target).await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn tunnel<A, B>(&self, mut inbound: A, mut outbound: B) -> Result<()>
|
||||||
|
where
|
||||||
|
A: Read + Write + Send + Sync + Unpin + 'static,
|
||||||
|
B: Read + Write + Send + Sync + Unpin + 'static,
|
||||||
|
{
|
||||||
|
use async_compat::CompatExt;
|
||||||
|
use tokio::io::copy_bidirectional as copy;
|
||||||
|
|
||||||
|
if let Err(e) = copy(
|
||||||
|
&mut outbound.compat_mut(), &mut inbound.compat_mut()).await
|
||||||
|
{
|
||||||
|
log::warn!("{}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read<S>(&self, conn: &mut S) -> Result<(http::request::Parts, Vec<u8>)>
|
||||||
|
where
|
||||||
|
S: Read + Write + Send + Unpin + 'static
|
||||||
|
{
|
||||||
|
let mut headers = [httparse::EMPTY_HEADER; 64];
|
||||||
|
let mut request = httparse::Request::new(&mut headers);
|
||||||
|
|
||||||
|
let mut buf = vec![0; self.config.buf_size];
|
||||||
|
self.io(conn.read(&mut buf))?;
|
||||||
|
|
||||||
|
let offset = request.parse(&buf)?.unwrap();
|
||||||
|
let payload = buf[..offset].to_vec();
|
||||||
|
|
||||||
|
let method = match request.method {
|
||||||
|
Some(x) => x,
|
||||||
|
None => return Err(Error::BadRequest("METHOD".to_string()))
|
||||||
|
};
|
||||||
|
|
||||||
|
let path = match request.path {
|
||||||
|
Some(x) => {
|
||||||
|
let mut text = x.to_string();
|
||||||
|
if text.find("://").is_none() {
|
||||||
|
// in case of an cannot-be-a-base url
|
||||||
|
// find a port number, if any
|
||||||
|
let port = text
|
||||||
|
.rfind(":")
|
||||||
|
.and_then(|x| text.get(x + 1..));
|
||||||
|
|
||||||
|
let scheme = match port {
|
||||||
|
Some("443") => "https",
|
||||||
|
Some("21") => "ftp",
|
||||||
|
Some("80") | _ => "http",
|
||||||
|
};
|
||||||
|
|
||||||
|
text = format!("{}://{}", scheme, text);
|
||||||
|
}
|
||||||
|
text.parse::<http::Uri>()?
|
||||||
|
},
|
||||||
|
None => return Err(Error::BadRequest("PATH".to_string()))
|
||||||
|
};
|
||||||
|
|
||||||
|
let version = match request.version {
|
||||||
|
Some(3) => http::Version::HTTP_3,
|
||||||
|
Some(2) => http::Version::HTTP_2,
|
||||||
|
Some(11) => http::Version::HTTP_11,
|
||||||
|
Some(1) => http::Version::HTTP_10,
|
||||||
|
Some(_) => http::Version::HTTP_09,
|
||||||
|
None => return Err(Error::BadRequest("VERSION".to_string()))
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mut parts, _) = http::Request::builder()
|
||||||
|
.method(method)
|
||||||
|
.uri(path)
|
||||||
|
.version(version)
|
||||||
|
.body(())?
|
||||||
|
.into_parts();
|
||||||
|
|
||||||
|
for (k, v) in headers.map(|x: _| (x.name, x.value)) {
|
||||||
|
if k.is_empty() { break }
|
||||||
|
let key = k.parse::<http::HeaderName>()?;
|
||||||
|
let value = std::str::from_utf8(v)?.parse::<http::HeaderValue>()?;
|
||||||
|
parts.headers.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((parts, payload))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_request_blocked(&self, url: &str) -> bool {
|
||||||
|
let attempt: _ = adblock::request::Request::new(
|
||||||
|
url, url, "fetch"
|
||||||
|
);
|
||||||
|
|
||||||
|
let req = match attempt {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => return true
|
||||||
|
};
|
||||||
|
|
||||||
|
match &self.ruleset {
|
||||||
|
Some(x) => x.check_network_request(&req).matched,
|
||||||
|
None => true // always use tunnel when without rules
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn io<T, F>(&self, f: F) -> Result<T>
|
||||||
|
where
|
||||||
|
F: std::future::Future<Output=std::result::Result<T, std::io::Error>>,
|
||||||
|
{
|
||||||
|
async_std::task::block_on(async {
|
||||||
|
Ok(async_std::io::timeout(self.config.timeout, f).await?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/connection.rs
Normal file
74
src/connection.rs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
use std::net::TcpStream;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use async_io::Async;
|
||||||
|
use socks::Socks5Stream;
|
||||||
|
|
||||||
|
pub enum ConnectionBuilder {
|
||||||
|
Http(String),
|
||||||
|
Socks5(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnectionBuilder {
|
||||||
|
pub async fn connect(&self, target: &str) -> Result<Connection, std::io::Error> {
|
||||||
|
let conn = match self {
|
||||||
|
Self::Http(addr) => {
|
||||||
|
Connection::new(TcpStream::connect(addr)?)
|
||||||
|
},
|
||||||
|
Self::Socks5(addr) => {
|
||||||
|
Connection::new(Socks5Stream::connect(addr, target)?.into_inner())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Connection {
|
||||||
|
inner: Async<TcpStream>
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for Connection {}
|
||||||
|
unsafe impl Sync for Connection {}
|
||||||
|
|
||||||
|
impl Connection {
|
||||||
|
pub fn new(conn: TcpStream) -> Self
|
||||||
|
{
|
||||||
|
Self { inner: Async::new(conn).unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_inner(self) -> Result<TcpStream, std::io::Error> {
|
||||||
|
self.inner.into_inner()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl async_std::io::Read for Connection {
|
||||||
|
fn poll_read(
|
||||||
|
mut self: std::pin::Pin<&mut Self>,
|
||||||
|
ctx: &mut std::task::Context<'_>,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> std::task::Poll<std::io::Result<usize>> {
|
||||||
|
Async::poll_read(Pin::new(&mut self.inner), ctx, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl async_std::io::Write for Connection {
|
||||||
|
fn poll_write(
|
||||||
|
mut self: std::pin::Pin<&mut Self>,
|
||||||
|
ctx: &mut std::task::Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> std::task::Poll<std::io::Result<usize>> {
|
||||||
|
Async::poll_write(Pin::new(&mut self.inner), ctx, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(mut self: std::pin::Pin<&mut Self>, ctx: &mut std::task::Context<'_>)
|
||||||
|
-> std::task::Poll<std::io::Result<()>>
|
||||||
|
{
|
||||||
|
Async::poll_flush(Pin::new(&mut self.inner), ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_close(mut self: std::pin::Pin<&mut Self>, ctx: &mut std::task::Context<'_>)
|
||||||
|
-> std::task::Poll<std::io::Result<()>>
|
||||||
|
{
|
||||||
|
Async::poll_close(Pin::new(&mut self.inner), ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/error.rs
Normal file
58
src/error.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
macro_rules! impl_error {
|
||||||
|
(pub enum $error:ident {
|
||||||
|
$v1:ident($i1:literal), $($variant:ident($inner:path),)* }) =>
|
||||||
|
{
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum $error {
|
||||||
|
$v1(String),
|
||||||
|
$($variant($inner),)*
|
||||||
|
}
|
||||||
|
$(
|
||||||
|
impl From<$inner> for $error {
|
||||||
|
fn from(value: $inner) -> Self {
|
||||||
|
Self::$variant(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
impl std::fmt::Display for $error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
$error::$v1(x) => write!(f, $i1, x),
|
||||||
|
$($error::$variant(e) => e.fmt(f),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_error! {
|
||||||
|
pub enum Error {
|
||||||
|
BadRequest("Missing part '{}'"),
|
||||||
|
Io(std::io::Error),
|
||||||
|
Parse(httparse::Error),
|
||||||
|
Http(http::Error),
|
||||||
|
Uri(http::uri::InvalidUri),
|
||||||
|
HeaderName(http::header::InvalidHeaderName),
|
||||||
|
HeaderValue(http::header::InvalidHeaderValue),
|
||||||
|
ToStr(http::header::ToStrError),
|
||||||
|
Utf8(std::str::Utf8Error),
|
||||||
|
Timeout(async_std::future::TimeoutError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
|
impl_error! {
|
||||||
|
pub enum BuildError {
|
||||||
|
Unsupported("Unsupported proxy protocol '{}'"),
|
||||||
|
Io(std::io::Error),
|
||||||
|
Client(ureq::Error),
|
||||||
|
Tls(native_tls::Error),
|
||||||
|
Decode(base64::DecodeError),
|
||||||
|
Utf8(std::string::FromUtf8Error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type BuildResult<T> = std::result::Result<T, BuildError>;
|
||||||
|
impl std::error::Error for BuildError {}
|
||||||
62
src/main.rs
Normal file
62
src/main.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
pub mod agent;
|
||||||
|
pub mod error;
|
||||||
|
pub mod connection;
|
||||||
|
pub mod server;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Cli {
|
||||||
|
#[arg(short, long)]
|
||||||
|
port: Option<u16>,
|
||||||
|
|
||||||
|
#[arg(short, long, value_name = "URL")]
|
||||||
|
filter_url: Option<url::Url>,
|
||||||
|
|
||||||
|
#[arg(long, value_name = "SIZE")]
|
||||||
|
buf_size: Option<usize>,
|
||||||
|
|
||||||
|
#[arg(short, long, value_name = "SEC")]
|
||||||
|
timeout: Option<u64>,
|
||||||
|
|
||||||
|
#[arg(value_name = "URL")]
|
||||||
|
remote: url::Url
|
||||||
|
}
|
||||||
|
|
||||||
|
const URL: &str = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt";
|
||||||
|
const LOCALHOST: &str = "127.0.0.1";
|
||||||
|
const PORT: u16 = 9000;
|
||||||
|
const BUF_SIZE:usize = 1024;
|
||||||
|
const TIMEOUT: u64 = 15;
|
||||||
|
|
||||||
|
async fn try_launch(agent: Result<agent::Agent, error::BuildError>,
|
||||||
|
server: server::Server) -> Result<(), Box<dyn std::error::Error>>
|
||||||
|
{
|
||||||
|
Ok(server.run(agent?).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if std::env::var("RUST_LOG").ok().is_none() {
|
||||||
|
std::env::set_var("RUST_LOG", "info");
|
||||||
|
}
|
||||||
|
|
||||||
|
let cli = Cli::parse();
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
let port = cli.port.unwrap_or(PORT);
|
||||||
|
let server = server::Server::bind((LOCALHOST, port));
|
||||||
|
|
||||||
|
let agent = agent::AgentBuilder::new()
|
||||||
|
.buffer(cli.buf_size.unwrap_or(BUF_SIZE))
|
||||||
|
.filter(cli.filter_url.unwrap_or(URL.parse().unwrap()))
|
||||||
|
.timeout(cli.timeout.unwrap_or(TIMEOUT))
|
||||||
|
.build(cli.remote);
|
||||||
|
|
||||||
|
if let Err(e) = async_std::task::block_on(
|
||||||
|
try_launch(agent, server))
|
||||||
|
{
|
||||||
|
eprintln!("Error: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/server.rs
Normal file
42
src/server.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Server {
|
||||||
|
addrs: std::net::SocketAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Server {
|
||||||
|
pub async fn run(self, agent: crate::agent::Agent) -> Result<(), std::io::Error> {
|
||||||
|
let listener = async_std::net::TcpListener::bind(self.addrs).await?;
|
||||||
|
let agent = Arc::new(agent);
|
||||||
|
|
||||||
|
log::info!("IMPOSTER/0.1 HTTP SERVER");
|
||||||
|
log::info!("Server listening at {}", self.addrs);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let agent = agent.clone();
|
||||||
|
let (inbound, addr) = listener.accept().await?;
|
||||||
|
|
||||||
|
log::info!("*** Incoming connection from {}", addr);
|
||||||
|
|
||||||
|
async_std::task::spawn(async move {
|
||||||
|
if let Err(e) = agent.handle(inbound).await {
|
||||||
|
log::error!("Agent: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind<A>(addrs: A) -> Self
|
||||||
|
where
|
||||||
|
A: std::net::ToSocketAddrs
|
||||||
|
{
|
||||||
|
let addrs = addrs.to_socket_addrs()
|
||||||
|
.expect("Bind Error")
|
||||||
|
.collect::<Vec<std::net::SocketAddr>>()
|
||||||
|
.pop()
|
||||||
|
.expect("Bind Error");
|
||||||
|
|
||||||
|
Self { addrs }
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user