proxy request and readme update
This commit is contained in:
parent
304a14ccc8
commit
64d945b104
46
Cargo.lock
generated
46
Cargo.lock
generated
@ -38,6 +38,12 @@ dependencies = [
|
|||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.6.0"
|
version = "2.6.0"
|
||||||
@ -86,10 +92,17 @@ version = "0.8.20"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ezhttp"
|
name = "ezhttp"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"base64",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
"openssl",
|
"openssl",
|
||||||
@ -100,6 +113,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-io-timeout",
|
"tokio-io-timeout",
|
||||||
"tokio-openssl",
|
"tokio-openssl",
|
||||||
|
"tokio-socks",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -585,6 +599,26 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.65"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.65"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "threadpool"
|
name = "threadpool"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
@ -644,6 +678,18 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-socks"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"futures-util",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.8.0"
|
version = "2.8.0"
|
||||||
|
@ -20,4 +20,6 @@ lazy_static = "1.5.0"
|
|||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
openssl = "0.10.68"
|
openssl = "0.10.68"
|
||||||
tokio-openssl = "0.6.5"
|
tokio-openssl = "0.6.5"
|
||||||
|
tokio-socks = "0.5.2"
|
||||||
|
base64 = "0.22.1"
|
33
README.md
33
README.md
@ -1,5 +1,5 @@
|
|||||||
# EzHttp
|
# EzHttp
|
||||||
Easy http server for small sites
|
Simple http library with client and server
|
||||||
|
|
||||||
This library is under developement, so if you found any bugs, please write them to [Issues](https://github.com/MeexReay/ezhttp/issues)
|
This library is under developement, so if you found any bugs, please write them to [Issues](https://github.com/MeexReay/ezhttp/issues)
|
||||||
|
|
||||||
@ -12,7 +12,27 @@ ezhttp = { git = "https://github.com/MeexReay/ezhttp" } # unstable
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
Hello world example:
|
Client example:
|
||||||
|
```rust
|
||||||
|
use ezhttp::prelude::*;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), HttpError> {
|
||||||
|
let client = HttpClient::builder().build(); // or HttpClient::default()
|
||||||
|
|
||||||
|
let url = URL::from_str("https://google.com")?;
|
||||||
|
let request: HttpRequest = RequestBuilder::get(url).build();
|
||||||
|
|
||||||
|
let response: HttpResponse = client.send(request).await?;
|
||||||
|
|
||||||
|
println!("response status: {}", response.status_code);
|
||||||
|
println!("response body: {} bytes", response.body.as_text().unwrap().len());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Site example:
|
||||||
```rust
|
```rust
|
||||||
use ezhttp::prelude::*;
|
use ezhttp::prelude::*;
|
||||||
|
|
||||||
@ -46,7 +66,14 @@ impl HttpServer for EzSite {
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
start_server(EzSite("Hello World!".to_string()), "localhost:8080").await.expect("http server error");
|
HttpServerStarter::new(
|
||||||
|
EzSite("Hello World!".to_string()),
|
||||||
|
"localhost:8080"
|
||||||
|
).timeout(Some(Duration::from_secs(5)))
|
||||||
|
.threads(5)
|
||||||
|
.start_forever()
|
||||||
|
.await
|
||||||
|
.expect("http server error");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use base64::Engine;
|
||||||
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
||||||
use tokio::net::TcpStream;
|
use tokio::{io::{AsyncReadExt, AsyncWriteExt}, net::TcpStream};
|
||||||
use tokio_openssl::SslStream;
|
use tokio_openssl::SslStream;
|
||||||
|
use tokio_socks::tcp::{Socks4Stream, Socks5Stream};
|
||||||
|
|
||||||
use super::{error::HttpError, gen_multipart_boundary, headers::Headers, prelude::HttpResponse, request::HttpRequest};
|
use super::{error::HttpError, gen_multipart_boundary, headers::Headers, prelude::HttpResponse, request::HttpRequest};
|
||||||
|
|
||||||
|
use base64::prelude::BASE64_STANDARD;
|
||||||
|
|
||||||
pub mod req_builder;
|
pub mod req_builder;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
@ -17,12 +20,8 @@ pub use proxy::*;
|
|||||||
|
|
||||||
|
|
||||||
// TODO: proxy support
|
// TODO: proxy support
|
||||||
async fn send_request(request: HttpRequest, ssl_verify: bool, _proxy: Proxy, headers: Headers) -> Result<HttpResponse, HttpError> {
|
async fn send_request(request: HttpRequest, ssl_verify: bool, proxy: Proxy, headers: Headers) -> Result<HttpResponse, HttpError> {
|
||||||
let mut request = request;
|
let mut request = request.clone();
|
||||||
|
|
||||||
let mut stream = TcpStream::connect(
|
|
||||||
format!("{}:{}", request.url.domain, request.url.port)
|
|
||||||
).await.map_err(|_| HttpError::ConnectError)?;
|
|
||||||
|
|
||||||
for (key, value) in headers.entries() {
|
for (key, value) in headers.entries() {
|
||||||
request.headers.put(key, value);
|
request.headers.put(key, value);
|
||||||
@ -32,35 +31,151 @@ async fn send_request(request: HttpRequest, ssl_verify: bool, _proxy: Proxy, hea
|
|||||||
request.headers.put("Host", request.url.domain.to_string());
|
request.headers.put("Host", request.url.domain.to_string());
|
||||||
request.headers.put("Content-Length", request.body.as_bytes().len().to_string());
|
request.headers.put("Content-Length", request.body.as_bytes().len().to_string());
|
||||||
|
|
||||||
if request.url.scheme == "http" {
|
let site_host = format!("{}:{}", request.url.domain, request.url.port);
|
||||||
request.send(&mut stream).await?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::recv(&mut stream).await?)
|
match proxy {
|
||||||
} else if request.url.scheme == "https" {
|
Proxy::Http { host, auth } => {
|
||||||
let mut ssl_connector = SslConnector::builder(SslMethod::tls())
|
let mut stream = TcpStream::connect(host).await.map_err(|_| HttpError::ConnectError)?;
|
||||||
.map_err(|_| HttpError::SslError)?;
|
|
||||||
|
match auth {
|
||||||
|
Some((user,password)) => stream.write_all(&[
|
||||||
|
b"CONNECT ", site_host.as_bytes(), b" HTTP/1.1\r\n",
|
||||||
|
b"Host: ", site_host.as_bytes(), b"\r\n",
|
||||||
|
b"Proxy-Authorization: basic ", BASE64_STANDARD.encode(format!("{user}:{password}")).as_bytes(), b"\r\n\r\n",
|
||||||
|
].concat()).await,
|
||||||
|
None => stream.write_all(&[
|
||||||
|
b"CONNECT ", site_host.as_bytes(), b" HTTP/1.1\r\n",
|
||||||
|
b"Host: ", site_host.as_bytes(), b"\r\n\r\n",
|
||||||
|
].concat()).await
|
||||||
|
}.map_err(|_| HttpError::ConnectError)?;
|
||||||
|
|
||||||
|
HttpResponse::recv(&mut stream).await.map_err(|_| HttpError::ConnectError)?;
|
||||||
|
|
||||||
|
if request.url.scheme == "http" {
|
||||||
|
request.send(&mut stream).await?;
|
||||||
|
|
||||||
ssl_connector.set_verify(if ssl_verify { SslVerifyMode::PEER } else { SslVerifyMode::NONE });
|
Ok(HttpResponse::recv(&mut stream).await?)
|
||||||
|
} else if request.url.scheme == "https" {
|
||||||
|
let mut wrapper = ssl_wrapper(ssl_verify, request.url.domain.clone(), stream).await?;
|
||||||
|
|
||||||
let ssl_connector = ssl_connector.build();
|
request.send(&mut wrapper).await?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::recv(&mut wrapper).await?)
|
||||||
|
} else {
|
||||||
|
Err(HttpError::UnknownScheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Proxy::Https { host, auth } => {
|
||||||
|
let mut stream = TcpStream::connect(host).await.map_err(|_| HttpError::ConnectError)?;
|
||||||
|
|
||||||
let ssl = ssl_connector
|
match auth {
|
||||||
.configure()
|
Some((user,password)) => stream.write_all(&[
|
||||||
.map_err(|_| HttpError::SslError)?
|
b"CONNECT ", site_host.as_bytes(), b" HTTP/1.1\r\n",
|
||||||
.into_ssl(&request.url.domain)
|
b"Host: ", site_host.as_bytes(), b"\r\n",
|
||||||
.map_err(|_| HttpError::SslError)?;
|
b"Proxy-Authorization: basic ", BASE64_STANDARD.encode(format!("{user}:{password}")).as_bytes(), b"\r\n\r\n",
|
||||||
|
].concat()).await,
|
||||||
|
None => stream.write_all(&[
|
||||||
|
b"CONNECT ", site_host.as_bytes(), b" HTTP/1.1\r\n",
|
||||||
|
b"Host: ", site_host.as_bytes(), b"\r\n\r\n",
|
||||||
|
].concat()).await
|
||||||
|
}.map_err(|_| HttpError::ConnectError)?;
|
||||||
|
|
||||||
let mut wrapper = SslStream::new(ssl, stream)
|
HttpResponse::recv(&mut stream).await.map_err(|_| HttpError::ConnectError)?;
|
||||||
.map_err(|_| HttpError::SslError)?;
|
|
||||||
|
|
||||||
let mut wrapper = Pin::new(&mut wrapper);
|
if request.url.scheme == "http" {
|
||||||
|
request.send(&mut stream).await?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::recv(&mut stream).await?)
|
||||||
|
} else if request.url.scheme == "https" {
|
||||||
|
let mut wrapper = ssl_wrapper(ssl_verify, request.url.domain.clone(), stream).await?;
|
||||||
|
|
||||||
wrapper.as_mut().connect().await.map_err(|_| HttpError::SslError)?;
|
request.send(&mut wrapper).await?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::recv(&mut wrapper).await?)
|
||||||
|
} else {
|
||||||
|
Err(HttpError::UnknownScheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Proxy::Socks4 { host, user } => {
|
||||||
|
let mut stream = match user {
|
||||||
|
Some(user) => Socks4Stream::connect_with_userid(host, site_host, &user).await,
|
||||||
|
None => Socks4Stream::connect(host, site_host).await
|
||||||
|
}.map_err(|_| HttpError::ConnectError)?;
|
||||||
|
|
||||||
request.send(&mut wrapper).await?;
|
if request.url.scheme == "http" {
|
||||||
|
request.send(&mut stream).await?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::recv(&mut stream).await?)
|
||||||
|
} else if request.url.scheme == "https" {
|
||||||
|
let mut wrapper = ssl_wrapper(ssl_verify, request.url.domain.clone(), stream).await?;
|
||||||
|
|
||||||
Ok(HttpResponse::recv(&mut wrapper).await?)
|
request.send(&mut wrapper).await?;
|
||||||
} else {
|
|
||||||
Err(HttpError::UnknownScheme)
|
Ok(HttpResponse::recv(&mut wrapper).await?)
|
||||||
|
} else {
|
||||||
|
Err(HttpError::UnknownScheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Proxy::Socks5 { host, auth } => {
|
||||||
|
let mut stream = match auth {
|
||||||
|
Some(auth) => Socks5Stream::connect_with_password(host, site_host, &auth.0, &auth.1).await,
|
||||||
|
None => Socks5Stream::connect(host, site_host).await
|
||||||
|
}.map_err(|_| HttpError::ConnectError)?;
|
||||||
|
|
||||||
|
if request.url.scheme == "http" {
|
||||||
|
request.send(&mut stream).await?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::recv(&mut stream).await?)
|
||||||
|
} else if request.url.scheme == "https" {
|
||||||
|
let mut wrapper = ssl_wrapper(ssl_verify, request.url.domain.clone(), stream).await?;
|
||||||
|
|
||||||
|
request.send(&mut wrapper).await?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::recv(&mut wrapper).await?)
|
||||||
|
} else {
|
||||||
|
Err(HttpError::UnknownScheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Proxy::None => {
|
||||||
|
let mut stream = TcpStream::connect(site_host).await.map_err(|_| HttpError::ConnectError)?;
|
||||||
|
|
||||||
|
if request.url.scheme == "http" {
|
||||||
|
request.send(&mut stream).await?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::recv(&mut stream).await?)
|
||||||
|
} else if request.url.scheme == "https" {
|
||||||
|
let mut wrapper = ssl_wrapper(ssl_verify, request.url.domain.clone(), stream).await?;
|
||||||
|
|
||||||
|
request.send(&mut wrapper).await?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::recv(&mut wrapper).await?)
|
||||||
|
} else {
|
||||||
|
Err(HttpError::UnknownScheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ssl_wrapper<S: AsyncReadExt + AsyncWriteExt>(ssl_verify: bool, domain: String, stream: S) -> Result<Pin<Box<SslStream<S>>>, HttpError> {
|
||||||
|
let mut ssl_connector = SslConnector::builder(SslMethod::tls())
|
||||||
|
.map_err(|_| HttpError::SslError)?;
|
||||||
|
|
||||||
|
ssl_connector.set_verify(if ssl_verify { SslVerifyMode::PEER } else { SslVerifyMode::NONE });
|
||||||
|
|
||||||
|
let ssl_connector = ssl_connector.build();
|
||||||
|
|
||||||
|
let ssl = ssl_connector
|
||||||
|
.configure()
|
||||||
|
.map_err(|_| HttpError::SslError)?
|
||||||
|
.into_ssl(&domain)
|
||||||
|
.map_err(|_| HttpError::SslError)?;
|
||||||
|
|
||||||
|
let wrapper = SslStream::new(ssl, stream)
|
||||||
|
.map_err(|_| HttpError::SslError)?;
|
||||||
|
|
||||||
|
let mut wrapper = Box::pin(wrapper);
|
||||||
|
|
||||||
|
wrapper.as_mut().connect().await.map_err(|_| HttpError::SslError)?;
|
||||||
|
|
||||||
|
Ok(wrapper)
|
||||||
}
|
}
|
@ -4,7 +4,7 @@ use std::net::{ToSocketAddrs, SocketAddr};
|
|||||||
pub enum Proxy {
|
pub enum Proxy {
|
||||||
None,
|
None,
|
||||||
Socks5 { host: SocketAddr, auth: Option<(String, String)> },
|
Socks5 { host: SocketAddr, auth: Option<(String, String)> },
|
||||||
Socks4 { host: SocketAddr, user: String },
|
Socks4 { host: SocketAddr, user: Option<String> },
|
||||||
Http { host: SocketAddr, auth: Option<(String, String)> },
|
Http { host: SocketAddr, auth: Option<(String, String)> },
|
||||||
Https { host: SocketAddr, auth: Option<(String, String)> },
|
Https { host: SocketAddr, auth: Option<(String, String)> },
|
||||||
}
|
}
|
||||||
@ -22,8 +22,12 @@ impl Proxy {
|
|||||||
Self::Socks5 { host: host.to_socket_addrs().unwrap().next().unwrap(), auth: Some((user, password)) }
|
Self::Socks5 { host: host.to_socket_addrs().unwrap().next().unwrap(), auth: Some((user, password)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn socks4(host: impl ToSocketAddrs, user_id: String) -> Self {
|
pub fn socks4(host: impl ToSocketAddrs) -> Self {
|
||||||
Self::Socks4 { host: host.to_socket_addrs().unwrap().next().unwrap(), user: user_id }
|
Self::Socks4 { host: host.to_socket_addrs().unwrap().next().unwrap(), user: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn socks4_with_auth(host: impl ToSocketAddrs, user_id: String) -> Self {
|
||||||
|
Self::Socks4 { host: host.to_socket_addrs().unwrap().next().unwrap(), user: Some(user_id) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn http(host: impl ToSocketAddrs) -> Self {
|
pub fn http(host: impl ToSocketAddrs) -> Self {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user