diff --git a/docs/root/api/starting_envoy.rst b/docs/root/api/starting_envoy.rst index bdf36166ee..9e7ebdebd4 100644 --- a/docs/root/api/starting_envoy.rst +++ b/docs/root/api/starting_envoy.rst @@ -174,7 +174,7 @@ Specify the rate at which Envoy Mobile should flush its queued stats. Specifies the length of time a stream should wait without a headers or data event before timing out. Defaults to 15 seconds. -See `the Envoy docs `_ +See `the Envoy docs `__ for further information. **Example**:: @@ -185,6 +185,23 @@ for further information. // Swift builder.addStreamIdleTimeoutSeconds(5) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``addPerTryIdleTimeoutSeconds`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Specifies the length of time a retry (including the initial attempt) should wait without a headers +or data event before timing out. Defaults to 15 seconds. +See `the Envoy docs `__ +for further information. + +**Example**:: + + // Kotlin + builder.addPerTryIdleTimeoutSeconds(5L) + + // Swift + builder.addPerTryIdleTimeoutSeconds(5) + ~~~~~~~~~~~~~~~~~ ``addAppVersion`` ~~~~~~~~~~~~~~~~~ diff --git a/library/cc/engine_builder.cc b/library/cc/engine_builder.cc index 3ff9a5cff3..9607a6381c 100644 --- a/library/cc/engine_builder.cc +++ b/library/cc/engine_builder.cc @@ -112,6 +112,7 @@ std::string EngineBuilder::generateConfigStr() { {"stats_domain", this->stats_domain_}, {"stats_flush_interval", fmt::format("{}s", this->stats_flush_seconds_)}, {"stream_idle_timeout", fmt::format("{}s", this->stream_idle_timeout_seconds_)}, + {"per_try_idle_timeout", fmt::format("{}s", this->per_try_idle_timeout_seconds_)}, {"virtual_clusters", this->virtual_clusters_}, }; diff --git a/library/cc/engine_builder.h b/library/cc/engine_builder.h index 42360b3d83..f5688d3b2b 100644 --- a/library/cc/engine_builder.h +++ b/library/cc/engine_builder.h @@ -65,6 +65,7 @@ class EngineBuilder { std::string device_os_ = "unspecified"; std::string virtual_clusters_ = "[]"; int stream_idle_timeout_seconds_ = 15; + int per_try_idle_timeout_seconds_ = 15; // TODO(crockeo): add after filter integration // private var platformFilterChain = mutableListOf() diff --git a/library/common/config/config.cc b/library/common/config/config.cc index e9cc84b0e0..c1ebd47ae0 100644 --- a/library/common/config/config.cc +++ b/library/common/config/config.cc @@ -48,6 +48,7 @@ const std::string config_header = R"( - &statsd_host 127.0.0.1 - &statsd_port 8125 - &stream_idle_timeout 15s +- &per_try_idle_timeout 15s - &virtual_clusters [] !ignore stats_defs: @@ -228,6 +229,7 @@ const char* config_template = R"( cluster_header: x-envoy-mobile-cluster timeout: 0s retry_policy: + per_try_idle_timeout: *per_try_idle_timeout retry_back_off: base_interval: 0.25s max_interval: 60s diff --git a/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java b/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java index c2e61fdb9e..61184dc5db 100644 --- a/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java +++ b/library/java/io/envoyproxy/envoymobile/engine/EnvoyConfiguration.java @@ -25,6 +25,7 @@ public class EnvoyConfiguration { public final List httpPlatformFilterFactories; public final Integer statsFlushSeconds; public final Integer streamIdleTimeoutSeconds; + public final Integer perTryIdleTimeoutSeconds; public final String appVersion; public final String appId; public final String virtualClusters; @@ -50,6 +51,7 @@ public class EnvoyConfiguration { * @param h2ConnectionKeepaliveTimeoutSeconds rate in seconds to timeout h2 pings. * @param statsFlushSeconds interval at which to flush Envoy stats. * @param streamIdleTimeoutSeconds idle timeout for HTTP streams. + * @param perTryIdleTimeoutSeconds per try idle timeout for HTTP streams. * @param appVersion the App Version of the App using this Envoy Client. * @param appId the App ID of the App using this Envoy Client. * @param virtualClusters the JSON list of virtual cluster configs. @@ -64,8 +66,9 @@ public EnvoyConfiguration(Boolean adminInterfaceEnabled, String grpcStatsDomain, String dnsPreresolveHostnames, int h2ConnectionKeepaliveIdleIntervalMilliseconds, int h2ConnectionKeepaliveTimeoutSeconds, int statsFlushSeconds, - int streamIdleTimeoutSeconds, String appVersion, String appId, - String virtualClusters, List nativeFilterChain, + int streamIdleTimeoutSeconds, int perTryIdleTimeoutSeconds, + String appVersion, String appId, String virtualClusters, + List nativeFilterChain, List httpPlatformFilterFactories, Map stringAccessors) { this.adminInterfaceEnabled = adminInterfaceEnabled; @@ -82,6 +85,7 @@ public EnvoyConfiguration(Boolean adminInterfaceEnabled, String grpcStatsDomain, this.h2ConnectionKeepaliveTimeoutSeconds = h2ConnectionKeepaliveTimeoutSeconds; this.statsFlushSeconds = statsFlushSeconds; this.streamIdleTimeoutSeconds = streamIdleTimeoutSeconds; + this.perTryIdleTimeoutSeconds = perTryIdleTimeoutSeconds; this.appVersion = appVersion; this.appId = appId; this.virtualClusters = virtualClusters; @@ -133,6 +137,7 @@ String resolveTemplate(final String templateYAML, final String platformFilterTem .append(String.format("- &h2_connection_keepalive_timeout %ss\n", h2ConnectionKeepaliveTimeoutSeconds)) .append(String.format("- &stream_idle_timeout %ss\n", streamIdleTimeoutSeconds)) + .append(String.format("- &per_try_idle_timeout %ss\n", perTryIdleTimeoutSeconds)) .append(String.format("- &metadata { device_os: %s, app_version: %s, app_id: %s }\n", "Android", appVersion, appId)) .append("- &virtual_clusters ") diff --git a/library/java/org/chromium/net/impl/NativeCronetEngineBuilderImpl.java b/library/java/org/chromium/net/impl/NativeCronetEngineBuilderImpl.java index e244345673..dd7eab2680 100644 --- a/library/java/org/chromium/net/impl/NativeCronetEngineBuilderImpl.java +++ b/library/java/org/chromium/net/impl/NativeCronetEngineBuilderImpl.java @@ -38,6 +38,7 @@ public class NativeCronetEngineBuilderImpl extends CronetEngineBuilderImpl { private int mH2ConnectionKeepaliveTimeoutSeconds = 10; private int mStatsFlushSeconds = 60; private int mStreamIdleTimeoutSeconds = 15; + private int mPerTryIdleTimeoutSeconds = 15; private String mAppVersion = "unspecified"; private String mAppId = "unspecified"; private String mVirtualClusters = "[]"; @@ -75,7 +76,7 @@ private EnvoyConfiguration createEnvoyConfiguration() { mDnsRefreshSeconds, mDnsFailureRefreshSecondsBase, mDnsFailureRefreshSecondsMax, mDnsQueryTimeoutSeconds, mDnsPreresolveHostnames, mH2ConnectionKeepaliveIdleIntervalMilliseconds, mH2ConnectionKeepaliveTimeoutSeconds, - mStatsFlushSeconds, mStreamIdleTimeoutSeconds, mAppVersion, mAppId, mVirtualClusters, - mNativeFilterChain, mPlatformFilterChain, mStringAccessors); + mStatsFlushSeconds, mStreamIdleTimeoutSeconds, mPerTryIdleTimeoutSeconds, mAppVersion, + mAppId, mVirtualClusters, mNativeFilterChain, mPlatformFilterChain, mStringAccessors); } } diff --git a/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt b/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt index 463916e76d..d07813a148 100644 --- a/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt +++ b/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt @@ -39,6 +39,7 @@ open class EngineBuilder( private var h2ConnectionKeepaliveTimeoutSeconds = 10 private var statsFlushSeconds = 60 private var streamIdleTimeoutSeconds = 15 + private var perTryIdleTimeoutSeconds = 15 private var appVersion = "unspecified" private var appId = "unspecified" private var virtualClusters = "[]" @@ -199,6 +200,18 @@ open class EngineBuilder( return this } + /** + * Add a custom per try idle timeout for HTTP streams. Defaults to 15 seconds. + * + * @param perTryIdleTimeoutSeconds per try idle timeout for HTTP streams. + * + * @return this builder. + */ + fun addPerTryIdleTimeoutSeconds(perTryIdleTimeoutSeconds: Int): EngineBuilder { + this.perTryIdleTimeoutSeconds = perTryIdleTimeoutSeconds + return this + } + /** * Add an HTTP filter factory used to create platform filters for streams sent by this client. * @@ -350,8 +363,8 @@ open class EngineBuilder( dnsRefreshSeconds, dnsFailureRefreshSecondsBase, dnsFailureRefreshSecondsMax, dnsQueryTimeoutSeconds, dnsPreresolveHostnames, h2ConnectionKeepaliveIdleIntervalMilliseconds, h2ConnectionKeepaliveTimeoutSeconds, - statsFlushSeconds, streamIdleTimeoutSeconds, appVersion, appId, - virtualClusters, nativeFilterChain, platformFilterChain, stringAccessors + statsFlushSeconds, streamIdleTimeoutSeconds, perTryIdleTimeoutSeconds, appVersion, + appId, virtualClusters, nativeFilterChain, platformFilterChain, stringAccessors ), configuration.yaml, logLevel @@ -365,8 +378,8 @@ open class EngineBuilder( dnsRefreshSeconds, dnsFailureRefreshSecondsBase, dnsFailureRefreshSecondsMax, dnsQueryTimeoutSeconds, dnsPreresolveHostnames, h2ConnectionKeepaliveIdleIntervalMilliseconds, h2ConnectionKeepaliveTimeoutSeconds, - statsFlushSeconds, streamIdleTimeoutSeconds, appVersion, appId, - virtualClusters, nativeFilterChain, platformFilterChain, stringAccessors + statsFlushSeconds, streamIdleTimeoutSeconds, perTryIdleTimeoutSeconds, appVersion, + appId, virtualClusters, nativeFilterChain, platformFilterChain, stringAccessors ), logLevel ) diff --git a/library/objective-c/EnvoyConfiguration.m b/library/objective-c/EnvoyConfiguration.m index 4285275c73..a7a1980274 100644 --- a/library/objective-c/EnvoyConfiguration.m +++ b/library/objective-c/EnvoyConfiguration.m @@ -17,6 +17,7 @@ - (instancetype)initWithAdminInterfaceEnabled:(BOOL)adminInterfaceEnabled h2ConnectionKeepaliveTimeoutSeconds:(UInt32)h2ConnectionKeepaliveTimeoutSeconds statsFlushSeconds:(UInt32)statsFlushSeconds streamIdleTimeoutSeconds:(UInt32)streamIdleTimeoutSeconds + perTryIdleTimeoutSeconds:(UInt32)perTryIdleTimeoutSeconds appVersion:(NSString *)appVersion appId:(NSString *)appId virtualClusters:(NSString *)virtualClusters @@ -47,6 +48,7 @@ - (instancetype)initWithAdminInterfaceEnabled:(BOOL)adminInterfaceEnabled self.h2ConnectionKeepaliveTimeoutSeconds = h2ConnectionKeepaliveTimeoutSeconds; self.statsFlushSeconds = statsFlushSeconds; self.streamIdleTimeoutSeconds = streamIdleTimeoutSeconds; + self.perTryIdleTimeoutSeconds = perTryIdleTimeoutSeconds; self.appVersion = appVersion; self.appId = appId; self.virtualClusters = virtualClusters; @@ -122,6 +124,8 @@ - (nullable NSString *)resolveTemplate:(NSString *)templateYAML { (unsigned long)self.h2ConnectionKeepaliveTimeoutSeconds]; [definitions appendFormat:@"- &stream_idle_timeout %lus\n", (unsigned long)self.streamIdleTimeoutSeconds]; + [definitions + appendFormat:@"- &per_try_idle_timeout %lus\n", (unsigned long)self.perTryIdleTimeoutSeconds]; [definitions appendFormat:@"- &metadata { device_os: %@, app_version: %@, app_id: %@ }\n", @"iOS", self.appVersion, self.appId]; [definitions appendFormat:@"- &virtual_clusters %@\n", self.virtualClusters]; diff --git a/library/objective-c/EnvoyEngine.h b/library/objective-c/EnvoyEngine.h index a8b09c3933..571088d585 100644 --- a/library/objective-c/EnvoyEngine.h +++ b/library/objective-c/EnvoyEngine.h @@ -305,6 +305,7 @@ extern const int kEnvoyFilterResumeStatusResumeIteration; @property (nonatomic, assign) UInt32 h2ConnectionKeepaliveTimeoutSeconds; @property (nonatomic, assign) UInt32 statsFlushSeconds; @property (nonatomic, assign) UInt32 streamIdleTimeoutSeconds; +@property (nonatomic, assign) UInt32 perTryIdleTimeoutSeconds; @property (nonatomic, strong) NSString *appVersion; @property (nonatomic, strong) NSString *appId; @property (nonatomic, strong) NSString *virtualClusters; @@ -330,6 +331,7 @@ extern const int kEnvoyFilterResumeStatusResumeIteration; h2ConnectionKeepaliveTimeoutSeconds:(UInt32)h2ConnectionKeepaliveTimeoutSeconds statsFlushSeconds:(UInt32)statsFlushSeconds streamIdleTimeoutSeconds:(UInt32)streamIdleTimeoutSeconds + perTryIdleTimeoutSeconds:(UInt32)perTryIdleTimeoutSeconds appVersion:(NSString *)appVersion appId:(NSString *)appId virtualClusters:(NSString *)virtualClusters diff --git a/library/swift/EngineBuilder.swift b/library/swift/EngineBuilder.swift index 18a3ca4d09..e488833125 100644 --- a/library/swift/EngineBuilder.swift +++ b/library/swift/EngineBuilder.swift @@ -25,6 +25,7 @@ open class EngineBuilder: NSObject { private var h2ConnectionKeepaliveTimeoutSeconds: UInt32 = 10 private var statsFlushSeconds: UInt32 = 60 private var streamIdleTimeoutSeconds: UInt32 = 15 + private var perTryIdleTimeoutSeconds: UInt32 = 15 private var appVersion: String = "unspecified" private var appId: String = "unspecified" private var virtualClusters: String = "[]" @@ -180,6 +181,17 @@ open class EngineBuilder: NSObject { return self } + /// Add a custom per try idle timeout for HTTP streams. Defaults to 15 seconds. + /// + /// - parameter perTryIdleSeconds: Idle timeout for HTTP streams. + /// + /// - returns: This builder. + @discardableResult + public func addPerTryIdleTimeoutSeconds(_ perTryIdleTimeoutSeconds: UInt32) -> Self { + self.perTryIdleTimeoutSeconds = perTryIdleTimeoutSeconds + return self + } + /// Add an HTTP platform filter factory used to construct filters for streams sent by this client. /// /// - parameter name: Custom name to use for this filter factory. Useful for having @@ -332,6 +344,7 @@ open class EngineBuilder: NSObject { h2ConnectionKeepaliveTimeoutSeconds: self.h2ConnectionKeepaliveTimeoutSeconds, statsFlushSeconds: self.statsFlushSeconds, streamIdleTimeoutSeconds: self.streamIdleTimeoutSeconds, + perTryIdleTimeoutSeconds: self.perTryIdleTimeoutSeconds, appVersion: self.appVersion, appId: self.appId, virtualClusters: self.virtualClusters, diff --git a/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt b/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt index fff7efa1c8..91edf50bfa 100644 --- a/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt +++ b/test/java/io/envoyproxy/envoymobile/engine/EnvoyConfigurationTest.kt @@ -28,8 +28,8 @@ class EnvoyConfigurationTest { @Test fun `resolving with default configuration resolves with values`() { val envoyConfiguration = EnvoyConfiguration( - false, "stats.foo.com", null, 123, 234, 345, 456, 321, "[hostname]", 222, 333, 567, 678, "v1.2.3", "com.mydomain.myapp", "[test]", - listOf(EnvoyNativeFilterConfig("filter_name", "test_config")), + false, "stats.foo.com", null, 123, 234, 345, 456, 321, "[hostname]", 222, 333, 567, 678, 910, "v1.2.3", "com.mydomain.myapp", "[test]", + listOf(EnvoyNativeFilterConfig("filter_name", "test_config")), emptyList(), emptyMap() ) @@ -62,6 +62,10 @@ class EnvoyConfigurationTest { assertThat(resolvedTemplate).contains("&stats_domain stats.foo.com") assertThat(resolvedTemplate).contains("&stats_flush_interval 567s") + // Idle timeouts + assertThat(resolvedTemplate).contains("&stream_idle_timeout 678s") + assertThat(resolvedTemplate).contains("&per_try_idle_timeout 910s") + // Filters assertThat(resolvedTemplate).contains("filter_name") assertThat(resolvedTemplate).contains("test_config") @@ -70,7 +74,7 @@ class EnvoyConfigurationTest { @Test fun `resolve templates with invalid templates will throw on build`() { val envoyConfiguration = EnvoyConfiguration( - false, "stats.foo.com", null, 123, 234, 345, 456, 321, "[hostname]", 123, 123, 567, 678, "v1.2.3", "com.mydomain.myapp", "[test]", + false, "stats.foo.com", null, 123, 234, 345, 456, 321, "[hostname]", 123, 123, 567, 678, 910, "v1.2.3", "com.mydomain.myapp", "[test]", emptyList(), emptyList(), emptyMap() ) @@ -85,7 +89,7 @@ class EnvoyConfigurationTest { @Test fun `cannot configure both statsD and gRPC stat sink`() { val envoyConfiguration = EnvoyConfiguration( - false, "stats.foo.com", 5050, 123, 234, 345, 456, 321, "[hostname]", 123, 123, 567, 678, "v1.2.3", "com.mydomain.myapp", "[test]", + false, "stats.foo.com", 5050, 123, 234, 345, 456, 321, "[hostname]", 123, 123, 567, 678, 910, "v1.2.3", "com.mydomain.myapp", "[test]", emptyList(), emptyList(), emptyMap() ) diff --git a/test/kotlin/io/envoyproxy/envoymobile/EngineBuilderTest.kt b/test/kotlin/io/envoyproxy/envoymobile/EngineBuilderTest.kt index 973f335136..08bee0de7c 100644 --- a/test/kotlin/io/envoyproxy/envoymobile/EngineBuilderTest.kt +++ b/test/kotlin/io/envoyproxy/envoymobile/EngineBuilderTest.kt @@ -120,6 +120,16 @@ class EngineBuilderTest { assertThat(engine.envoyConfiguration!!.streamIdleTimeoutSeconds).isEqualTo(1234) } + @Test + fun `specifying per try idle timeout overrides default`() { + engineBuilder = EngineBuilder(Standard()) + engineBuilder.addEngineType { envoyEngine } + engineBuilder.addPerTryIdleTimeoutSeconds(5678) + + val engine = engineBuilder.build() as EngineImpl + assertThat(engine.envoyConfiguration!!.perTryIdleTimeoutSeconds).isEqualTo(5678) + } + @Test fun `specifying app version overrides default`() { engineBuilder = EngineBuilder(Standard()) diff --git a/test/swift/EngineBuilderTests.swift b/test/swift/EngineBuilderTests.swift index 09bb03392c..2e15d92a55 100644 --- a/test/swift/EngineBuilderTests.swift +++ b/test/swift/EngineBuilderTests.swift @@ -222,6 +222,20 @@ final class EngineBuilderTests: XCTestCase { self.waitForExpectations(timeout: 0.01) } + func testAddingPerTryIdleTimeoutSecondsAddsToConfigurationWhenRunningEnvoy() { + let expectation = self.expectation(description: "Run called with expected data") + MockEnvoyEngine.onRunWithConfig = { config, _ in + XCTAssertEqual(21, config.perTryIdleTimeoutSeconds) + expectation.fulfill() + } + + _ = EngineBuilder() + .addEngineType(MockEnvoyEngine.self) + .addPerTryIdleTimeoutSeconds(21) + .build() + self.waitForExpectations(timeout: 0.01) + } + func testAddingAppVersionAddsToConfigurationWhenRunningEnvoy() { let expectation = self.expectation(description: "Run called with expected data") MockEnvoyEngine.onRunWithConfig = { config, _ in @@ -306,6 +320,7 @@ final class EngineBuilderTests: XCTestCase { h2ConnectionKeepaliveTimeoutSeconds: 333, statsFlushSeconds: 600, streamIdleTimeoutSeconds: 700, + perTryIdleTimeoutSeconds: 777, appVersion: "v1.2.3", appId: "com.envoymobile.ios", virtualClusters: "[test]", @@ -331,6 +346,7 @@ final class EngineBuilderTests: XCTestCase { XCTAssertTrue(resolvedYAML.contains("&h2_connection_keepalive_timeout 333s")) XCTAssertTrue(resolvedYAML.contains("&stream_idle_timeout 700s")) + XCTAssertTrue(resolvedYAML.contains("&per_try_idle_timeout 777s")) XCTAssertFalse(resolvedYAML.contains("admin: *admin_interface")) @@ -365,6 +381,7 @@ final class EngineBuilderTests: XCTestCase { h2ConnectionKeepaliveTimeoutSeconds: 333, statsFlushSeconds: 600, streamIdleTimeoutSeconds: 700, + perTryIdleTimeoutSeconds: 700, appVersion: "v1.2.3", appId: "com.envoymobile.ios", virtualClusters: "[test]",