diff --git a/mobile/examples/objective-c/hello_world/AppDelegate.mm b/mobile/examples/objective-c/hello_world/AppDelegate.mm index 5633e61ab828..ee69e7ed9e53 100644 --- a/mobile/examples/objective-c/hello_world/AppDelegate.mm +++ b/mobile/examples/objective-c/hello_world/AppDelegate.mm @@ -16,12 +16,7 @@ - (BOOL)application:(UIApplication *)application [self.window setRootViewController:controller]; [self.window makeKeyAndVisible]; - NSString *configFile = [[NSBundle mainBundle] pathForResource:@"config" ofType:@"yaml"]; - NSString *configYaml = [NSString stringWithContentsOfFile:configFile - encoding:NSUTF8StringEncoding - error:NULL]; - NSLog(@"Loading config:\n%@", configYaml); - self.envoy = [[Envoy alloc] initWithConfig:configYaml]; + self.envoy = [[Envoy alloc] initWithConfig:[Configuration new]]; NSLog(@"Finished launching!"); return YES; } diff --git a/mobile/examples/objective-c/hello_world/BUILD b/mobile/examples/objective-c/hello_world/BUILD index bbe5364e1fe9..80ec187415b7 100644 --- a/mobile/examples/objective-c/hello_world/BUILD +++ b/mobile/examples/objective-c/hello_world/BUILD @@ -8,7 +8,6 @@ objc_library( "*.h", "*.mm", ]), - data = ["config.yaml"], deps = ["//dist:envoy_mobile_ios"], ) diff --git a/mobile/examples/objective-c/hello_world/config.yaml b/mobile/examples/objective-c/hello_world/config.yaml deleted file mode 120000 index d78ec6edb730..000000000000 --- a/mobile/examples/objective-c/hello_world/config.yaml +++ /dev/null @@ -1 +0,0 @@ -../../common/config.yaml \ No newline at end of file diff --git a/mobile/examples/swift/hello_world/AppDelegate.swift b/mobile/examples/swift/hello_world/AppDelegate.swift index 14c5ec6a10b4..7fae89cd2bfd 100644 --- a/mobile/examples/swift/hello_world/AppDelegate.swift +++ b/mobile/examples/swift/hello_world/AppDelegate.swift @@ -1,24 +1,16 @@ import Envoy import UIKit -private enum ConfigLoadError: Swift.Error { - case noFileAtPath -} - @UIApplicationMain final class AppDelegate: UIResponder, UIApplicationDelegate { - private var envoy: Envoy! + private let envoy = try! Envoy() + var window: UIWindow? func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - NSLog("Loading config") - let envoyConfig = try! self.loadEnvoyConfig() - NSLog("Loaded config:\n\(envoyConfig)") - self.envoy = Envoy(config: envoyConfig) - let window = UIWindow(frame: UIScreen.main.bounds) window.rootViewController = ViewController() window.makeKeyAndVisible() @@ -27,12 +19,4 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { return true } - - private func loadEnvoyConfig() throws -> String { - guard let configFile = Bundle.main.path(forResource: "config", ofType: "yaml") else { - throw ConfigLoadError.noFileAtPath - } - - return try String(contentsOfFile: configFile, encoding: .utf8) - } } diff --git a/mobile/examples/swift/hello_world/BUILD b/mobile/examples/swift/hello_world/BUILD index 4ea25fb45888..aa5ca57164a2 100644 --- a/mobile/examples/swift/hello_world/BUILD +++ b/mobile/examples/swift/hello_world/BUILD @@ -6,7 +6,6 @@ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") swift_library( name = "appmain", srcs = glob(["*.swift"]), - data = ["config.yaml"], deps = ["//dist:envoy_mobile_ios"], ) diff --git a/mobile/examples/swift/hello_world/config.yaml b/mobile/examples/swift/hello_world/config.yaml deleted file mode 120000 index d78ec6edb730..000000000000 --- a/mobile/examples/swift/hello_world/config.yaml +++ /dev/null @@ -1 +0,0 @@ -../../common/config.yaml \ No newline at end of file diff --git a/mobile/library/common/BUILD b/mobile/library/common/BUILD index bdb73396fc20..4c2be1e21bd7 100644 --- a/mobile/library/common/BUILD +++ b/mobile/library/common/BUILD @@ -1,12 +1,15 @@ licenses(["notice"]) # Apache 2 -load("@envoy//bazel:envoy_build_system.bzl", "envoy_cc_binary", "envoy_cc_library", "envoy_package") +load("@envoy//bazel:envoy_build_system.bzl", "envoy_cc_library", "envoy_package") envoy_package() envoy_cc_library( name = "envoy_main_interface_lib", - srcs = ["main_interface.cc"], + srcs = [ + "config_template.cc", + "main_interface.cc", + ], hdrs = ["main_interface.h"], repository = "@envoy", deps = [ diff --git a/mobile/library/common/config_template.cc b/mobile/library/common/config_template.cc new file mode 100644 index 000000000000..aea22e799fd3 --- /dev/null +++ b/mobile/library/common/config_template.cc @@ -0,0 +1,90 @@ +// NOLINT(namespace-envoy) + +const char* config_template = R"( +static_resources: + listeners: + - address: + socket_address: {address: 0.0.0.0, port_value: 9000, protocol: TCP} + filter_chains: + - filters: + - name: envoy.http_connection_manager + config: + stat_prefix: base + route_config: + virtual_hosts: + - name: all + domains: ["*"] + routes: + - match: {prefix: "/"} + route: {cluster: base} + http_filters: + - name: envoy.router + - address: + socket_address: + protocol: TCP + address: 0.0.0.0 + port_value: 9001 + filter_chains: + - filters: + - name: envoy.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager + stat_prefix: client + http_protocol_options: + allow_absolute_url: true + route_config: + name: local_route + virtual_hosts: + - name: all + domains: ["*"] + routes: + - match: + prefix: "/" + route: + cluster: default_egress + http_filters: + - name: envoy.filters.http.dynamic_forward_proxy + config: + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: AUTO + - name: envoy.router + clusters: + - name: base + connect_timeout: {{ connect_timeout }} + dns_refresh_rate: {{ dns_refresh_rate }} + dns_lookup_family: V4_ONLY + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: base + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: {address: example.com, port_value: 443} + tls_context: + sni: example.com + type: LOGICAL_DNS + - name: default_egress + connect_timeout: {{ connect_timeout }} + dns_refresh_rate: {{ dns_refresh_rate }} + lb_policy: CLUSTER_PROVIDED + cluster_type: + name: envoy.clusters.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.config.cluster.dynamic_forward_proxy.v2alpha.ClusterConfig + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: AUTO + # WARNING! + # TODO: Enable TLS in https://github.com/lyft/envoy-mobile/issues/322 + # + # tls_context: + # common_tls_context: + # validation_context: + # trusted_ca: {filename: /etc/ssl/certs/ca-certificates.crt} +stats_flush_interval: {{ stats_flush_interval }} +watchdog: + megamiss_timeout: 60s + miss_timeout: 60s +)"; diff --git a/mobile/library/common/include/BUILD b/mobile/library/common/include/BUILD index 32922f9525f9..8615443c0b83 100644 --- a/mobile/library/common/include/BUILD +++ b/mobile/library/common/include/BUILD @@ -6,7 +6,11 @@ envoy_package() envoy_cc_library( name = "c_types_interface", - srcs = ["c_types.cc"], - hdrs = ["c_types.h"], + srcs = [ + "c_types.cc", + ], + hdrs = [ + "c_types.h", + ], repository = "@envoy", ) diff --git a/mobile/library/common/main_interface.h b/mobile/library/common/main_interface.h index 32f5025c795a..51cf2f6ea1eb 100644 --- a/mobile/library/common/main_interface.h +++ b/mobile/library/common/main_interface.h @@ -11,6 +11,13 @@ extern "C" { // functions #endif +/** + * Template configuration compiled with the Envoy Mobile library. + * More information about Envoy's config can be found at: + * https://www.envoyproxy.io/docs/envoy/latest/configuration/configuration + */ +extern const char* config_template; + /** * Initialize an underlying HTTP stream. * @param engine, handle to the engine that will manage this stream. diff --git a/mobile/library/objective-c/EnvoyEngine.h b/mobile/library/objective-c/EnvoyEngine.h index 21a0287ba2a2..0dbcfec842cf 100644 --- a/mobile/library/objective-c/EnvoyEngine.h +++ b/mobile/library/objective-c/EnvoyEngine.h @@ -145,6 +145,20 @@ typedef NSDictionary *> EnvoyHeaders; @end +#pragma mark - EnvoyConfiguration + +/// Namespace for Envoy configurations. +@interface EnvoyConfiguration : NSObject + +/** + Provides a default configuration template that may be used for starting Envoy. + + @return A template that may be used as a starting point for constructing configurations. + */ ++ (NSString *)templateString; + +@end + #pragma mark - EnvoyEngineImpl // Concrete implementation of the `EnvoyEngine` protocol. diff --git a/mobile/library/objective-c/EnvoyEngine.m b/mobile/library/objective-c/EnvoyEngine.m index edca5af5ac9c..ecb37c082196 100644 --- a/mobile/library/objective-c/EnvoyEngine.m +++ b/mobile/library/objective-c/EnvoyEngine.m @@ -51,6 +51,16 @@ - (EnvoyHTTPStream *)startStreamWithObserver:(EnvoyObserver *)observer { @end +#pragma mark - EnvoyConfiguration + +@implementation EnvoyConfiguration + ++ (NSString *)templateString { + return [[NSString alloc] initWithUTF8String:config_template]; +} + +@end + #pragma mark - Utilities to move elsewhere typedef struct { diff --git a/mobile/library/swift/src/BUILD b/mobile/library/swift/src/BUILD index 60b7945fecf7..74d7b67f8bf3 100644 --- a/mobile/library/swift/src/BUILD +++ b/mobile/library/swift/src/BUILD @@ -6,6 +6,7 @@ swift_static_framework( name = "ios_framework", srcs = [ "Client.swift", + "Configuration.swift", "Envoy.swift", "Error.swift", "LogLevel.swift", diff --git a/mobile/library/swift/src/Configuration.swift b/mobile/library/swift/src/Configuration.swift new file mode 100644 index 000000000000..3f093daf1557 --- /dev/null +++ b/mobile/library/swift/src/Configuration.swift @@ -0,0 +1,62 @@ +import Foundation + +/// Error that may be thrown by the configuration type. +@objc +public enum ConfigurationError: Int, Swift.Error { + /// Not all keys within the provided template were resolved. + case unresolvedTemplateKey +} + +/// Builder used for creating configurations for new Envoy instances. +@objcMembers +public final class Configuration: NSObject { + /// Timeout to apply to connections. + public private(set) var connectTimeoutSeconds: UInt = 30 + + /// Interval at which DNS should be refreshed. + public private(set) var dnsRefreshSeconds: UInt = 60 + + /// Interval at which stats should be flushed. + public private(set) var statsFlushSeconds: UInt = 60 + + public func withConnectTimeoutSeconds(_ connectTimeoutSeconds: UInt) -> Configuration { + self.connectTimeoutSeconds = connectTimeoutSeconds + return self + } + + public func withDNSRefreshSeconds(_ dnsRefreshSeconds: UInt) -> Configuration { + self.dnsRefreshSeconds = dnsRefreshSeconds + return self + } + + public func withStatsFlushSeconds(_ statsFlushSeconds: UInt) -> Configuration { + self.statsFlushSeconds = statsFlushSeconds + return self + } + + // MARK: - Internal + + /// Converts the structure into a file by replacing values in a default template configuration. + /// + /// - returns: A file that may be used for starting an instance of Envoy. + public func build() throws -> String { + var template = EnvoyConfiguration.templateString() + let templateKeysToValues: [String: String] = [ + "connect_timeout": "\(self.connectTimeoutSeconds)s", + "dns_refresh_rate": "\(self.dnsRefreshSeconds)s", + "stats_flush_interval": "\(self.statsFlushSeconds)s", + ] + + for (templateKey, value) in templateKeysToValues { + while let range = template.range(of: "{{ \(templateKey) }}") { + template = template.replacingCharacters(in: range, with: value) + } + } + + if template.contains("{{") { + throw ConfigurationError.unresolvedTemplateKey + } + + return template + } +} diff --git a/mobile/library/swift/src/Envoy.swift b/mobile/library/swift/src/Envoy.swift index 979668b8e343..16a868b9786d 100644 --- a/mobile/library/swift/src/Envoy.swift +++ b/mobile/library/swift/src/Envoy.swift @@ -15,12 +15,20 @@ public final class Envoy: NSObject { return self.runner.isFinished } - /// Initialize a new Envoy instance. + /// Initialize a new Envoy instance using a typed configuration. /// - /// - parameter config: Configuration file that is recognizable by Envoy (YAML). + /// - parameter config: Configuration to use for starting Envoy. /// - parameter logLevel: Log level to use for this instance. - public init(config: String, logLevel: LogLevel = .info) { - self.runner = RunnerThread(config: config, engine: self.engine, logLevel: logLevel) + public convenience init(config: Configuration = Configuration(), logLevel: LogLevel = .info) throws { + self.init(configYAML: try config.build(), logLevel: logLevel) + } + + /// Initialize a new Envoy instance using a string configuration. + /// + /// - parameter configYAML: Configuration YAML to use for starting Envoy. + /// - parameter logLevel: Log level to use for this instance. + public init(configYAML: String, logLevel: LogLevel = .info) { + self.runner = RunnerThread(config: configYAML, engine: self.engine, logLevel: logLevel) self.runner.start() }