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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
@ -86,10 +92,17 @@ version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "ezhttp"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"lazy_static",
|
||||
"mime_guess",
|
||||
"openssl",
|
||||
@ -100,6 +113,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-io-timeout",
|
||||
"tokio-openssl",
|
||||
"tokio-socks",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
@ -585,6 +599,26 @@ dependencies = [
|
||||
"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]]
|
||||
name = "threadpool"
|
||||
version = "1.8.1"
|
||||
@ -644,6 +678,18 @@ dependencies = [
|
||||
"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]]
|
||||
name = "unicase"
|
||||
version = "2.8.0"
|
||||
|
@ -21,3 +21,5 @@ rand = "0.8.5"
|
||||
mime_guess = "2.0.5"
|
||||
openssl = "0.10.68"
|
||||
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
|
||||
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)
|
||||
|
||||
@ -12,7 +12,27 @@ ezhttp = { git = "https://github.com/MeexReay/ezhttp" } # unstable
|
||||
|
||||
## 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
|
||||
use ezhttp::prelude::*;
|
||||
|
||||
@ -46,7 +66,14 @@ impl HttpServer for EzSite {
|
||||
|
||||
#[tokio::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 base64::Engine;
|
||||
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::{io::{AsyncReadExt, AsyncWriteExt}, net::TcpStream};
|
||||
use tokio_openssl::SslStream;
|
||||
use tokio_socks::tcp::{Socks4Stream, Socks5Stream};
|
||||
|
||||
use super::{error::HttpError, gen_multipart_boundary, headers::Headers, prelude::HttpResponse, request::HttpRequest};
|
||||
|
||||
use base64::prelude::BASE64_STANDARD;
|
||||
|
||||
pub mod req_builder;
|
||||
pub mod client;
|
||||
@ -17,12 +20,8 @@ pub use proxy::*;
|
||||
|
||||
|
||||
// TODO: proxy support
|
||||
async fn send_request(request: HttpRequest, ssl_verify: bool, _proxy: Proxy, headers: Headers) -> Result<HttpResponse, HttpError> {
|
||||
let mut request = request;
|
||||
|
||||
let mut stream = TcpStream::connect(
|
||||
format!("{}:{}", request.url.domain, request.url.port)
|
||||
).await.map_err(|_| HttpError::ConnectError)?;
|
||||
async fn send_request(request: HttpRequest, ssl_verify: bool, proxy: Proxy, headers: Headers) -> Result<HttpResponse, HttpError> {
|
||||
let mut request = request.clone();
|
||||
|
||||
for (key, value) in headers.entries() {
|
||||
request.headers.put(key, value);
|
||||
@ -32,11 +31,132 @@ async fn send_request(request: HttpRequest, ssl_verify: bool, _proxy: Proxy, hea
|
||||
request.headers.put("Host", request.url.domain.to_string());
|
||||
request.headers.put("Content-Length", request.body.as_bytes().len().to_string());
|
||||
|
||||
let site_host = format!("{}:{}", request.url.domain, request.url.port);
|
||||
|
||||
match proxy {
|
||||
Proxy::Http { host, auth } => {
|
||||
let mut stream = TcpStream::connect(host).await.map_err(|_| HttpError::ConnectError)?;
|
||||
|
||||
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?;
|
||||
|
||||
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::Https { host, auth } => {
|
||||
let mut stream = TcpStream::connect(host).await.map_err(|_| HttpError::ConnectError)?;
|
||||
|
||||
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?;
|
||||
|
||||
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::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)?;
|
||||
|
||||
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::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)?;
|
||||
|
||||
@ -47,20 +167,15 @@ async fn send_request(request: HttpRequest, ssl_verify: bool, _proxy: Proxy, hea
|
||||
let ssl = ssl_connector
|
||||
.configure()
|
||||
.map_err(|_| HttpError::SslError)?
|
||||
.into_ssl(&request.url.domain)
|
||||
.into_ssl(&domain)
|
||||
.map_err(|_| HttpError::SslError)?;
|
||||
|
||||
let mut wrapper = SslStream::new(ssl, stream)
|
||||
let wrapper = SslStream::new(ssl, stream)
|
||||
.map_err(|_| HttpError::SslError)?;
|
||||
|
||||
let mut wrapper = Pin::new(&mut wrapper);
|
||||
let mut wrapper = Box::pin(wrapper);
|
||||
|
||||
wrapper.as_mut().connect().await.map_err(|_| HttpError::SslError)?;
|
||||
|
||||
request.send(&mut wrapper).await?;
|
||||
|
||||
Ok(HttpResponse::recv(&mut wrapper).await?)
|
||||
} else {
|
||||
Err(HttpError::UnknownScheme)
|
||||
}
|
||||
Ok(wrapper)
|
||||
}
|
@ -4,7 +4,7 @@ use std::net::{ToSocketAddrs, SocketAddr};
|
||||
pub enum Proxy {
|
||||
None,
|
||||
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)> },
|
||||
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)) }
|
||||
}
|
||||
|
||||
pub fn socks4(host: impl ToSocketAddrs, user_id: String) -> Self {
|
||||
Self::Socks4 { host: host.to_socket_addrs().unwrap().next().unwrap(), user: user_id }
|
||||
pub fn socks4(host: impl ToSocketAddrs) -> Self {
|
||||
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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user