Same as `forward` but uses `reqwest` as the client used to forward the request.
(
req: HttpRequest,
mut payload: web::Payload,
method: actix_web::http::Method,
peer_addr: Option<PeerAddr>,
url: web::Data<Url>,
client: web::Data<reqwest::Client>,
)
| 54 | |
| 55 | /// Same as `forward` but uses `reqwest` as the client used to forward the request. |
| 56 | async fn forward_reqwest( |
| 57 | req: HttpRequest, |
| 58 | mut payload: web::Payload, |
| 59 | method: actix_web::http::Method, |
| 60 | peer_addr: Option<PeerAddr>, |
| 61 | url: web::Data<Url>, |
| 62 | client: web::Data<reqwest::Client>, |
| 63 | ) -> Result<HttpResponse, Error> { |
| 64 | let path = req |
| 65 | .uri() |
| 66 | .path() |
| 67 | .strip_prefix(REQWEST_PREFIX) |
| 68 | .unwrap_or(req.uri().path()); |
| 69 | |
| 70 | let mut new_url = (**url).clone(); |
| 71 | new_url.set_path(path); |
| 72 | new_url.set_query(req.uri().query()); |
| 73 | |
| 74 | let (tx, rx) = mpsc::unbounded_channel(); |
| 75 | |
| 76 | actix_web::rt::spawn(async move { |
| 77 | while let Some(chunk) = payload.next().await { |
| 78 | tx.send(chunk).unwrap(); |
| 79 | } |
| 80 | }); |
| 81 | |
| 82 | let forwarded_req = client |
| 83 | .request( |
| 84 | reqwest::Method::from_bytes(method.as_str().as_bytes()).unwrap(), |
| 85 | new_url, |
| 86 | ) |
| 87 | .body(reqwest::Body::wrap_stream(UnboundedReceiverStream::new(rx))); |
| 88 | |
| 89 | // TODO: This forwarded implementation is incomplete as it only handles the unofficial |
| 90 | // X-Forwarded-For header but not the official Forwarded one. |
| 91 | let forwarded_req = match peer_addr { |
| 92 | Some(PeerAddr(addr)) => forwarded_req.header("x-forwarded-for", addr.ip().to_string()), |
| 93 | None => forwarded_req, |
| 94 | }; |
| 95 | |
| 96 | let res = forwarded_req |
| 97 | .send() |
| 98 | .await |
| 99 | .map_err(error::ErrorInternalServerError)?; |
| 100 | |
| 101 | let mut client_resp = |
| 102 | HttpResponse::build(actix_web::http::StatusCode::from_u16(res.status().as_u16()).unwrap()); |
| 103 | |
| 104 | // Remove `Connection` as per |
| 105 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection#Directives |
| 106 | for (header_name, header_value) in res.headers().iter().filter(|(h, _)| *h != "connection") { |
| 107 | client_resp.insert_header(( |
| 108 | actix_web::http::header::HeaderName::from_bytes(header_name.as_ref()).unwrap(), |
| 109 | actix_web::http::header::HeaderValue::from_bytes(header_value.as_ref()).unwrap(), |
| 110 | )); |
| 111 | } |
| 112 | |
| 113 | Ok(client_resp.streaming(res.bytes_stream())) |