mirror of
https://codeberg.org/gigirassy/image-proxy/
synced 2026-08-30 15:37:41 +00:00
Add src/main.rs
This commit is contained in:
+138
@@ -0,0 +1,138 @@
|
|||||||
|
use bytes::Bytes;
|
||||||
|
use http_body_util::Full;
|
||||||
|
use hyper::body::Incoming;
|
||||||
|
use hyper::server::conn::http1;
|
||||||
|
use hyper::service::service_fn;
|
||||||
|
use hyper::{Method, Request, Response, StatusCode};
|
||||||
|
use hyper_util::rt::TokioIo;
|
||||||
|
use mimalloc::MiMalloc;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
|
#[global_allocator]
|
||||||
|
static GLOBAL: MiMalloc = MiMalloc;
|
||||||
|
|
||||||
|
async fn handle_http(req: Request<Incoming>) -> Result<Response<Full<Bytes>>, hyper::Error> {
|
||||||
|
if req.method() != Method::GET {
|
||||||
|
return Ok(Response::builder()
|
||||||
|
.status(StatusCode::METHOD_NOT_ALLOWED)
|
||||||
|
.body(Full::new(Bytes::from("Only GET supported")))
|
||||||
|
.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = match req.uri().to_string().parse::<reqwest::Url>() {
|
||||||
|
Ok(u) => u,
|
||||||
|
Err(_) => {
|
||||||
|
return Ok(Response::builder()
|
||||||
|
.status(StatusCode::BAD_REQUEST)
|
||||||
|
.body(Full::new(Bytes::from("Invalid URL")))
|
||||||
|
.unwrap())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let resp = match client.get(url).send().await {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(_) => {
|
||||||
|
return Ok(Response::builder()
|
||||||
|
.status(StatusCode::BAD_GATEWAY)
|
||||||
|
.body(Full::new(Bytes::from("Upstream error")))
|
||||||
|
.unwrap())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let headers = resp.headers().clone();
|
||||||
|
let content_type = headers
|
||||||
|
.get(reqwest::header::CONTENT_TYPE)
|
||||||
|
.and_then(|v| v.to_str().ok())
|
||||||
|
.unwrap_or("");
|
||||||
|
|
||||||
|
let bytes = match resp.bytes().await {
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(_) => {
|
||||||
|
return Ok(Response::builder()
|
||||||
|
.status(StatusCode::BAD_GATEWAY)
|
||||||
|
.body(Full::new(Bytes::from("Read error")))
|
||||||
|
.unwrap())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only compress images
|
||||||
|
if content_type.starts_with("image/") {
|
||||||
|
if let Ok(img) = image::load_from_memory(&bytes) {
|
||||||
|
let mut out = Vec::new();
|
||||||
|
|
||||||
|
match content_type {
|
||||||
|
ct if ct.contains("jpeg") || ct.contains("jpg") => {
|
||||||
|
let _ = img.write_to(
|
||||||
|
&mut std::io::Cursor::new(&mut out),
|
||||||
|
image::ImageFormat::Jpeg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ct if ct.contains("png") => {
|
||||||
|
let _ = img.write_to(
|
||||||
|
&mut std::io::Cursor::new(&mut out),
|
||||||
|
image::ImageFormat::Png,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
out = bytes.to_vec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(Response::builder()
|
||||||
|
.header("content-type", content_type)
|
||||||
|
.body(Full::new(Bytes::from(out)))
|
||||||
|
.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Response::builder()
|
||||||
|
.body(Full::new(bytes))
|
||||||
|
.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONNECT tunneling (HTTPS_PROXY support, but no inspection)
|
||||||
|
async fn tunnel(mut client: tokio::net::TcpStream, host: String) {
|
||||||
|
match tokio::net::TcpStream::connect(host).await {
|
||||||
|
Ok(mut server) => {
|
||||||
|
let (mut cr, mut cw) = client.split();
|
||||||
|
let (mut sr, mut sw) = server.split();
|
||||||
|
|
||||||
|
let client_to_server = tokio::io::copy(&mut cr, &mut sw);
|
||||||
|
let server_to_client = tokio::io::copy(&mut sr, &mut cw);
|
||||||
|
|
||||||
|
let _ = tokio::join!(client_to_server, server_to_client);
|
||||||
|
}
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
let addr: SocketAddr = "0.0.0.0:8544".parse()?;
|
||||||
|
let listener = TcpListener::bind(addr).await?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let (stream, _) = listener.accept().await?;
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let io = TokioIo::new(stream);
|
||||||
|
|
||||||
|
let svc = service_fn(|req: Request<Incoming>| async move {
|
||||||
|
if req.method() == Method::CONNECT {
|
||||||
|
// HTTPS tunneling (no compression possible here)
|
||||||
|
return Ok::<_, hyper::Error>(
|
||||||
|
Response::builder()
|
||||||
|
.status(200)
|
||||||
|
.body(Full::new(Bytes::new()))
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_http(req).await
|
||||||
|
});
|
||||||
|
|
||||||
|
let _ = http1::Builder::new().serve_connection(io, svc).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user