From 6975e0026fe10c9d8412d734ac665ad4e7cfd6b7 Mon Sep 17 00:00:00 2001 From: "haruto.otake" Date: Thu, 5 Dec 2024 02:35:13 +0900 Subject: [PATCH] feat: implements tcp connection timeout --- native/yaha_native/src/binding.rs | 9 +++++++++ native/yaha_native/src/context.rs | 4 ++++ src/YetAnotherHttpHandler/NativeHttpHandlerCore.cs | 5 +++++ src/YetAnotherHttpHandler/NativeMethods.Uwp.g.cs | 3 +++ src/YetAnotherHttpHandler/NativeMethods.g.cs | 3 +++ src/YetAnotherHttpHandler/YetAnotherHttpHandler.cs | 12 ++++++++++++ 6 files changed, 36 insertions(+) diff --git a/native/yaha_native/src/binding.rs b/native/yaha_native/src/binding.rs index 9bdc3ff..d5a6394 100644 --- a/native/yaha_native/src/binding.rs +++ b/native/yaha_native/src/binding.rs @@ -279,6 +279,15 @@ pub extern "C" fn yaha_client_config_http2_keep_alive_while_idle( .http2_keep_alive_while_idle(val); } +#[no_mangle] +pub extern "C" fn yaha_client_config_connect_timeout( + ctx: *mut YahaNativeContext, + timeout_milliseconds: u64, +) { + let ctx = YahaNativeContextInternal::from_raw_context(ctx); + ctx.connect_timeout.get_or_insert(Duration::from_millis(timeout_milliseconds)); +} + #[no_mangle] pub extern "C" fn yaha_client_config_http2_max_concurrent_reset_streams( ctx: *mut YahaNativeContext, diff --git a/native/yaha_native/src/context.rs b/native/yaha_native/src/context.rs index 01e8a05..3c20b61 100644 --- a/native/yaha_native/src/context.rs +++ b/native/yaha_native/src/context.rs @@ -1,6 +1,7 @@ use std::{ num::NonZeroIsize, sync::{Arc, Mutex}, + time::Duration, }; use futures_channel::mpsc::Sender; use http_body_util::combinators::BoxBody; @@ -55,6 +56,7 @@ pub struct YahaNativeContextInternal<'a> { pub skip_certificate_verification: Option, pub root_certificates: Option, pub override_server_name: Option, + pub connect_timeout: Option, pub client_auth_certificates: Option>>, pub client_auth_key: Option>, pub client: Option, BoxBody>>, @@ -81,6 +83,7 @@ impl YahaNativeContextInternal<'_> { skip_certificate_verification: None, root_certificates: None, override_server_name: None, + connect_timeout: None, client_auth_certificates: None, client_auth_key: None, on_status_code_and_headers_receive, @@ -157,6 +160,7 @@ impl YahaNativeContextInternal<'_> { let mut http_conn = HttpConnector::new(); http_conn.set_nodelay(true); http_conn.enforce_http(false); + http_conn.set_connect_timeout(self.connect_timeout); builder.wrap_connector(http_conn) } diff --git a/src/YetAnotherHttpHandler/NativeHttpHandlerCore.cs b/src/YetAnotherHttpHandler/NativeHttpHandlerCore.cs index 0b044d3..2c5e12e 100644 --- a/src/YetAnotherHttpHandler/NativeHttpHandlerCore.cs +++ b/src/YetAnotherHttpHandler/NativeHttpHandlerCore.cs @@ -141,6 +141,11 @@ private unsafe void Initialize(YahaNativeContext* ctx, NativeClientSettings sett if (YahaEventSource.Log.IsEnabled()) YahaEventSource.Log.Info($"Option '{nameof(settings.Http2MaxFrameSize)}' = {http2MaxFrameSize}"); NativeMethods.yaha_client_config_http2_max_frame_size(ctx, http2MaxFrameSize); } + if (settings.ConnectTimeout is { } connectTimeout) + { + if (YahaEventSource.Log.IsEnabled()) YahaEventSource.Log.Info($"Option '{nameof(settings.ConnectTimeout)}' = {connectTimeout}"); + NativeMethods.yaha_client_config_connect_timeout(ctx, (ulong)connectTimeout.TotalMilliseconds); + } if (settings.Http2KeepAliveInterval is { } http2KeepAliveInterval) { if (YahaEventSource.Log.IsEnabled()) YahaEventSource.Log.Info($"Option '{nameof(settings.Http2KeepAliveInterval)}' = {http2KeepAliveInterval}"); diff --git a/src/YetAnotherHttpHandler/NativeMethods.Uwp.g.cs b/src/YetAnotherHttpHandler/NativeMethods.Uwp.g.cs index 0eb13db..0a4b941 100644 --- a/src/YetAnotherHttpHandler/NativeMethods.Uwp.g.cs +++ b/src/YetAnotherHttpHandler/NativeMethods.Uwp.g.cs @@ -89,6 +89,9 @@ internal static unsafe partial class NativeMethods [DllImport(__DllName, EntryPoint = "yaha_client_config_http2_keep_alive_while_idle", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void yaha_client_config_http2_keep_alive_while_idle(YahaNativeContext* ctx, [MarshalAs(UnmanagedType.U1)] bool val); + [DllImport(__DllName, EntryPoint = "yaha_client_config_connect_timeout", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern void yaha_client_config_connect_timeout(YahaNativeContext* ctx, ulong timeout_milliseconds); + [DllImport(__DllName, EntryPoint = "yaha_client_config_http2_max_concurrent_reset_streams", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void yaha_client_config_http2_max_concurrent_reset_streams(YahaNativeContext* ctx, nuint max); diff --git a/src/YetAnotherHttpHandler/NativeMethods.g.cs b/src/YetAnotherHttpHandler/NativeMethods.g.cs index 2c921a7..26b955f 100644 --- a/src/YetAnotherHttpHandler/NativeMethods.g.cs +++ b/src/YetAnotherHttpHandler/NativeMethods.g.cs @@ -94,6 +94,9 @@ internal static unsafe partial class NativeMethods [DllImport(__DllName, EntryPoint = "yaha_client_config_http2_keep_alive_while_idle", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void yaha_client_config_http2_keep_alive_while_idle(YahaNativeContext* ctx, [MarshalAs(UnmanagedType.U1)] bool val); + [DllImport(__DllName, EntryPoint = "yaha_client_config_connect_timeout", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern void yaha_client_config_connect_timeout(YahaNativeContext* ctx, ulong timeout_milliseconds); + [DllImport(__DllName, EntryPoint = "yaha_client_config_http2_max_concurrent_reset_streams", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void yaha_client_config_http2_max_concurrent_reset_streams(YahaNativeContext* ctx, nuint max); diff --git a/src/YetAnotherHttpHandler/YetAnotherHttpHandler.cs b/src/YetAnotherHttpHandler/YetAnotherHttpHandler.cs index c0951a2..0357577 100644 --- a/src/YetAnotherHttpHandler/YetAnotherHttpHandler.cs +++ b/src/YetAnotherHttpHandler/YetAnotherHttpHandler.cs @@ -95,6 +95,16 @@ public class YetAnotherHttpHandler : HttpMessageHandler /// hyper: http2_max_frame_size /// public uint? Http2MaxFrameSize { get => _settings.Http2MaxFrameSize; set => _settings.Http2MaxFrameSize = value; } + + /// + /// Gets or sets timeout for TCP connection establishment + /// Pass null to never timeout. + /// Default is never timeout. + /// + /// + /// hyper: set_connect_timeout + /// + public TimeSpan? ConnectTimeout { get => _settings.ConnectTimeout; set => _settings.ConnectTimeout = value; } /// /// Gets or sets an interval for HTTP2 Ping frames should be sent to keep a connection alive. @@ -204,6 +214,7 @@ internal class NativeClientSettings public uint? Http2InitialConnectionWindowSize { get; set; } public bool? Http2AdaptiveWindow { get; set; } public uint? Http2MaxFrameSize { get; set; } + public TimeSpan? ConnectTimeout { get; set; } public TimeSpan? Http2KeepAliveInterval { get; set; } public TimeSpan? Http2KeepAliveTimeout { get; set; } public bool? Http2KeepAliveWhileIdle { get; set; } @@ -227,6 +238,7 @@ public NativeClientSettings Clone() Http2InitialConnectionWindowSize = this.Http2InitialConnectionWindowSize, Http2AdaptiveWindow = this.Http2AdaptiveWindow, Http2MaxFrameSize = this.Http2MaxFrameSize, + ConnectTimeout = this.ConnectTimeout, Http2KeepAliveInterval = this.Http2KeepAliveInterval, Http2KeepAliveTimeout = this.Http2KeepAliveTimeout, Http2KeepAliveWhileIdle = this.Http2KeepAliveWhileIdle,