如何解析URL参数为HashMap?基于Warp/Hyper实现HTTP/HTTPS转发代理
实现HTTP/HTTPS请求转发代理(基于Rust)
你需要实现的代理核心是直接复用原始请求的路径和查询参数转发到目标服务C,无需解析参数即可保证性能,以下是基于warp和Hyper两种框架的具体实现方案:
方案一:使用warp框架(声明式路由)
warp的路由系统可以轻松捕获请求路径与查询参数,结合Hyper客户端完成转发:
1. 配置依赖
在Cargo.toml中添加:
[dependencies] warp = "0.3" hyper = { version = "1.0", features = ["client", "http1"] } hyper-tls = "0.6" # 若需要转发HTTPS请求 tokio = { version = "1.0", features = ["full"] } url = "2.0"
2. 代码实现
use hyper::{Client, Request, Uri}; use warp::{Filter, Reply}; use std::convert::Infallible; // 目标服务C的基础地址 const TARGET_BASE: &str = "https://c.com"; #[tokio::main] async fn main() { // 捕获任意路径和查询参数 let proxy_route = warp::any() .and(warp::path::full()) .and(warp::query::raw()) .and_then(|path: warp::path::FullPath, query: String| async move { // 构造目标URI:基础地址 + 路径 + 查询参数 let target_uri_str = if query.is_empty() { format!("{}{}", TARGET_BASE, path.as_str()) } else { format!("{}{}?{}", TARGET_BASE, path.as_str(), query) }; // 解析目标URI let target_uri: Uri = target_uri_str.parse().map_err(|e| { eprintln!("Invalid target URI: {}", e); warp::reject::custom(ProxyError::UriParse) })?; // 获取原始请求并构造转发请求 let req = warp::request::request().await?; let forward_req = Request::builder() .uri(target_uri) .method(req.method().clone()) .headers(req.headers().clone()) .body(req.into_body()) .map_err(|e| { eprintln!("Failed to build forward request: {}", e); warp::reject::custom(ProxyError::RequestBuild) })?; // 发送请求并返回响应 let client = Client::builder().build::<_, hyper::Body>(hyper_tls::HttpsConnector::new()); let resp = client.request(forward_req).await.map_err(|e| { eprintln!("Forward request failed: {}", e); warp::reject::custom(ProxyError::Forward) })?; Ok(warp::reply::with_status(resp.into_body(), resp.status())) }) .recover(|_err: warp::Rejection| async move { Ok(warp::reply::with_status("Proxy error", hyper::StatusCode::BAD_GATEWAY)) }); warp::serve(proxy_route).run(([127, 0, 0, 1], 3030)).await; } // 自定义错误类型 #[derive(Debug)] enum ProxyError { UriParse, RequestBuild, Forward, } impl warp::reject::Reject for ProxyError {}
方案二:使用Hyper框架(底层控制)
Hyper是更底层的HTTP库,适合需要高度自定义的场景:
1. 配置依赖
[dependencies] hyper = { version = "1.0", features = ["full"] } hyper-tls = "0.6" tokio = { version = "1.0", features = ["full"] } url = "2.0"
2. 代码实现
use hyper::{Body, Client, Request, Response, Server, Uri}; use hyper::service::{make_service_fn, service_fn}; use std::convert::Infallible; const TARGET_BASE: &str = "https://c.com"; async fn handle_proxy(req: Request<Body>) -> Result<Response<Body>, Infallible> { // 提取原始请求的路径和查询参数 let path_and_query = req.uri() .path_and_query() .map(|pq| pq.as_str()) .unwrap_or(""); // 构造目标URI let target_uri_str = format!("{}{}", TARGET_BASE, path_and_query); let target_uri: Uri = match target_uri_str.parse() { Ok(uri) => uri, Err(e) => { eprintln!("Invalid target URI: {}", e); return Ok(Response::builder() .status(hyper::StatusCode::BAD_GATEWAY) .body(Body::from("Invalid target URI")) .unwrap()); } }; // 构造转发请求并发送 let forward_req = Request::builder() .uri(target_uri) .method(req.method().clone()) .headers(req.headers().clone()) .body(req.into_body()) .unwrap(); let client = Client::builder().build::<_, Body>(hyper_tls::HttpsConnector::new()); match client.request(forward_req).await { Ok(resp) => Ok(resp), Err(e) => { eprintln!("Forward request failed: {}", e); Ok(Response::builder() .status(hyper::StatusCode::BAD_GATEWAY) .body(Body::from("Forward request failed")) .unwrap()) } } } #[tokio::main] async fn main() { let addr = ([127, 0, 0, 1], 3030).into(); let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle_proxy)) }); let server = Server::bind(&addr).serve(make_svc); if let Err(e) = server.await { eprintln!("Server error: {}", e); } }
额外说明
- 性能优化:直接复用原始路径和查询字符串(如代码中的
path_and_query或query: String)比解析成HashMap再重新拼接更高效,无需额外序列化/反序列化开销。 - 解析为HashMap:如果确实需要解析参数,可使用
url::Url库:let url = url::Url::parse(&format!("http://localhost{}", path_and_query)).unwrap(); let params: std::collections::HashMap<_, _> = url.query_pairs().into_owned().collect(); - HTTPS支持:依赖
hyper-tls提供HTTPS客户端能力,若目标服务是HTTP可移除该依赖。
内容的提问来源于stack exchange,提问作者YNX
相关产品推荐
相关产品推荐

