Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kotlin: add cancel stream test and fix custom yaml path #1424

Merged
merged 11 commits into from
May 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ public EnvoyHTTPStream startStream(EnvoyHTTPCallbacks callbacks) {
return envoyEngine.startStream(callbacks);
}

@Override
public int runWithConfig(String configurationYAML, String logLevel) {
public int runWithTemplate(String configurationYAML, EnvoyConfiguration envoyConfiguration,
String logLevel) {
// re-enable lifecycle-based stat flushing when https://github.com/lyft/envoy-mobile/issues/748
// gets fixed. AndroidAppLifecycleMonitor monitor = new AndroidAppLifecycleMonitor();
// application.registerActivityLifecycleCallbacks(monitor);
return envoyEngine.runWithConfig(configurationYAML, logLevel);
return envoyEngine.runWithTemplate(configurationYAML, envoyConfiguration, logLevel);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public class EnvoyConfiguration {
* @param appId the App ID of the App using this Envoy Client.
* @param virtualClusters the JSON list of virtual cluster configs.
* @param nativeFilterChain the configuration for native filters.
* @param httpPlatformFilterFactories the configuration for platform filters.
* @param stringAccesssors platform string accessors to register.
* @param httpPlatformFilterFactories the configuration for platform filters.
* @param stringAccessors platform string accessors to register.
*/
public EnvoyConfiguration(String statsDomain, int connectTimeoutSeconds, int dnsRefreshSeconds,
int dnsFailureRefreshSecondsBase, int dnsFailureRefreshSecondsMax,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package io.envoyproxy.envoymobile.engine;

import io.envoyproxy.envoymobile.engine.types.EnvoyHTTPCallbacks;
import io.envoyproxy.envoymobile.engine.types.EnvoyLogger;
import io.envoyproxy.envoymobile.engine.types.EnvoyOnEngineRunning;
import io.envoyproxy.envoymobile.engine.types.EnvoyStringAccessor;

import java.util.Map;
Expand All @@ -25,11 +23,15 @@ public interface EnvoyEngine {
/**
* Run the Envoy engine with the provided yaml string and log level.
*
* The envoyConfiguration is used to resolve the configurationYAML.
*
* @param configurationYAML The configuration yaml with which to start Envoy.
* @param envoyConfiguration The EnvoyConfiguration used to start Envoy.
* @param logLevel The log level to use when starting Envoy.
* @return A status indicating if the action was successful.
*/
int runWithConfig(String configurationYAML, String logLevel);
int runWithTemplate(String configurationYAML, EnvoyConfiguration envoyConfiguration,
String logLevel);

/**
* Run the Envoy engine with the provided EnvoyConfiguration and log level.
Expand Down
47 changes: 35 additions & 12 deletions library/java/io/envoyproxy/envoymobile/engine/EnvoyEngineImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,32 @@ public void terminate() {
/**
* Run the Envoy engine with the provided yaml string and log level.
*
* The envoyConfiguration is used to resolve the configurationYAML.
*
* @param configurationYAML The configuration yaml with which to start Envoy.
* @param logLevel The log level to use when starting Envoy.
* @param envoyConfiguration The EnvoyConfiguration used to start Envoy.
* @param logLevel The log level to use when starting Envoy.
* @return A status indicating if the action was successful.
*/
@Override
public int runWithConfig(String configurationYAML, String logLevel) {
try {
return JniLibrary.runEngine(this.engineHandle, configurationYAML, logLevel);
} catch (Throwable throwable) {
// TODO: Need to have a way to log the exception somewhere.
return ENVOY_FAILURE;
public int runWithTemplate(String configurationYAML, EnvoyConfiguration envoyConfiguration,
String logLevel) {
for (EnvoyHTTPFilterFactory filterFactory : envoyConfiguration.httpPlatformFilterFactories) {
JniLibrary.registerFilterFactory(filterFactory.getFilterName(),
new JvmFilterFactoryContext(filterFactory));
}

for (Map.Entry<String, EnvoyStringAccessor> entry :
envoyConfiguration.stringAccessors.entrySet()) {
JniLibrary.registerStringAccessor(entry.getKey(),
new JvmStringAccessorContext(entry.getValue()));
}

return runWithResolvedYAML(
envoyConfiguration.resolveTemplate(configurationYAML, JniLibrary.statsSinkTemplateString(),
JniLibrary.platformFilterTemplateString(),
JniLibrary.nativeFilterTemplateString()),
logLevel);
}

/**
Expand All @@ -84,11 +98,20 @@ public int runWithConfig(EnvoyConfiguration envoyConfiguration, String logLevel)
new JvmStringAccessorContext(entry.getValue()));
}

return runWithConfig(envoyConfiguration.resolveTemplate(
JniLibrary.templateString(), JniLibrary.statsSinkTemplateString(),
JniLibrary.platformFilterTemplateString(),
JniLibrary.nativeFilterTemplateString()),
logLevel);
return runWithResolvedYAML(
envoyConfiguration.resolveTemplate(
JniLibrary.templateString(), JniLibrary.statsSinkTemplateString(),
JniLibrary.platformFilterTemplateString(), JniLibrary.nativeFilterTemplateString()),
logLevel);
}

private int runWithResolvedYAML(String configurationYAML, String logLevel) {
try {
return JniLibrary.runEngine(this.engineHandle, configurationYAML, logLevel);
} catch (Throwable throwable) {
// TODO: Need to have a way to log the exception somewhere.
return ENVOY_FAILURE;
}
}

/**
Expand Down
12 changes: 11 additions & 1 deletion library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,17 @@ open class EngineBuilder(
fun build(): Engine {
return when (configuration) {
is Custom -> {
EngineImpl(engineType(), configuration.yaml, logLevel)
EngineImpl(
engineType(),
EnvoyConfiguration(
statsDomain, connectTimeoutSeconds,
dnsRefreshSeconds, dnsFailureRefreshSecondsBase, dnsFailureRefreshSecondsMax,
statsFlushSeconds, appVersion, appId, virtualClusters, nativeFilterChain,
platformFilterChain, stringAccessors
),
configuration.yaml,
logLevel
)
}
is Standard -> {
EngineImpl(
Expand Down
14 changes: 4 additions & 10 deletions library/kotlin/io/envoyproxy/envoymobile/EngineImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import io.envoyproxy.envoymobile.engine.EnvoyEngine
/**
* An implementation of {@link Engine}.
*/
class EngineImpl internal constructor(
class EngineImpl constructor(
internal val envoyEngine: EnvoyEngine,
internal val envoyConfiguration: EnvoyConfiguration?,
internal val envoyConfiguration: EnvoyConfiguration,
internal val configurationYAML: String?,
internal val logLevel: LogLevel
) : Engine {
Expand All @@ -22,17 +22,11 @@ class EngineImpl internal constructor(
logLevel: LogLevel = LogLevel.INFO
) : this(envoyEngine, envoyConfiguration, null, logLevel)

constructor(
envoyEngine: EnvoyEngine,
configurationYAML: String,
logLevel: LogLevel = LogLevel.INFO
) : this(envoyEngine, null, configurationYAML, logLevel)

init {
streamClient = StreamClientImpl(envoyEngine)
pulseClient = PulseClientImpl(envoyEngine)
if (envoyConfiguration == null) {
envoyEngine.runWithConfig(configurationYAML, logLevel.level)
if (configurationYAML != null) {
envoyEngine.runWithTemplate(configurationYAML, envoyConfiguration, logLevel.level)
} else {
envoyEngine.runWithConfig(envoyConfiguration, logLevel.level)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import io.envoyproxy.envoymobile.engine.types.EnvoyStringAccessor
internal class MockEnvoyEngine : EnvoyEngine {
override fun runWithConfig(envoyConfiguration: EnvoyConfiguration?, logLevel: String?): Int = 0

override fun runWithConfig(configurationYAML: String?, logLevel: String?): Int = 0
override fun runWithTemplate(
configurationYAML: String,
envoyConfiguration: EnvoyConfiguration,
logLevel: String
): Int = 0

override fun startStream(callbacks: EnvoyHTTPCallbacks?): EnvoyHTTPStream = MockEnvoyHTTPStream(callbacks!!)

Expand Down
14 changes: 14 additions & 0 deletions test/kotlin/integration/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,17 @@ envoy_mobile_jni_kt_test(
"//library/kotlin/io/envoyproxy/envoymobile:envoy_interfaces_lib",
],
)

envoy_mobile_jni_kt_test(
name = "cancel_stream_test",
srcs = [
"CancelStreamTest.kt",
],
native_deps = [
"//library/common/jni:libjava_jni_lib.so",
"//library/common/jni:java_jni_lib.jnilib",
],
deps = [
"//library/kotlin/io/envoyproxy/envoymobile:envoy_interfaces_lib",
],
)
157 changes: 157 additions & 0 deletions test/kotlin/integration/CancelStreamTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package test.kotlin.integration

import io.envoyproxy.envoymobile.Custom
import io.envoyproxy.envoymobile.EngineBuilder
import io.envoyproxy.envoymobile.EnvoyError
import io.envoyproxy.envoymobile.FilterDataStatus
import io.envoyproxy.envoymobile.FilterHeadersStatus
import io.envoyproxy.envoymobile.FilterTrailersStatus
import io.envoyproxy.envoymobile.RequestHeadersBuilder
import io.envoyproxy.envoymobile.RequestMethod
import io.envoyproxy.envoymobile.ResponseFilter
import io.envoyproxy.envoymobile.ResponseHeaders
import io.envoyproxy.envoymobile.ResponseTrailers
import io.envoyproxy.envoymobile.UpstreamHttpProtocol
import io.envoyproxy.envoymobile.engine.JniLibrary
import java.nio.ByteBuffer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

private const val hcmType =
"type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
private const val pbfType = "type.googleapis.com/envoymobile.extensions.filters.http.platform_bridge.PlatformBridge"
private const val filterName = "cancel_validation_filter"
private const val config =
"""
static_resources:
listeners:
- name: fake_remote_listener
address:
socket_address: { protocol: TCP, address: 127.0.0.1, port_value: 10101 }
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": $hcmType
stat_prefix: remote_hcm
route_config:
name: remote_route
virtual_hosts:
- name: remote_service
domains: ["*"]
routes:
- match: { prefix: "/" }
direct_response: { status: 200 }
http_filters:
- name: envoy.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
- name: base_api_listener
address:
socket_address: { protocol: TCP, address: 0.0.0.0, port_value: 10000 }
api_listener:
api_listener:
"@type": $hcmType
stat_prefix: api_hcm
route_config:
name: api_router
virtual_hosts:
- name: api
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: fake_remote }
http_filters:
- name: envoy.filters.http.platform_bridge
typed_config:
"@type": $pbfType
platform_filter_name: $filterName
- name: envoy.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: fake_remote
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: fake_remote
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address: { address: 127.0.0.1, port_value: 10101 }
"""

class CancelStreamTest {

init {
JniLibrary.loadTestLibrary()
}

private val filterExpectation = CountDownLatch(1)
private val runExpectation = CountDownLatch(1)

class CancelValidationFilter(
private val latch: CountDownLatch
) : ResponseFilter {
override fun onResponseHeaders(headers: ResponseHeaders, endStream: Boolean): FilterHeadersStatus<ResponseHeaders> {
return FilterHeadersStatus.Continue(headers)
}

override fun onResponseData(body: ByteBuffer, endStream: Boolean): FilterDataStatus<ResponseHeaders> {
return FilterDataStatus.Continue(body)
}

override fun onResponseTrailers(trailers: ResponseTrailers): FilterTrailersStatus<ResponseHeaders, ResponseTrailers> {
return FilterTrailersStatus.Continue(trailers)
}

override fun onError(error: EnvoyError) {}

override fun onCancel() {
latch.countDown()
}
}

@Test
fun `cancel stream calls onCancel callback`() {
val engine = EngineBuilder(Custom(config))
.addPlatformFilter(
name = "cancel_validation_filter",
factory = { CancelValidationFilter(filterExpectation) }
)
.setOnEngineRunning {}
buildbreaker marked this conversation as resolved.
Show resolved Hide resolved
.build()

val client = engine.streamClient()

val requestHeaders = RequestHeadersBuilder(
method = RequestMethod.GET,
scheme = "https",
authority = "example.com",
path = "/test"
)
.addUpstreamHttpProtocol(UpstreamHttpProtocol.HTTP2)
.build()

client.newStreamPrototype()
.setOnCancel {
runExpectation.countDown()
}
.start(Executors.newSingleThreadExecutor())
.sendHeaders(requestHeaders, false)
.cancel()

filterExpectation.await(10, TimeUnit.SECONDS)
runExpectation.await(10, TimeUnit.SECONDS)

engine.terminate()

assertThat(filterExpectation.count).isEqualTo(0)
assertThat(runExpectation.count).isEqualTo(0)
}
}
5 changes: 3 additions & 2 deletions test/swift/integration/CancelStreamTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ final class CancelStreamTests: XCTestCase {
let hcmType = "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
// swiftlint:disable:next line_length
let pbfType = "type.googleapis.com/envoymobile.extensions.filters.http.platform_bridge.PlatformBridge"
let filterName = "cancel_validation_filter"
let config =
"""
static_resources:
Expand Down Expand Up @@ -53,7 +54,7 @@ final class CancelStreamTests: XCTestCase {
- name: envoy.filters.http.platform_bridge
typed_config:
"@type": \(pbfType)
platform_filter_name: cancel_validation_filter
platform_filter_name: \(filterName)
- name: envoy.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
Expand Down Expand Up @@ -102,7 +103,7 @@ final class CancelStreamTests: XCTestCase {
let client = EngineBuilder(yaml: config)
.addLogLevel(.trace)
.addPlatformFilter(
name: "cancel_validation_filter",
name: filterName,
factory: { CancelValidationFilter(expectation: filterExpectation) }
)
.build()
Expand Down