(
server: &str,
name: &str,
local: Option<&str>,
target_host: &str,
target_port: u16,
tls: &TlsOptions,
)
| 2844 | } |
| 2845 | |
| 2846 | pub async fn service_forward_tcp( |
| 2847 | server: &str, |
| 2848 | name: &str, |
| 2849 | local: Option<&str>, |
| 2850 | target_host: &str, |
| 2851 | target_port: u16, |
| 2852 | tls: &TlsOptions, |
| 2853 | ) -> Result<()> { |
| 2854 | let (bind_addr, bind_port) = parse_tcp_forward_spec(local, target_port)?; |
| 2855 | let mut client = grpc_client(server, tls).await?; |
| 2856 | |
| 2857 | let sandbox = fetch_ready_sandbox_for_forward(&mut client, name).await?; |
| 2858 | |
| 2859 | let listener = tokio::net::TcpListener::bind((bind_addr.as_str(), bind_port)) |
| 2860 | .await |
| 2861 | .into_diagnostic() |
| 2862 | .wrap_err_with(|| format!("failed to bind local forward on {bind_addr}:{bind_port}"))?; |
| 2863 | let local_addr = listener |
| 2864 | .local_addr() |
| 2865 | .into_diagnostic() |
| 2866 | .wrap_err("failed to read local forward address")?; |
| 2867 | eprintln!( |
| 2868 | "{} Forwarding {} -> {}:{} in sandbox {} via gRPC", |
| 2869 | "✓".green().bold(), |
| 2870 | local_addr, |
| 2871 | target_host, |
| 2872 | target_port, |
| 2873 | name, |
| 2874 | ); |
| 2875 | |
| 2876 | let sandbox_id = sandbox.object_id().to_string(); |
| 2877 | let (fatal_tx, mut fatal_rx) = tokio::sync::mpsc::channel::<String>(1); |
| 2878 | let mut health_check = tokio::time::interval(Duration::from_secs(2)); |
| 2879 | health_check.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay); |
| 2880 | loop { |
| 2881 | tokio::select! { |
| 2882 | Some(reason) = fatal_rx.recv() => { |
| 2883 | return Err(miette::miette!("service forward stopped: {reason}")); |
| 2884 | } |
| 2885 | |
| 2886 | _ = health_check.tick() => { |
| 2887 | fetch_ready_sandbox_for_forward(&mut client, name).await?; |
| 2888 | } |
| 2889 | |
| 2890 | accepted = listener.accept() => { |
| 2891 | let (socket, peer) = accepted |
| 2892 | .into_diagnostic() |
| 2893 | .wrap_err("failed to accept local forward connection")?; |
| 2894 | let mut client = client.clone(); |
| 2895 | let sandbox_id = sandbox_id.clone(); |
| 2896 | let target_host = target_host.to_string(); |
| 2897 | let service_id = format!("service-forward:{name}:{target_host}:{target_port}"); |
| 2898 | let fatal_tx = fatal_tx.clone(); |
| 2899 | tokio::spawn(async move { |
| 2900 | let token = match create_forward_session_token(&mut client, &sandbox_id).await { |
| 2901 | Ok(token) => token, |
| 2902 | Err(err) => { |
| 2903 | tracing::warn!(peer = %peer, error = %err, "service forward session creation failed"); |
no test coverage detected