Rust中用ntex快速实现http代理

banq

Ntex是一个功能强大、实用且速度极快的 Rust 可组合Web服务框架。它是 Rust 中最快的 Web 框架之一,为 Web 服务器开发提供了强大的抽象。

为什么选择 ntex?
以下是我使用 ntex 的主要原因:

  • 性能:Ntex 是 Rust 中最快的 Web 框架之一。
  • 符合人体工程学:Ntex 为 Web 服务器开发提供了强大的抽象。
  • 可组合:Ntex 设计为可组合,允许您使用简单的组件构建复杂的 Web 服务器。
  • 生态系统:Ntex 拥有丰富的中间件、扩展和库生态系统。
  • 内置 http 客户端:Ntex 提供内置的 http 客户端,用于向其他服务器发出请求。
  • 运行时:Ntex 允许您在不同的运行时之间进行选择,包括 tokio和 async-std。

设置项目
让我们首先用 Cargo 创建一个新项目:

cargo new ntex-http-proxy
cd ntex-http-proxy

添加 ntex 作为依赖项:

cargo add ntex --features tokio

从基本的 http 处理程序开始
让我们首先创建一个Hello, World!以纯文本格式返回的基本 http 处理程序:

use ntex::{http, web};

async fn forward() -> Result<web::HttpResponse, web::Error> {
  Ok(
    web::HttpResponse::Ok()
      .content_type("text/plain")
      .body("Hello, world!"),
  )
}

#[ntex::main]
async fn main() -> std::io::Result<()> {
  web::server(move || {
    web::App::new()
      .state(http::Client::new())
      .wrap(web::middleware::Logger::default())
      .default_service(web::route().to(forward))
  })
  .bind(("0.0.0.0", 9090))?
  .run()
  .await
}

让我们分解一下代码:

  • forward 是一个异步函数,返回 web::HttpResponse 或 web::Error。
  • main 是应用程序的入口点。 它是一个返回 std::io::Result<()> 的异步函数。
  • 我们使用 web::server 创建一个新的 ntex 网络服务器,并传递一个返回 web::App 的闭包。
  • 我们创建一个新的 http::Client 并将其添加到应用程序状态。
  • 我们为应用程序添加一个日志中间件。
  • 我们定义一个默认服务,将所有请求转发到 forward 处理程序。 我们将服务器绑定到 0.0.0.0:9090 并运行它。

运行代码:

cargo run
curl http://localhost:9090

添加代理处理程序
我们首先将url和futures_util添加到我们的依赖项中,以便能够解析 URL 并将响应转换为流:

cargo add url futures-util

然后我们更改代码以将请求转发到另一台服务器:

use futures_util::TryStreamExt;
use ntex::{http, web};

async fn forward(
  req: web::HttpRequest,
  body: ntex::util::Bytes,
  client: web::types::State<http::Client>,
  forward_url: web::types::State<url::Url>,
) -> Result<web::HttpResponse, web::Error> {
  let mut new_url = forward_url.get_ref().clone();
  new_url.set_path(req.uri().path());
  new_url.set_query(req.uri().query());
  let forwarded_req = client.request_from(new_url.as_str(), req.head());
  let res = forwarded_req
    .send_body(body)
    .await
    .map_err(web::Error::from)?;
  let mut client_resp = web::HttpResponse::build(res.status());
  let stream = res.into_stream();
  Ok(client_resp.streaming(stream))
}

#[ntex::main]
async fn main() -> std::io::Result<()> {
  let forward_url = "https://www.rust-lang.org".to_owned();
  let forward_url = url::Url::parse(&forward_url)
    .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?;
  web::server(move || {
    web::App::new()
      .state(http::Client::new())
      .state(forward_url.clone())
      .wrap(web::middleware::Logger::default())
      .default_service(web::route().to(forward))
  })
  .bind(("0.0.0.0", 9090))?
  .run()
  .await
}

让我们分解一下代码:

  • 我们将url和futures_util添加到我们的依赖项中。
  • 我们改变forward函数以将请求、主体、客户端和 forward_url 作为参数。
  • 我们通过克隆forward_url并从请求中设置路径和查询来创建一个新的url。
  • 我们使用客户端和新的 URL 创建一个新请求。
  • 我们发送请求的主体并等待响应。
  • 我们用响应的状态代码构建一个新的响应。
  • 我们将响应转换为流并返回它。

运行:

cargo run
curl http://localhost:9090

您应该在响应中看到 rust-lang.org 主页。

结论
在本教程中,我们使用 ntex 创建了一个基本的 http 代理服务器。 我们首先创建了一个简单的 http 处理程序,以纯文本格式返回 Hello, World! 然后,我们添加了一个代理处理程序,将请求转发到另一台服务器。

我们使用 url 和 futures_util 板块来解析 urls 并将响应转换为流。 我们通过运行服务器并向其发出请求来测试服务器。

我们看到,服务器成功地将请求转发到目标服务器,并返回了响应。

我们几乎不需要编写代码,就能编写一个基本的 http 代理服务器(不到 50 行代码),而且我们可以很容易地扩展它,增加更多的功能,如缓存、速率限制、身份验证等。