Skip to content

Instantly share code, notes, and snippets.

@suconghou
Created November 6, 2021 14:53
Show Gist options
  • Save suconghou/c853f65f6c5c8b6d14b4f416f02f38a9 to your computer and use it in GitHub Desktop.
Save suconghou/c853f65f6c5c8b6d14b4f416f02f38a9 to your computer and use it in GitHub Desktop.
actix-web
use actix_web::client::{Client, ClientRequest};
use actix_web::{get, web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder};
use core::time::Duration;
use std::error;
use std::io;
const EXPOSE_HEADERS: [&str; 7] = [
"accept-ranges",
"content-range",
"content-length",
"content-type",
"content-encoding",
"last-modified",
"etag",
];
const FWD_HEADERS: [&str; 9] = [
"user-agent",
"accept",
"accept-encoding",
"accept-language",
"if-modified-since",
"if-none-match",
"range",
"content-length",
"content-type",
];
#[get("/video/{vid:[\\w\\-]{6,15}}.{ext:(jpg|webp)}")]
async fn image(req: HttpRequest, info: web::Path<(String, String)>) -> impl Responder {
let info = info.into_inner();
proxy_image(req, &info.0, &info.1).await
}
pub async fn proxy_image(req: HttpRequest, vid: &String, ext: &String) -> impl Responder {
// if we change https to http , then memory leak gone
let url = match ext.as_str() {
"jpg" => format!("https://r.suconghou.cn/video/{}.{}", vid, ext),
_ => format!("https://r.suconghou.cn/video/{}.{}", vid, ext),
};
proxy(
req,
url,
10,
Box::new(io::Error::new(io::ErrorKind::Other, "")),
)
.await
}
async fn proxy(
req: HttpRequest,
url: String,
timeout: u64,
err: Box<dyn error::Error>,
) -> impl Responder {
if url == "" {
return HttpResponse::InternalServerError()
.body(format!("{:?}", err))
.await;
}
let mut forwarded_req = request(url, timeout);
let r = req.headers();
for item in &FWD_HEADERS {
if r.contains_key(*item) {
forwarded_req = forwarded_req.set_header(*item, r.get(*item).unwrap().clone());
}
}
forwarded_req.send().await.map_err(Error::from).map(|res| {
let status = res.status();
let mut client_resp = HttpResponse::build(status);
for (header_name, header_value) in res
.headers()
.iter()
.filter(|(h, _)| EXPOSE_HEADERS.contains(&h.as_str()))
{
client_resp.set_header(header_name.clone(), header_value.clone());
}
client_resp.streaming(res)
})
}
fn request(url: String, timeout: u64) -> ClientRequest {
let client = Client::builder()
.timeout(Duration::from_secs(timeout))
.no_default_headers()
.max_redirects(3)
.initial_window_size(524288)
.initial_connection_window_size(524288)
.finish();
client
.get(url)
.no_decompress()
.timeout(Duration::from_secs(timeout))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(image))
.bind("0.0.0.0:8877")?
.run()
.await
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment