Build and send an HTTP request to the backend configured in `route`. Returns the prepared [`reqwest::RequestBuilder`] with auth, headers, model rewrite, and body applied. The caller decides whether to apply a total request timeout before sending. `stream_response` controls whether Vertex AI Anthropic routes upgrade the stored `:rawPredict` suffix to `:streamRawPredict` in the upstream URL. It mu
(
client: &reqwest::Client,
route: &ResolvedRoute,
method: &str,
path: &str,
headers: &[(String, String)],
body: bytes::Bytes,
stream_response: bool,
)
| 191 | /// passes `true`), but `:streamRawPredict` accepts both streaming and |
| 192 | /// non-streaming request bodies, so the behaviour is correct in all cases. |
| 193 | fn prepare_backend_request( |
| 194 | client: &reqwest::Client, |
| 195 | route: &ResolvedRoute, |
| 196 | method: &str, |
| 197 | path: &str, |
| 198 | headers: &[(String, String)], |
| 199 | body: bytes::Bytes, |
| 200 | stream_response: bool, |
| 201 | ) -> Result<(reqwest::RequestBuilder, String), RouterError> { |
| 202 | // For AWS Bedrock routes the model id is encoded in the URL path |
| 203 | // (`/model/{modelId}/invoke[-with-response-stream]`), not in the |
| 204 | // JSON body. The caller's path can carry any model id; rewrite it |
| 205 | // to the operator-configured `route.model` so a sandbox cannot |
| 206 | // pick a different upstream model than what `inference set` |
| 207 | // configured. If the path is not a recognized Bedrock shape on a |
| 208 | // Bedrock route, reject the request rather than forwarding |
| 209 | // verbatim. |
| 210 | let rewritten_path: String; |
| 211 | let path = if route_is_bedrock(route) { |
| 212 | match rewrite_bedrock_path(route, path) { |
| 213 | Some(p) => { |
| 214 | rewritten_path = p; |
| 215 | rewritten_path.as_str() |
| 216 | } |
| 217 | None => { |
| 218 | return Err(RouterError::Internal(format!( |
| 219 | "AWS Bedrock route received unprocessable path '{path}' or invalid \ |
| 220 | route.model; expected /model/<id>/invoke and a model id with no \ |
| 221 | path separators, URL delimiters, percent escapes, traversal \ |
| 222 | segments, whitespace, or control characters" |
| 223 | ))); |
| 224 | } |
| 225 | } |
| 226 | } else { |
| 227 | path |
| 228 | }; |
| 229 | let url = build_provider_url(route, &route.model, path, stream_response); |
| 230 | let headers = sanitize_request_headers(route, headers); |
| 231 | |
| 232 | let reqwest_method: reqwest::Method = method |
| 233 | .parse() |
| 234 | .map_err(|_| RouterError::Internal(format!("invalid HTTP method: {method}")))?; |
| 235 | |
| 236 | let mut builder = client.request(reqwest_method, &url); |
| 237 | |
| 238 | // Inject API key using the route's configured auth mechanism. |
| 239 | match &route.auth { |
| 240 | AuthHeader::Bearer => { |
| 241 | builder = builder.bearer_auth(&route.api_key); |
| 242 | } |
| 243 | AuthHeader::Custom(header_name) => { |
| 244 | builder = builder.header(*header_name, &route.api_key); |
| 245 | } |
| 246 | AuthHeader::None => { |
| 247 | // Bridge-fronted upstream: no router-side auth injection. |
| 248 | // The configured `endpoint` is expected to be a translating |
| 249 | // bridge / proxy whose own pod holds operator-side |
| 250 | // credentials. Used today by the `aws-bedrock` profile |