Если вы хотите создать высокопроизводительный и безопасный HTTP-прокси-сервер, Rust с крейтами hyper и tokio - это мощная комбинация для начала работы. Мощные функции безопасности памяти и параллелизма Rust делают его идеальным для создания надежных сетевых приложений, в то время как hyper предоставляет гибкий и простой в использовании интерфейс для создания HTTP-серверов и клиентов. tokio - это среда выполнения для написания надежных, асинхронных и компактных приложений с высокой пропускной способностью ввода-вывода.
В этой статье мы рассмотрим, как создать простой HTTP прокси-сервер в Rust с помощью hyper и tokio. Мы рассмотрим основы построения HTTP-сервера с помощью hyper, отправки запросов на URL с помощью tokio и переадресации входящих запросов на URL. К концу этого руководства у вас будет прочная основа для создания высокопроизводительных HTTP-прокси-серверов с помощью Rust, hyper и tokio.
cargo new http-proxy-server
cd http-proxy-server
echo 'hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }
http = "0.2"' >> Cargo.toml
cargo update
Далее мы определим простой прокси-сервер, который прослушивает указанный порт и перенаправляет входящие запросы на нужные URL. Наш сервер будет использовать метод make_service_fn
из модуля hyper::service
для создания новой службы, которая обрабатывает входящие запросы и передает их прокси-функции для обработчики:
#![deny(warnings)]
use std::convert::Infallible;
use std::net::SocketAddr;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Client, Request, Response, Server};
type HttpClient = Client<hyper::client::HttpConnector>;
#[tokio::main]
async fn main() {
let addr = SocketAddr::from(([127, 0, 0, 1], 8100));
let client = Client::builder()
.http1_title_case_headers(true)
.http1_preserve_header_case(true)
.build_http();
let make_service = make_service_fn(move |_| {
let client = client.clone();
async move { Ok::<_, Infallible>(service_fn(move |req| proxy(client.clone(), req))) }
});
let server = Server::bind(&addr)
.http1_preserve_header_case(true)
.http1_title_case_headers(true)
.serve(make_service);
println!("Прослушивание http://{}", addr);
if let Err(e) = server.await {
eprintln!("ошибка сервера: {}", e);
}
}
Наша основная функция настраивает новый HTTP-сервер, который прослушивает указанный адрес сокета. Мы используем структуру Client
из hyper crate
для создания HTTP-клиента, который мы будем использовать для пересылки запросов на URL.
Функция make_service_fn
создает новую службу, которая обрабатывает входящие запросы и передает их прокси-функции для обработки. Мы используем синтаксис async/await
от Rust для определения асинхронной функции, которая возвращает результат, содержащий либо допустимую службу, либо ошибку.
Метод Server::bind
создает новый HTTP-сервер, который прослушивает указанный адрес сокета, а метод Server::serve
запускает сервер и обслуживает входящие запросы с использованием указанной службы.
Далее давайте определим нашу прокси-функцию, которая будет обрабатывать входящие запросы и перенаправлять их на роутер URL:
async fn proxy(_client: HttpClient, req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
let headers = req.headers().clone();
println!("заголовки: {:?}", headers);
let path = req.uri().path().to_string();
if path.starts_with("/hello") {
let target_url = "http://127.0.0.1:8000".to_owned();
В функции прокси мы сначала клонируем заголовки входящих запросов и выводим их на консоль. Далее мы извлекаем путь к запросу и проверяем, начинается ли он с /hello
. Если это произойдет, мы создадим новый HTTP-запрос к роутеру URL с помощью функции get_response
и вернем ответ клиенту.
Если путь запроса не начинается с /hello
, мы возвращаем клиенту сообщение об ошибке “маршрут не найден”. Вот обновленная функция прокси-сервера:
async fn proxy(_client: HttpClient, req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
let headers = req.headers().clone();
println!("заголовки: {:?}", headers);
let path = req.uri().path().to_string();
if path.starts_with("/hello") {
let target_url = "http://127.0.0.1:8000".to_owned();
let resp = get_response(_client, req, &target_url, &path).await?;
return Ok(resp);
}
let resp = Response::new(Body::from("извините! маршрут не найден"));
Ok(resp)
}
Функция get_response
создает новый HTTP-запрос к URL и отправляет его с помощью метода client.request
. Затем мы ждем ответа и возвращаем его вызывающему абоненту. Вот обновленная функция get_response
:
async fn get_response(client: HttpClient, req: Request<Body>, target_url: &str, path: &str) -> Result<Response<Body>, hyper::Error> {
let target_url = format!("{}{}", target_url, path);
let headers = req.headers().clone();
let mut request_builder = Request::builder()
.method(req.method())
.uri(target_url)
.body(req.into_body())
.unwrap();
*request_builder.headers_mut() = headers;
let response = client.request(request_builder).await?;
let body = hyper::body::to_bytes(response.into_body()).await?;
let body = String::from_utf8(body.to_vec()).unwrap();
let mut resp = Response::new(Body::from(body));
*resp.status_mut() = http::StatusCode::OK;
Ok(resp)
}
Функция get_response
сначала создает полный URL-адрес путем объединения URL-адреса и пути входящего запроса. Затем мы клонируем заголовки входящих запросов и используем их для создания нового HTTP-запроса с тем же методом, URI и телом, что и у входящего запроса.
Мы используем метод client.request
для отправки нового HTTP-запроса на роутер URL, ожидания ответа и преобразования тела ответа в строку. Наконец, мы создаем новый ответ с тем же телом, что и сам ответ, и возвращаем его вызывающему.
Вот и все! С помощью всего нескольких строк кода мы создали базовый HTTP-прокси-сервер в Rust, который может перенаправлять входящие запросы на URL. Вы можете расширить этот код, чтобы добавить дополнительные функции, такие как кэширование, аутентификация и балансировка нагрузки.
Чтобы запустить этот код, просто сохраните его в файл с именем main.rs
в новом проекте Rust и запустите следующую команду:
cargo run
Это запустит HTTP-прокси-сервер и будет прослушивать входящие запросы на указанный порт. Вы можете протестировать сервер, открыв веб-браузер и перейдя к http://localhost:8100/hello . Сервер должен перенаправить запрос на URL и вернуть ответ браузеру.
Вывод
В этой статье мы рассмотрели, как создать высокопроизводительный HTTP-прокси-сервер в Rust с помощью крейтов hyper и tokio. Мы рассмотрели основы построения HTTP-сервера с помощью hyper, выполнения запросов к роутеру URL с помощью tokio и переадресации входящих запросов на нужный URL.