Skip to main content

spin_trigger_http/
outbound_http.rs

1use std::{
2    net::{IpAddr, Ipv4Addr, SocketAddr},
3    sync::Arc,
4};
5
6use http::uri::Scheme;
7use spin_core::async_trait;
8use spin_factor_outbound_http::intercept::{self, InterceptOutcome, InterceptRequest};
9use spin_factor_outbound_networking::config::allowed_hosts::parse_service_chaining_target;
10use spin_factors::RuntimeFactors;
11use spin_http::routes::RouteMatch;
12use wasmtime::ToWasmtimeResult;
13use wasmtime_wasi_http::p2::{HttpError, HttpResult};
14
15use crate::HttpServer;
16
17/// An outbound HTTP interceptor that handles service chaining requests.
18pub struct OutboundHttpInterceptor<F: RuntimeFactors> {
19    server: Arc<HttpServer<F>>,
20}
21
22impl<F: RuntimeFactors> OutboundHttpInterceptor<F> {
23    pub fn new(server: Arc<HttpServer<F>>) -> Self {
24        Self { server }
25    }
26}
27
28const CHAINED_CLIENT_ADDR: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
29
30#[async_trait]
31impl<F: RuntimeFactors> intercept::OutboundHttpInterceptor for OutboundHttpInterceptor<F> {
32    async fn intercept(&self, request: InterceptRequest) -> HttpResult<InterceptOutcome> {
33        // Handle service chaining requests
34        if let Some(component_id) = parse_service_chaining_target(request.uri()) {
35            let req = request.into_hyper_request();
36            let path = req.uri().path().to_owned();
37            let route_match = RouteMatch::synthetic(component_id, path);
38            let resp = self
39                .server
40                .handle_trigger_route(req, route_match, Scheme::HTTP, CHAINED_CLIENT_ADDR)
41                .await
42                .to_wasmtime_result()
43                .map_err(HttpError::trap)?;
44            Ok(InterceptOutcome::Complete(resp))
45        } else {
46            Ok(InterceptOutcome::Continue(request))
47        }
48    }
49}