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

listener: first implementation of an Api Listener #9516

Merged
merged 57 commits into from
Jan 17, 2020
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
d182cbe
wip: get an api listener at all costs
Dec 23, 2019
3c36ef8
fmt build
Dec 23, 2019
d91b112
building and worksgit add source/! :tada:
Dec 24, 2019
c6f2d8a
initial clean up. Builds, tested
Dec 26, 2019
31b8ecd
Merge branch 'master' into api-listener
Dec 26, 2019
cfa81d8
fmt
Dec 27, 2019
538d73c
back to singleton. ApiListener and ApiListenerHandle interfaces
Dec 27, 2019
ad81348
fmt
Dec 27, 2019
e617918
comments
Dec 28, 2019
efe5cff
comments split up
Dec 28, 2019
2760a21
move read callbacks outside of the lambda
Dec 28, 2019
1a57cf0
comment
Dec 28, 2019
d74abb6
fmt
Dec 28, 2019
5684186
change api listener to implemented
Dec 28, 2019
1427f3e
fixes
Dec 29, 2019
06d0043
Merge branch 'master' of https://github.com/envoyproxy/envoy into api…
Jan 2, 2020
3dd0b13
fmt
Jan 2, 2020
9f6094b
fmt, with clang-format 9
Jan 2, 2020
65e3a71
does this work with proto formatting?
Jan 2, 2020
c9f429b
change to implemented
Jan 2, 2020
c8f9658
Merge branch 'master' into api-listener
Jan 2, 2020
9647159
Merge branch 'master' into api-listener
Jan 3, 2020
dad95f6
remove acronym
Jan 3, 2020
25d9494
generated
Jan 3, 2020
45ec135
Merge branch 'master' into api-listener
Jan 6, 2020
542ceda
missing bazel deps
Jan 6, 2020
ae1442c
finish updating from merge
Jan 6, 2020
1df8a3e
comments
Jan 7, 2020
8354ad9
comments
Jan 7, 2020
8624c14
generated
Jan 7, 2020
6de2dbc
comments
Jan 7, 2020
1c68d73
accidental commit
Jan 7, 2020
f4e8d9e
Merge branch 'master' into api-listener
Jan 9, 2020
b2c9ba5
fmt
Jan 9, 2020
ce59043
comments
Jan 10, 2020
7ce32a5
fmt
Jan 10, 2020
6f242d9
Merge branch 'master' into api-listener
Jan 10, 2020
6e2ee13
bad merge
Jan 10, 2020
df69e8c
move stats back. my test will fail
Jan 11, 2020
0ce64d4
adjust
Jan 11, 2020
ed5e8e6
type
Jan 11, 2020
cacb666
fmt
Jan 11, 2020
c5f629a
test
Jan 11, 2020
ba3d861
spell
Jan 11, 2020
2116afa
stats
Jan 11, 2020
246eb28
Merge branch 'master' into api-listener
Jan 13, 2020
50cb982
Merge branch 'api-listener' of github.com:junr03/envoy into api-listener
Jan 14, 2020
e1c789e
comments and test
Jan 15, 2020
9b99e58
factory context
Jan 15, 2020
7d8ffe3
Merge branch 'master' into api-listener
Jan 15, 2020
cd640b3
hcm
Jan 15, 2020
63c3afb
Merge branch 'master' into api-listener
Jan 15, 2020
4d47fa5
optional reference_wrapper
Jan 15, 2020
516b71b
Merge branch 'master' into api-listener
Jan 16, 2020
835adbb
comments
Jan 17, 2020
77a5570
nits
Jan 17, 2020
7442f86
Merge branch 'master' into api-listener
Jan 17, 2020
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
7 changes: 6 additions & 1 deletion api/envoy/api/v2/listener.proto
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,16 @@ message Listener {
// creating a packet-oriented UDP listener. If not present, treat it as "raw_udp_listener".
listener.UdpListenerConfig udp_listener_config = 18;

// [#not-implemented-hide:]
// Used to represent an API listener, which is used in non-proxy clients. The type of API
// exposed to the non-proxy application depends on the type of API listener.
// When this field is set, no other field except for :ref:`name<envoy_api_field_Listener.name>`
// should be set.
//
// .. note::
//
// Currently only one ApiListener can be installed; and it can only be done via bootstrap config,
// not LDS.
//
// [#next-major-version: In the v3 API, instead of this messy approach where the socket
// listener fields are directly in the top-level Listener message and the API listener types
// are in the ApiListener message, the socket listener messages should be in their own message,
Expand Down
3 changes: 1 addition & 2 deletions api/envoy/config/listener/v2/api_listener.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ option java_outer_classname = "ApiListenerProto";
option java_multiple_files = true;
option (udpa.annotations.file_migrate).move_to_package = "envoy.config.listener.v3alpha";

// [#not-implemented-hide:]
// Describes a type of API listener, which is used in non-proxy clients. The type of API
// exposed to the non-proxy application depends on the type of API listener.
message ApiListener {
// The type in this field determines the type of API listener. At present, the following
// types are supported:
// envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager (HTTP)
// envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager (HTTP)
Copy link
Member

Choose a reason for hiding this comment

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

@htuch do you have any thoughts on how this type of documentation could be automatically kept up to date in the future? I'm wondering if we could somehow if a protodoc insertion point that inserts all known variants of a message or something like that. Not sure what the best thing to do here is. This will need to be kept up to date for v3 very soon for example.

Copy link
Member

Choose a reason for hiding this comment

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

The RST anchors are automagically transformed via protoxform. I think we should add an annotation to support this, so that protoxform can (1) do consistency checking to ensure there aren't typos and (2) automatic upgrade.

// [#next-major-version: In the v3 API, replace this Any field with a oneof containing the
// specific config message for each type of API listener. We could not do this in v2 because
// it would have caused circular dependencies for go protos: lds.proto depends on this file,
Expand Down
3 changes: 1 addition & 2 deletions api/envoy/config/listener/v3alpha/api_listener.proto
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ option java_package = "io.envoyproxy.envoy.config.listener.v3alpha";
option java_outer_classname = "ApiListenerProto";
option java_multiple_files = true;

// [#not-implemented-hide:]
// Describes a type of API listener, which is used in non-proxy clients. The type of API
// exposed to the non-proxy application depends on the type of API listener.
message ApiListener {
Expand All @@ -19,7 +18,7 @@ message ApiListener {

// The type in this field determines the type of API listener. At present, the following
// types are supported:
// envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager (HTTP)
// envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager (HTTP)
// [#next-major-version: In the v3 API, replace this Any field with a oneof containing the
// specific config message for each type of API listener. We could not do this in v2 because
// it would have caused circular dependencies for go protos: lds.proto depends on this file,
Expand Down
7 changes: 6 additions & 1 deletion api/envoy/config/listener/v3alpha/listener.proto
Original file line number Diff line number Diff line change
Expand Up @@ -197,11 +197,16 @@ message Listener {
// "raw_udp_listener".
UdpListenerConfig udp_listener_config = 18;

// [#not-implemented-hide:]
// Used to represent an API listener, which is used in non-proxy clients. The type of API
// exposed to the non-proxy application depends on the type of API listener.
// When this field is set, no other field except for
// :ref:`name<envoy_api_field_config.listener.v3alpha.Listener.name>` should be set.
//
// .. note::
//
// Currently only one ApiListener can be installed; and it can only be done via bootstrap config,
// not LDS.
//
// [#next-major-version: In the v3 API, instead of this messy approach where the socket
// listener fields are directly in the top-level Listener message and the API listener types
// are in the ApiListener message, the socket listener messages should be in their own message,
Expand Down
7 changes: 6 additions & 1 deletion generated_api_shadow/envoy/api/v2/listener.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions include/envoy/http/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ load(

envoy_package()

envoy_cc_library(
name = "api_listener_interface",
hdrs = ["api_listener.h"],
deps = [":codec_interface"],
)

envoy_cc_library(
name = "async_client_interface",
hdrs = ["async_client.h"],
Expand Down
32 changes: 32 additions & 0 deletions include/envoy/http/api_listener.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include "envoy/http/codec.h"

namespace Envoy {
namespace Http {

/**
* ApiListener that allows consumers to interact with HTTP streams via API calls.
*/
// TODO(junr03): this is a replica of the functions in ServerConnectionCallbacks. It would be nice
// to not duplicate this interface layout.
class ApiListener {
public:
virtual ~ApiListener() = default;

/**
* Invoked when a new request stream is initiated by the remote.
* @param response_encoder supplies the encoder to use for creating the response. The request and
* response are backed by the same Stream object.
* @param is_internally_created indicates if this stream was originated by a
* client, or was created by Envoy, by example as part of an internal redirect.
* @return StreamDecoder& supplies the decoder callbacks to fire into for stream decoding events.
*/
virtual StreamDecoder& newStream(StreamEncoder& response_encoder,
bool is_internally_created = false) PURE;
junr03 marked this conversation as resolved.
Show resolved Hide resolved
};

using ApiListenerPtr = std::unique_ptr<ApiListener>;

} // namespace Http
} // namespace Envoy
7 changes: 7 additions & 0 deletions include/envoy/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ envoy_cc_library(
],
)

envoy_cc_library(
name = "api_listener_interface",
hdrs = ["api_listener.h"],
deps = ["//include/envoy/http:api_listener_interface"],
)

envoy_cc_library(
name = "configuration_interface",
hdrs = ["configuration.h"],
Expand Down Expand Up @@ -189,6 +195,7 @@ envoy_cc_library(
name = "listener_manager_interface",
hdrs = ["listener_manager.h"],
deps = [
":api_listener_interface",
":drain_manager_interface",
":filter_config_interface",
":guarddog_interface",
Expand Down
39 changes: 39 additions & 0 deletions include/envoy/server/api_listener.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include "envoy/http/api_listener.h"

namespace Envoy {
namespace Server {

/**
* Listener that allows consumer to interact with Envoy via a designated API.
*/
class ApiListener {
public:
enum class Type { HttpApiListener };

virtual ~ApiListener() = default;

/**
* An ApiListener is uniquely identified by its name.
*
* @return the name of the ApiListener.
*/
virtual absl::string_view name() const PURE;

/**
* @return the Type of the ApiListener.
*/
virtual Type type() const PURE;

/**
* @return Http::ApiListener IFF type() == Type::HttpApiListener, otherwise nullptr.
*/
virtual Http::ApiListener* http() PURE;
junr03 marked this conversation as resolved.
Show resolved Hide resolved
};

using ApiListenerPtr = std::unique_ptr<ApiListener>;
using ApiListenerOptRef = absl::optional<std::reference_wrapper<ApiListener>>;

} // namespace Server
} // namespace Envoy
9 changes: 9 additions & 0 deletions include/envoy/server/listener_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "envoy/network/filter.h"
#include "envoy/network/listen_socket.h"
#include "envoy/network/listener.h"
#include "envoy/server/api_listener.h"
#include "envoy/server/drain_manager.h"
#include "envoy/server/filter_config.h"
#include "envoy/server/guarddog.h"
Expand Down Expand Up @@ -212,6 +213,14 @@ class ListenerManager {
*/
using FailureStates = std::vector<std::unique_ptr<envoy::admin::v3alpha::UpdateFailureState>>;
virtual void endListenerUpdate(FailureStates&& failure_states) PURE;

// TODO(junr03): once ApiListeners support warming and draining, this function should return a
// weak_ptr to its caller. This would allow the caller to verify if the
// ApiListener is available to receive API calls on it.
/**
* @return ApiListener* the server's API Listener if it exists, nullptr if it does not.
junr03 marked this conversation as resolved.
Show resolved Hide resolved
*/
virtual ApiListenerOptRef apiListener() PURE;
};

} // namespace Server
Expand Down
1 change: 1 addition & 0 deletions source/common/http/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ envoy_cc_library(
"//include/envoy/common:time_interface",
"//include/envoy/event:deferred_deletable",
"//include/envoy/event:dispatcher_interface",
"//include/envoy/http:api_listener_interface",
"//include/envoy/http:codec_interface",
"//include/envoy/http:context_interface",
"//include/envoy/http:filter_interface",
Expand Down
37 changes: 25 additions & 12 deletions source/common/http/conn_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -286,18 +286,33 @@ void ConnectionManagerImpl::handleCodecException(const char* error) {
read_callbacks_->connection().close(Network::ConnectionCloseType::FlushWriteAndDelay);
}

void ConnectionManagerImpl::createCodec(Buffer::Instance& data) {
ASSERT(!codec_);
codec_ = config_.createCodec(read_callbacks_->connection(), data, *this);

switch (codec_->protocol()) {
case Protocol::Http3:
stats_.named_.downstream_cx_http3_total_.inc();
stats_.named_.downstream_cx_http3_active_.inc();
break;
case Protocol::Http2:
stats_.named_.downstream_cx_http2_total_.inc();
stats_.named_.downstream_cx_http2_active_.inc();
break;
case Protocol::Http11:
case Protocol::Http10:
stats_.named_.downstream_cx_http1_total_.inc();
stats_.named_.downstream_cx_http1_active_.inc();
break;
default:
NOT_REACHED_GCOVR_EXCL_LINE;
junr03 marked this conversation as resolved.
Show resolved Hide resolved
}
junr03 marked this conversation as resolved.
Show resolved Hide resolved
}

Network::FilterStatus ConnectionManagerImpl::onData(Buffer::Instance& data, bool) {
if (!codec_) {
// Http3 codec should have been instantiated by now.
codec_ = config_.createCodec(read_callbacks_->connection(), data, *this);
if (codec_->protocol() == Protocol::Http2) {
stats_.named_.downstream_cx_http2_total_.inc();
stats_.named_.downstream_cx_http2_active_.inc();
} else {
ASSERT(codec_->protocol() != Protocol::Http3);
stats_.named_.downstream_cx_http1_total_.inc();
stats_.named_.downstream_cx_http1_active_.inc();
}
createCodec(data);
}

bool redispatch;
Expand Down Expand Up @@ -357,10 +372,8 @@ Network::FilterStatus ConnectionManagerImpl::onNewConnection() {
}
// Only QUIC connection's stream_info_ specifies protocol.
Buffer::OwnedImpl dummy;
codec_ = config_.createCodec(read_callbacks_->connection(), dummy, *this);
createCodec(dummy);
ASSERT(codec_->protocol() == Protocol::Http3);
stats_.named_.downstream_cx_http3_total_.inc();
stats_.named_.downstream_cx_http3_active_.inc();
// Stop iterating through each filters for QUIC. Currently a QUIC connection
// only supports one filter, HCM, and bypasses the onData() interface. Because
// QUICHE already handles de-multiplexing.
Expand Down
13 changes: 12 additions & 1 deletion source/common/http/conn_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "envoy/access_log/access_log.h"
#include "envoy/common/scope_tracker.h"
#include "envoy/event/deferred_deletable.h"
#include "envoy/http/api_listener.h"
#include "envoy/http/codec.h"
#include "envoy/http/codes.h"
#include "envoy/http/context.h"
Expand Down Expand Up @@ -48,7 +49,8 @@ namespace Http {
class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>,
public Network::ReadFilter,
public ServerConnectionCallbacks,
public Network::ConnectionCallbacks {
public Network::ConnectionCallbacks,
public Http::ApiListener {
public:
ConnectionManagerImpl(ConnectionManagerConfig& config, const Network::DrainDecision& drain_close,
Runtime::RandomGenerator& random_generator, Http::Context& http_context,
Expand All @@ -66,6 +68,15 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>,
Stats::Scope& scope);
static const HeaderMapImpl& continueHeader();

// Currently the ConnectionManager creates a codec lazily when either:
// a) onConnection for H3.
// b) onData for H1 and H2.
// With the introduction of ApiListeners, neither event occurs. This function allows consumer code
// to manually create a codec.
// TODO(junr03): consider passing a synthetic codec instead of creating once. The codec in the
// ApiListener case is solely used to determine the protocol version.
void createCodec(Buffer::Instance& data);

// Network::ReadFilter
Network::FilterStatus onData(Buffer::Instance& data, bool end_stream) override;
Network::FilterStatus onNewConnection() override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ envoy_cc_extension(
deps = [
"//include/envoy/config:config_provider_manager_interface",
"//include/envoy/filesystem:filesystem_interface",
"//include/envoy/http:codec_interface",
"//include/envoy/http:filter_interface",
"//include/envoy/registry",
"//include/envoy/router:route_config_provider_manager_interface",
Expand Down
Loading