Я хотел бы создать небольшой прокси-сервер Rust HTTP с использованием hyper, который принимает запросы, перенаправляет их и сбрасывает тело запроса + тело.
Основываясь на этом примере, прокси-часть работает нормально.
Однако я не могу просто скопировать и распечатать тело запроса. Моя основная проблема в том, что тело запроса нельзя просто скопировать во что-то вроде Vec<u8>
. Я не могу deconstruct
запрос прочитать тело, а затем создать его позже, поскольку деконструированные заголовки не могут быть добавлены в новый запрос.
Следующий код показывает мой минимальный пример HTTP-прокси:
extern crate futures;
extern crate hyper;
extern crate tokio_core;
use futures::{Future, Stream};
use hyper::{Body, Client, StatusCode};
use hyper::client::HttpConnector;
use hyper::header::{ContentLength, ContentType};
use hyper::server::{Http, Request, Response, Service};
use tokio_core::reactor::Core;
type HTTPClient = Client<HttpConnector, Body>;
struct Server {
client: HTTPClient,
}
impl Server {
pub fn new(client: HTTPClient) -> Server {
Server { client: client }
}
}
impl Service for Server {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, mut req: Request) -> Self::Future {
let req_uri_str = {
let uri = req.uri();
format!(
"http://localhost{}?{}",
uri.path(),
uri.query().unwrap_or_default()
)
};
req.set_uri(req_uri_str.parse().unwrap());
// Try to create a copy of the new request
/*
let (method, uri, version, headers, body) = req.deconstruct();
let mut req_copy: Request<hyper::Body> = Request::new(method, uri);
// Main problem: How can the request body be copied?
// >>> let body_bytes: Vec<u8> = ...
req_copy.set_body(body);
req_copy.set_version(version);
// Try to copy the headers
for header in headers.iter() {
req_copy.headers_mut().set(header.value().unwrap());
}
*/
// This works if the request is not deconstructed
let work = self.client
.request(req)
.and_then(|res| futures::future::ok(res))
.or_else(|err| {
let body = format!("{}\n", err);
futures::future::ok(
Response::new()
.with_status(StatusCode::BadRequest)
.with_header(ContentType::plaintext())
.with_header(ContentLength(body.len() as u64))
.with_body(body),
)
});
Box::new(work)
}
}
fn main() {
// Create HTTP client core + handles
let mut core = Core::new().unwrap();
let handle = core.handle();
let handle_clone = handle.clone();
// Create HTTP server
let server_addr = "127.0.0.1:9999".parse().unwrap();
let server = Http::new()
.serve_addr_handle(&server_addr, &handle, move || {
Ok(Server::new(Client::new(&handle_clone)))
})
.unwrap();
// Connect HTTP client with server
let handle_clone2 = handle.clone();
handle.spawn(
server
.for_each(move |conn| {
handle_clone2.spawn(conn.map(|_| ()).map_err(|err| println!("Error: {:?}", err)));
Ok(())
})
.map_err(|_| ()),
);
core.run(futures::future::empty::<(), ()>()).unwrap();
}
Это работает нормально, если у вас есть какая-либо HTTP-служба, работающая на порту 80, подключение с помощью браузера к порту 9999 будет идеально пересылать любые ответы и запросы.
Однако, если вы снова включите строки, касающиеся создания нового скопированного запроса, мой подход не удастся, поскольку я не понимаю, как копировать заголовки. (Более того, это мне не очень помогает, когда дело доходит до копирования тела запроса)
Мне известно, что здесь есть похожие вопросы, но ни один из них не соответствует моему требованию повторно использовать тело запроса после его просмотра (или вообще не иметь ответов).