add http.rs
This commit is contained in:
parent
a9614f934d
commit
312ba329e5
24
Cargo.lock
generated
24
Cargo.lock
generated
@ -526,12 +526,6 @@ version = "2.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
|
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fnv"
|
|
||||||
version = "1.0.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foreign-types"
|
name = "foreign-types"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@ -635,17 +629,6 @@ version = "0.3.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"fnv",
|
|
||||||
"itoa",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
@ -690,7 +673,6 @@ dependencies = [
|
|||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"clap",
|
"clap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"http",
|
|
||||||
"httparse",
|
"httparse",
|
||||||
"log",
|
"log",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
@ -735,12 +717,6 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itoa"
|
|
||||||
version = "1.0.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.69"
|
version = "0.3.69"
|
||||||
|
|||||||
@ -11,7 +11,6 @@ async-std = { version = "1.12.0", features = ["attributes"] }
|
|||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
clap = { version = "4.5.4", features = ["derive"] }
|
clap = { version = "4.5.4", features = ["derive"] }
|
||||||
env_logger = "0.11.3"
|
env_logger = "0.11.3"
|
||||||
http = "1.1.0"
|
|
||||||
httparse = "1.8.0"
|
httparse = "1.8.0"
|
||||||
log = "0.4.21"
|
log = "0.4.21"
|
||||||
native-tls = "0.2.12"
|
native-tls = "0.2.12"
|
||||||
|
|||||||
80
src/agent.rs
80
src/agent.rs
@ -3,6 +3,7 @@ use async_std::net::TcpStream;
|
|||||||
|
|
||||||
use crate::connection::ConnectionBuilder;
|
use crate::connection::ConnectionBuilder;
|
||||||
use crate::error::{Result, Error, BuildError, BuildResult};
|
use crate::error::{Result, Error, BuildError, BuildResult};
|
||||||
|
use crate::http;
|
||||||
|
|
||||||
pub struct AgentBuilder {
|
pub struct AgentBuilder {
|
||||||
filter_url: Option<url::Url>,
|
filter_url: Option<url::Url>,
|
||||||
@ -58,7 +59,7 @@ impl AgentBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(ref url) = self.filter_url {
|
if let Some(ref url) = self.filter_url {
|
||||||
log::info!(target: "builder", "Try downloading rule list from '{}'", url);
|
log::info!(target: "builder", "Try downloading rule list from '{url}'");
|
||||||
let https = native_tls::TlsConnector::new()?;
|
let https = native_tls::TlsConnector::new()?;
|
||||||
|
|
||||||
let client = ureq::AgentBuilder::new()
|
let client = ureq::AgentBuilder::new()
|
||||||
@ -68,9 +69,9 @@ impl AgentBuilder {
|
|||||||
.build();
|
.build();
|
||||||
let resp = client.get(url.as_str()).call()?;
|
let resp = client.get(url.as_str()).call()?;
|
||||||
let text = resp.into_string()?;
|
let text = resp.into_string()?;
|
||||||
let kbs = text.len() as f32 / 1000f32;
|
let len = text.len() as f32 / 1000f32;
|
||||||
|
|
||||||
log::info!(target: "builder", "Successfully downloaded data ({}/kB transmitted)", kbs);
|
log::info!(target: "builder", "Successfully downloaded data ({len}/kB transmitted)");
|
||||||
ruleset = Some(self.build_rules(text)?);
|
ruleset = Some(self.build_rules(text)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,25 +116,17 @@ impl Agent {
|
|||||||
where
|
where
|
||||||
S: Read + Write + Send + Sync + Unpin + 'static
|
S: Read + Write + Send + Sync + Unpin + 'static
|
||||||
{
|
{
|
||||||
let (request, payload) = self.read(&mut conn)?;
|
let request = self.read(&mut conn)?;
|
||||||
|
let host = request.host();
|
||||||
|
|
||||||
let value = request.headers.get("host").unwrap();
|
log::info!("CLIENT --> {host}");
|
||||||
let mut host = value.to_str()?.to_string();
|
|
||||||
|
|
||||||
if ! host.ends_with(char::is_numeric) {
|
if self.check_request_blocked(&request.path) {
|
||||||
// append a port number when without one
|
log::info!("CLIENT --> PROXY --> {host}");
|
||||||
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))?;
|
let mut outbound = self.io(self.builder.connect(&host))?;
|
||||||
|
|
||||||
// forward intercepted request
|
// forward intercepted request
|
||||||
outbound.write_all(&payload).await?;
|
outbound.write_all(request.as_bytes()).await?;
|
||||||
outbound.flush().await?;
|
outbound.flush().await?;
|
||||||
|
|
||||||
log::info!("CLIENT <-> PROXY (connection established)");
|
log::info!("CLIENT <-> PROXY (connection established)");
|
||||||
@ -173,7 +166,7 @@ impl Agent {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read<S>(&self, conn: &mut S) -> Result<(http::request::Parts, Vec<u8>)>
|
fn read<S>(&self, conn: &mut S) -> Result<http::Request>
|
||||||
where
|
where
|
||||||
S: Read + Write + Send + Unpin + 'static
|
S: Read + Write + Send + Unpin + 'static
|
||||||
{
|
{
|
||||||
@ -187,19 +180,21 @@ impl Agent {
|
|||||||
let payload = buf[..offset].to_vec();
|
let payload = buf[..offset].to_vec();
|
||||||
|
|
||||||
let method = match request.method {
|
let method = match request.method {
|
||||||
Some(x) => x,
|
Some(x) => x.parse::<crate::http::Method>().unwrap(),
|
||||||
None => return Err(Error::BadRequest("METHOD".to_string()))
|
None => return Err(Error::BadRequest("METHOD".to_string()))
|
||||||
};
|
};
|
||||||
|
|
||||||
let path = match request.path {
|
let mut path = match request.path {
|
||||||
Some(x) => {
|
Some(x) => x.to_string(),
|
||||||
let mut text = x.to_string();
|
None => return Err(Error::BadRequest("PATH".to_string()))
|
||||||
if text.find("://").is_none() {
|
};
|
||||||
|
|
||||||
|
if path.find("://").is_none() {
|
||||||
// in case of an cannot-be-a-base url
|
// in case of an cannot-be-a-base url
|
||||||
// find a port number, if any
|
// find a port number, if any
|
||||||
let port = text
|
let port = path
|
||||||
.rfind(":")
|
.rfind(":")
|
||||||
.and_then(|x| text.get(x + 1..));
|
.and_then(|x| path.get(x + 1..));
|
||||||
|
|
||||||
let scheme = match port {
|
let scheme = match port {
|
||||||
Some("443") => "https",
|
Some("443") => "https",
|
||||||
@ -207,12 +202,8 @@ impl Agent {
|
|||||||
Some("80") | _ => "http",
|
Some("80") | _ => "http",
|
||||||
};
|
};
|
||||||
|
|
||||||
text = format!("{}://{}", scheme, text);
|
path = format!("{}://{}", scheme, path);
|
||||||
}
|
}
|
||||||
text.parse::<http::Uri>()?
|
|
||||||
},
|
|
||||||
None => return Err(Error::BadRequest("PATH".to_string()))
|
|
||||||
};
|
|
||||||
|
|
||||||
let version = match request.version {
|
let version = match request.version {
|
||||||
Some(3) => http::Version::HTTP_3,
|
Some(3) => http::Version::HTTP_3,
|
||||||
@ -223,21 +214,26 @@ impl Agent {
|
|||||||
None => return Err(Error::BadRequest("VERSION".to_string()))
|
None => return Err(Error::BadRequest("VERSION".to_string()))
|
||||||
};
|
};
|
||||||
|
|
||||||
let (mut parts, _) = http::Request::builder()
|
let mut host = headers.iter()
|
||||||
.method(method)
|
.find_map(|x: _| (x.name == "Host").then_some(x.value))
|
||||||
.uri(path)
|
.map(|x| std::str::from_utf8(x))
|
||||||
.version(version)
|
.ok_or(Error::BadRequest("Host".to_string()))??
|
||||||
.body(())?
|
.to_string();
|
||||||
.into_parts();
|
|
||||||
|
|
||||||
for (k, v) in headers.map(|x: _| (x.name, x.value)) {
|
if host.find(":").is_none() {
|
||||||
if k.is_empty() { break }
|
// append a port number when without one
|
||||||
let key = k.parse::<http::HeaderName>()?;
|
host += ":80";
|
||||||
let value = std::str::from_utf8(v)?.parse::<http::HeaderValue>()?;
|
|
||||||
parts.headers.insert(key, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((parts, payload))
|
let request = crate::http::Request {
|
||||||
|
method,
|
||||||
|
path,
|
||||||
|
version,
|
||||||
|
host,
|
||||||
|
payload: payload.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_request_blocked(&self, url: &str) -> bool {
|
fn check_request_blocked(&self, url: &str) -> bool {
|
||||||
|
|||||||
@ -30,11 +30,6 @@ impl_error! {
|
|||||||
BadRequest("Missing part '{}'"),
|
BadRequest("Missing part '{}'"),
|
||||||
Io(std::io::Error),
|
Io(std::io::Error),
|
||||||
Parse(httparse::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),
|
Utf8(std::str::Utf8Error),
|
||||||
Timeout(async_std::future::TimeoutError),
|
Timeout(async_std::future::TimeoutError),
|
||||||
}
|
}
|
||||||
|
|||||||
74
src/http.rs
Normal file
74
src/http.rs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
macro_rules! impl_http {
|
||||||
|
(pub struct $name:ident($inner:ident) { $($variant:ident = $value:literal,)* }) => {
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct $name($inner);
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
enum $inner{ $($variant,)* }
|
||||||
|
|
||||||
|
impl $name {
|
||||||
|
$(
|
||||||
|
pub const $variant: Self = Self($inner::$variant);
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
impl ToString for $name {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self.0 {
|
||||||
|
$($inner::$variant => $value.to_string(),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::str::FromStr for $name {
|
||||||
|
type Err = ();
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
$($value => Ok(Self::$variant),)*
|
||||||
|
_ => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_http! {
|
||||||
|
pub struct Method(InnerMethod) {
|
||||||
|
OPTIONS = "OPTIONS",
|
||||||
|
GET = "GET",
|
||||||
|
POST = "POST",
|
||||||
|
PUT = "PUT",
|
||||||
|
DELETE = "DELETE",
|
||||||
|
HEAD = "HEAD",
|
||||||
|
TRACE = "TRACE",
|
||||||
|
CONNECT = "CONNECT",
|
||||||
|
PATCH = "PATCH",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_http! {
|
||||||
|
pub struct Version(InnerVersion) {
|
||||||
|
HTTP_09 = "HTTP/0.9",
|
||||||
|
HTTP_10 = "HTTP/1.0",
|
||||||
|
HTTP_11 = "HTTP/1.1",
|
||||||
|
HTTP_2 = "HTTP/2.0",
|
||||||
|
HTTP_3 = "HTTP/3.0",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Request {
|
||||||
|
pub method: Method,
|
||||||
|
pub path: String,
|
||||||
|
pub version: Version,
|
||||||
|
pub(crate) host: String,
|
||||||
|
pub(crate) payload: Box<[u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Request {
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
&self.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn host(&self) -> &str {
|
||||||
|
&self.host
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
|
mod http;
|
||||||
pub mod agent;
|
pub mod agent;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod connection;
|
pub mod connection;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user