Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
2 changes: 2 additions & 0 deletions library/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ envoy_cc_library(
srcs = [
"certificates.inc",
"config_template.cc",
"engine.cc",
"engine.h",
"main_interface.cc",
],
hdrs = ["main_interface.h"],
Expand Down
101 changes: 101 additions & 0 deletions library/common/engine.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include "library/common/engine.h"

#include "common/common/lock_guard.h"

namespace Envoy {

// As a server, Envoy's static factory registration happens when main is run. However, when compiled
// as a library, there is no guarantee that such registration will happen before the names are
// needed. The following calls ensure that registration happens before the entities are needed. Note
// that as more registrations are needed, explicit initialization calls will need to be added here.
static void registerFactories() {
Envoy::Extensions::Clusters::DynamicForwardProxy::forceRegisterClusterFactory();
Envoy::Extensions::HttpFilters::DynamicForwardProxy::
forceRegisterDynamicForwardProxyFilterFactory();
Envoy::Extensions::HttpFilters::RouterFilter::forceRegisterRouterFilterConfig();
Envoy::Extensions::NetworkFilters::HttpConnectionManager::
forceRegisterHttpConnectionManagerFilterConfigFactory();
Envoy::Extensions::TransportSockets::RawBuffer::forceRegisterDownstreamRawBufferSocketFactory();
Envoy::Extensions::TransportSockets::RawBuffer::forceRegisterUpstreamRawBufferSocketFactory();
Envoy::Extensions::TransportSockets::Tls::forceRegisterUpstreamSslSocketFactory();
Envoy::Upstream::forceRegisterLogicalDnsClusterFactory();
}

Engine::Engine(const char* config, const char* log_level,
std::atomic<envoy_network_t>& preferred_network) {
// Ensure static factory registration occurs on time.
// TODO: ensure this is only called one time once multiple Engine objects can be allocated.
registerFactories();

// Create the Http::Dispatcher first since it contains initial queueing logic.
// TODO: consider centralizing initial queueing in this class.
http_dispatcher_ = std::make_unique<Http::Dispatcher>(preferred_network);

// Start the Envoy on a dedicated thread.
main_thread_ = std::thread(&Engine::run, this, std::string(config), std::string(log_level));
}

envoy_status_t Engine::run(std::string config, std::string log_level) {
{
Thread::LockGuard lock(mutex_);
try {
char* envoy_argv[] = {strdup("envoy"), strdup("--config-yaml"), strdup(config.c_str()),
strdup("-l"), strdup(log_level.c_str()), nullptr};

main_common_ = std::make_unique<Envoy::MainCommon>(5, envoy_argv);
event_dispatcher_ = &main_common_->server()->dispatcher();
cv_.notifyOne();
} catch (const Envoy::NoServingException& e) {
return ENVOY_SUCCESS;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What makes NoServingException a successful run?

} catch (const Envoy::MalformedArgvException& e) {
std::cerr << e.what() << std::endl;
return ENVOY_FAILURE;
} catch (const Envoy::EnvoyException& e) {
std::cerr << e.what() << std::endl;
return ENVOY_FAILURE;
}

// Note: We're waiting longer than we might otherwise to drain to the main thread's dispatcher.
// This is because we're not simply waiting for its availability and for it to have started, but
// also because we're waiting for clusters to have done their first attempt at DNS resolution.
// When we improve synchronous failure handling and/or move to dynamic forwarding, we only need
// to wait until the dispatcher is running (and can drain by enqueueing a drain callback on it,
// as we did previously).
auto server = main_common_->server();
postinit_callback_handler_ = main_common_->server()->lifecycleNotifier().registerCallback(
Envoy::Server::ServerLifecycleNotifier::Stage::PostInit, [this, server]() -> void {
http_dispatcher_->ready(server->dispatcher(), server->clusterManager());
});
} // mutex_

// The main run loop must run without holding the mutex, so that the destructor can acquire it.
return TS_UNCHECKED_READ(main_common_)->run() ? ENVOY_SUCCESS : ENVOY_FAILURE;
}

Engine::~Engine() {
// If we're already on the main thread, it should be safe to simply destruct.
if (!main_thread_.joinable()) {
return;
}

// If we're not on the main thread, we need to be sure that MainCommon is finished being
// constructed so we can dispatch shutdown.
{
Thread::LockGuard lock(mutex_);
if (!main_common_) {
cv_.wait(mutex_);
}
ASSERT(main_common_);
// Gracefully shutdown the running envoy instance by resetting the main_common_ unique_ptr.
// Destroying MainCommon's member variables shutsdown things in the correct order and
// gracefully.
event_dispatcher_->post([this]() -> void { TS_UNCHECKED_READ(main_common_).reset(); });
} // _mutex

// Now we wait for the main thread to wrap things up.
main_thread_.join();
}

Http::Dispatcher& Engine::httpDispatcher() { return *http_dispatcher_; }

} // namespace Envoy
43 changes: 43 additions & 0 deletions library/common/engine.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once

#include "envoy/server/lifecycle_notifier.h"

#include "common/upstream/logical_dns_cluster.h"

#include "exe/main_common.h"

#include "extensions/clusters/dynamic_forward_proxy/cluster.h"
#include "extensions/filters/http/dynamic_forward_proxy/config.h"
#include "extensions/filters/http/router/config.h"
#include "extensions/filters/network/http_connection_manager/config.h"
#include "extensions/transport_sockets/raw_buffer/config.h"
#include "extensions/transport_sockets/tls/config.h"

#include "absl/base/call_once.h"
#include "library/common/http/dispatcher.h"
#include "library/common/types/c_types.h"

namespace Envoy {

class Engine {
public:
Engine(const char* config, const char* log_level,
std::atomic<envoy_network_t>& preferred_network);

~Engine();

envoy_status_t run(std::string config, std::string log_level);

Http::Dispatcher& httpDispatcher();

private:
Thread::MutexBasicLockable mutex_;
Thread::CondVar cv_;
std::thread main_thread_;
std::unique_ptr<Envoy::Http::Dispatcher> http_dispatcher_;
std::unique_ptr<Envoy::MainCommon> main_common_ GUARDED_BY(mutex_);
Envoy::Server::ServerLifecycleNotifier::HandlePtr postinit_callback_handler_;
Event::Dispatcher* event_dispatcher_;
};

} // namespace Envoy
13 changes: 8 additions & 5 deletions library/common/jni_interface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr
return init_engine();
}

