Прокси с аксумом 0.7 и запросом 0.12 на базе http 1?

У меня есть следующий код:

#[tokio::main]
async fn main() {
  let app = Router::new()
    .route("/", get(handler))
    .route("/custom/*key", get(proxy).post(proxy));

  // ...
}

pub async fn proxy(mut req: Request<Body>) -> Result<Response<Body>, StatusCode> {
  let path_query = req
    .uri()
    .path_and_query()
    .map_or_else(|| req.uri().path(), |v| v.as_str());

  let uri = format!("http://localhost:9000/{}", path_query);

  *req.uri_mut() = Uri::try_from(uri).unwrap();

  let http_client = reqwest::Client::new();

  let res = http_client.execute(req).await.unwrap()

  if res.status().is_server_error() {
    return Err(StatusCode::INTERNAL_SERVER_ERROR);
  }

  Ok(res)
}

Но очевидно, что это не работает:

error[E0308]: mismatched types
     |
35   |     let res = http_client.execute(req).await.unwrap();
     |                           ------- ^^^ expected `Request`, found `Request<Incoming>`
     |                           |
     |                           arguments to this method are incorrect
     |
     = note: `Request<Incoming>` and `reqwest::Request` have similar names, but are actually distinct types
note: `Request<Incoming>` is defined in crate `http`
    --> C:\Users\Fred\.cargo\registry\src\index.crates.io-6f17d22bba15001f\http-1.1.0\src\request.rs:158:1
     |
158  | pub struct Request<T> {
     | ^^^^^^^^^^^^^^^^^^^^^
note: `reqwest::Request` is defined in crate `reqwest`
    --> C:\Users\Fred\.cargo\registry\src\index.crates.io-6f17d22bba15001f\reqwest-0.12.4\src\async_impl\request.rs:22:1
     |
22   | pub struct Request {
     | ^^^^^^^^^^^^^^^^^^
note: method defined here
    --> C:\Users\Fred\.cargo\registry\src\index.crates.io-6f17d22bba15001f\reqwest-0.12.4\src\async_impl\client.rs:1965:12
     |
1965 |     pub fn execute(
     |            ^^^^^^^

и:

error[E0308]: mismatched types
    |
43  |     Ok(res)
    |     -- ^^^ expected `Response<Incoming>`, found `Response`
    |     |
    |     arguments to this enum variant are incorrect
    |
    = note: `reqwest::Response` and `Response<Incoming>` have similar names, but are actually distinct types
note: `reqwest::Response` is defined in crate `reqwest`
   --> C:\Users\Fred\.cargo\registry\src\index.crates.io-6f17d22bba15001f\reqwest-0.12.4\src\async_impl\response.rs:29:1

Я использую axum 0.7.5 и reqwest 0.12 (оба на http 1).

Можно ли заставить его работать?

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
0
112
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как говорится в ошибке, запрос Запрос не то же самое, что аксум Запрос , который является псевдонимом Request из ящика http. Однако они пытаются выразить одну и ту же информацию, поэтому нам просто нужно преобразовать одно в другое.

Существует реализация TryFrom для создания запроса Request из http Request, однако есть загвоздка в том, что axum Body не конвертируется в запрос Body. Но на http .map() есть Request функция, которая может трансформировать тип телосложения самостоятельно.

Мы можем избежать буферизации всего тела запроса, сопоставляя его с потоками байтов следующим образом:

let req = req.map(|body| reqwest::Body::wrap_stream(body.into_data_stream()));

Однако снова есть загвоздка: в версиях, о которых вы спрашиваете, .into_data_stream() из тела axum не реализует Sync то, что требует Body::wrap_stream reqwest. К счастью, есть обходной путь: предыдущие версии reqwest не требовали Sync, но могли сохранить свою Body реализацию Sync, используя SyncStream из ящика sync-wrapper (прочитайте их документацию, если хотите узнать, как это работает ).

Таким образом, полное преобразование запроса выглядит так:

use sync_wrapper::SyncStream;

let req = req.map(|body| reqwest::Body::wrap_stream(SyncStream::new(body.into_data_stream())));
let req = reqwest::Request::try_from(req).unwrap();

let res = http_client.execute(req).await.unwrap();

Вызов try_from теоретически может завершиться неудачно из-за повторного анализа URL-адреса из http Uri в запрос Url (псевдоним из ящика URL-адресов).

Преобразование из reqwest Response обратно в axum/http Response не так уж и плохо, поскольку reqwest Body реализует признак http Body, а axum Body может быть создан с помощью чего-либо, реализующего признак Body. И ни одно из этих преобразований не может потерпеть неудачу. Вот как это выглядит:

let res = axum::http::Response::from(res);
let res = res.map(|body| Body::new(body));

Большое спасибо. Что делать, если я хочу буферизовать все тело запроса (они очень маленькие)?

Fred Hors 05.06.2024 09:45

Разве использование sync_wrapper::SyncStream не является своего рода «хаком»?

Fred Hors 05.06.2024 12:08

Как использовать его без необходимости Sync?

Fred Hors 05.06.2024 12:08

@FredHors Потоковая передача должна быть предпочтительнее даже для небольших полезных нагрузок. Вам нужно будет буферизовать тело только в том случае, если вы захотите его осмотреть. Кроме того, есть много способов получить его и отправить как Vec<u8> или Bytes, что в принципе универсально.

kmdreko 05.06.2024 16:09

@FredHors Я связал ящик, чтобы вы могли оценить его сами. Вам нужно Sync создать тело запроса, и SyncStream — это совершенно безопасный способ сделать это, поскольку он требует эксклюзивных ссылок и, таким образом, позволяет избежать случаев, когда тип !Sync является проблемой.

kmdreko 05.06.2024 16:13

Другие вопросы по теме