extern "C" JNIEXPORT jint JNICALL
Java_io_envoyproxy_envoymobile_engine_JniLibrary_runEngine(JNIEnv* env,
jclass, // class
jstring config, jstring log_level) {
return run_engine(env->GetStringUTFChars(config, nullptr),
extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_runEngine(
JNIEnv* env, jclass, jlong engine, jstring config, jstring log_level) {
return run_engine(engine, env->GetStringUTFChars(config, nullptr),
env->GetStringUTFChars(log_level, nullptr));
}

Expand Down Expand Up @@ -121,6 +119,11 @@ static JNIEnv* get_env() {
int get_env_res = static_jvm->GetEnv((void**)&env, JNI_VERSION);
if (get_env_res == JNI_EDETACHED) {
__android_log_write(ANDROID_LOG_ERROR, "jni_lib", "equals JNI_EDETACHED");
// Note: the only thread that should need to be attached is Envoy's engine std::thread.
// TODO: harden this piece of code to make sure that we are only needing to attach Envoy
// engine's std::thread, and that we detach it successfully.
static_jvm->AttachCurrentThread(&env, NULL);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to add thread detachment in a subsequent PR, as it requires introducing engine level callbacks. I don't want to make this PR any bigger.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/NULL/nullptr

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do in subsequent PR to avoid re-approval and re-running CI

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static_jvm->GetEnv((void**)&env, JNI_VERSION);
}
return env;
}
Expand Down
95 changes: 12 additions & 83 deletions library/common/main_interface.cc
Original file line number Diff line number Diff line change
@@ -1,59 +1,43 @@
#include "library/common/main_interface.h"

#include <atomic>
#include <unordered_map>

#include "envoy/server/lifecycle_notifier.h"

#include "common/upstream/logical_dns_cluster.h"

#include "exe/main_common.h"

#include "extensions/clusters/dynamic_forward_proxy/cluster.h"
#include "extensions/filters/http/dynamic_forward_proxy/config.h"
#include "extensions/filters/http/router/config.h"
#include "extensions/filters/network/http_connection_manager/config.h"
#include "extensions/transport_sockets/raw_buffer/config.h"
#include "extensions/transport_sockets/tls/config.h"

#include "library/common/buffer/utility.h"
#include "library/common/engine.h"
#include "library/common/http/dispatcher.h"
#include "library/common/http/header_utility.h"

// NOLINT(namespace-envoy)

static std::unique_ptr<Envoy::MainCommon> main_common_;
static std::unique_ptr<Envoy::Http::Dispatcher> http_dispatcher_;
static Envoy::Server::ServerLifecycleNotifier::HandlePtr stageone_callback_handler_;
static std::unique_ptr<Envoy::Engine> engine_;
static std::atomic<envoy_stream_t> current_stream_handle_{0};
static std::atomic<envoy_network_t> preferred_network_{ENVOY_NET_GENERIC};

envoy_stream_t init_stream(envoy_engine_t) { return current_stream_handle_++; }

envoy_status_t start_stream(envoy_stream_t stream, envoy_http_callbacks callbacks) {
http_dispatcher_->startStream(stream, callbacks);
engine_->httpDispatcher().startStream(stream, callbacks);
return ENVOY_SUCCESS;
}

envoy_status_t send_headers(envoy_stream_t stream, envoy_headers headers, bool end_stream) {
return http_dispatcher_->sendHeaders(stream, headers, end_stream);
return engine_->httpDispatcher().sendHeaders(stream, headers, end_stream);
}

envoy_status_t send_data(envoy_stream_t stream, envoy_data data, bool end_stream) {
return http_dispatcher_->sendData(stream, data, end_stream);
return engine_->httpDispatcher().sendData(stream, data, end_stream);
}

// TODO: implement.
envoy_status_t send_metadata(envoy_stream_t, envoy_headers) { return ENVOY_FAILURE; }

envoy_status_t send_trailers(envoy_stream_t stream, envoy_headers trailers) {
return http_dispatcher_->sendTrailers(stream, trailers);
return engine_->httpDispatcher().sendTrailers(stream, trailers);
}

envoy_status_t reset_stream(envoy_stream_t stream) { return http_dispatcher_->resetStream(stream); }
envoy_status_t reset_stream(envoy_stream_t stream) {
return engine_->httpDispatcher().resetStream(stream);
}

envoy_engine_t init_engine() {
http_dispatcher_ = std::make_unique<Envoy::Http::Dispatcher>(preferred_network_);
// TODO(goaway): return new handle once multiple engine support is in place.
// https://github.com/lyft/envoy-mobile/issues/332
return 1;
Expand All @@ -67,62 +51,7 @@ envoy_status_t set_preferred_network(envoy_network_t network) {
/**
* External entrypoint for library.
*/
envoy_status_t run_engine(const char* config, const char* log_level) {
char* envoy_argv[] = {strdup("envoy"), strdup("--config-yaml"), strdup(config),
strdup("-l"), strdup(log_level), nullptr};

// Ensure static factory registration occurs on time.
// Envoy's static factory registration happens when main is run.
// However, when compiled as a library, there is no guarantee that such registration will happen
// before the names are needed.
// The following calls ensure that registration happens before the entities are needed.
// Note that as more registrations are needed, explicit initialization calls will need to be added
// here.
Envoy::Extensions::Clusters::DynamicForwardProxy::forceRegisterClusterFactory();
Envoy::Extensions::HttpFilters::DynamicForwardProxy::
forceRegisterDynamicForwardProxyFilterFactory();
Envoy::Extensions::HttpFilters::RouterFilter::forceRegisterRouterFilterConfig();
Envoy::Extensions::NetworkFilters::HttpConnectionManager::
forceRegisterHttpConnectionManagerFilterConfigFactory();
Envoy::Extensions::TransportSockets::RawBuffer::forceRegisterDownstreamRawBufferSocketFactory();
Envoy::Extensions::TransportSockets::RawBuffer::forceRegisterUpstreamRawBufferSocketFactory();
Envoy::Extensions::TransportSockets::Tls::forceRegisterUpstreamSslSocketFactory();
Envoy::Upstream::forceRegisterLogicalDnsClusterFactory();

// Initialize the server's main context under a try/catch loop and simply
// return EXIT_FAILURE as needed. Whatever code in the initialization path
// that fails is expected to log an error message so the user can diagnose.
// Note that in the Android examples logging will not be seen.
// This is a known problem, and will be addressed by:
// https://github.com/lyft/envoy-mobile/issues/34
try {
main_common_ = std::make_unique<Envoy::MainCommon>(5, envoy_argv);

// Note: init_engine must have been called prior to calling run_engine or the creation of the
// envoy runner thread.

// Note: We're waiting longer than we might otherwise to drain to the main thread's dispatcher.
// This is because we're not simply waiting for its availability and for it to have started, but
// also because we're waiting for clusters to have done their first attempt at DNS resolution.
// When we improve synchronous failure handling and/or move to dynamic forwarding, we only need
// to wait until the dispatcher is running (and can drain by enqueueing a drain callback on it,
// as we did previously).
stageone_callback_handler_ = main_common_->server()->lifecycleNotifier().registerCallback(
Envoy::Server::ServerLifecycleNotifier::Stage::PostInit, []() -> void {
http_dispatcher_->ready(main_common_->server()->dispatcher(),
main_common_->server()->clusterManager());
});
} catch (const Envoy::NoServingException& e) {
return ENVOY_SUCCESS;
} catch (const Envoy::MalformedArgvException& e) {
std::cerr << e.what() << std::endl;
return ENVOY_FAILURE;
} catch (const Envoy::EnvoyException& e) {
std::cerr << e.what() << std::endl;
return ENVOY_FAILURE;
}

// Run the server listener loop outside try/catch blocks, so that unexpected
// exceptions show up as a core-dumps for easier diagnostics.
return main_common_->run() ? ENVOY_SUCCESS : ENVOY_FAILURE;
envoy_status_t run_engine(envoy_engine_t, const char* config, const char* log_level) {
engine_ = std::make_unique<Envoy::Engine>(config, log_level, preferred_network_);
Comment thread
rebello95 marked this conversation as resolved.
return ENVOY_SUCCESS;
}
2 changes: 1 addition & 1 deletion library/common/main_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ envoy_status_t set_preferred_network(envoy_network_t network);
* @param log_level, the logging level to run envoy with.
* @return envoy_status_t, the resulting status of the operation.
*/
envoy_status_t run_engine(const char* config, const char* log_level);
envoy_status_t run_engine(envoy_engine_t engine, const char* config, const char* log_level);

#ifdef __cplusplus
} // functions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public EnvoyHTTPStream startStream(EnvoyHTTPCallbacks callbacks) {
@Override
public int runWithConfig(String configurationYAML, String logLevel) {
try {
return JniLibrary.runEngine(configurationYAML, logLevel);
return JniLibrary.runEngine(this.engineHandle, configurationYAML, logLevel);
} catch (Throwable throwable) {
// TODO: Need to have a way to log the exception somewhere
return 1;
Expand All @@ -43,7 +43,7 @@ public int runWithConfig(String configurationYAML, String logLevel) {
* Run the Envoy engine with the provided envoyConfiguration and log level.
*
* @param envoyConfiguration The EnvoyConfiguration used to start Envoy.
* @param logLevel The log level to use when starting Envoy.
* @param logLevel The log level to use when starting Envoy.
* @return int A status indicating if the action was successful.
*/
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ public static void load() {
// dependencies are loaded and initialized at most once.
private static class JavaLoader {

private JavaLoader() { System.loadLibrary(ENVOY_JNI); }
private JavaLoader() {
System.loadLibrary(ENVOY_JNI);
}
}

/**
Expand Down Expand Up @@ -131,11 +133,12 @@ private static class JavaLoader {
/**
* External entry point for library.
*
* @param engine, the engine to run.
* @param config, the configuration blob to run envoy with.
* @param logLevel, the logging level to run envoy with.
* @return int, the resulting status of the operation.
*/
protected static native int runEngine(String config, String logLevel);
protected static native int runEngine(long engine, String config, String logLevel);

// Other native methods

Expand Down
2 changes: 1 addition & 1 deletion library/objective-c/EnvoyEngineImpl.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ - (int)runWithConfigYAML:(NSString *)configYAML logLevel:(NSString *)logLevel {
// Envoy exceptions will only be caught here when compiled for 64-bit arches.
// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Exceptions/Articles/Exceptions64Bit.html
@try {
return (int)run_engine(configYAML.UTF8String, logLevel.UTF8String);
return (int)run_engine(_engineHandle, configYAML.UTF8String, logLevel.UTF8String);
} @catch (...) {
NSLog(@"Envoy exception caught.");
[NSNotificationCenter.defaultCenter postNotificationName:@"EnvoyException" object:self];
Expand Down
2 changes: 1 addition & 1 deletion test/performance/test_binary_size.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
// This binary is used to perform stripped down binary size investigations of the Envoy codebase.
// Please refer to the development docs for more information:
// https://envoy-mobile.github.io/docs/envoy-mobile/latest/development/performance/binary_size.html
int main() { return run_engine(nullptr, nullptr); }
int main() { return run_engine(0, nullptr, nullptr); }