Skip to content

Commit

Permalink
add alpn http filter (envoyproxy#2450)
Browse files Browse the repository at this point in the history
* add alpn filter

* update date

* address comment

Signed-off-by: crazyxy <yxyan@google.com>
  • Loading branch information
yxue authored and istio-testing committed Oct 11, 2019
1 parent 567a04e commit def0974
Show file tree
Hide file tree
Showing 10 changed files with 458 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/envoy/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ envoy_cc_binary(
"//extensions/metadata_exchange:metadata_exchange_lib",
"//extensions/stackdriver:stackdriver_plugin",
"//extensions/stats:stats_plugin",
"//src/envoy/http/alpn:config_lib",
"//src/envoy/http/authn:filter_lib",
"//src/envoy/http/jwt_auth:http_filter_factory",
"//src/envoy/http/mixer:filter_lib",
Expand Down
83 changes: 83 additions & 0 deletions src/envoy/http/alpn/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Copyright 2019 Istio Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################
#

load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_cc_test",
"envoy_package",
)

envoy_package()

envoy_cc_library(
name = "alpn_filter",
srcs = ["alpn_filter.cc"],
hdrs = ["alpn_filter.h"],
repository = "@envoy",
deps = [
"//external:alpn_filter_config_cc_proto",
"@envoy//include/envoy/http:filter_interface",
"@envoy//source/common/network:application_protocol_lib",
"@envoy//source/extensions/filters/http/common:pass_through_filter_lib",
],
)

envoy_cc_library(
name = "config_lib",
srcs = ["config.cc"],
hdrs = ["config.h"],
repository = "@envoy",
visibility = ["//visibility:public"],
deps = [
":alpn_filter",
"//src/envoy/utils:filter_names_lib",
"@envoy//include/envoy/registry",
"@envoy//source/exe:envoy_common_lib",
"@envoy//source/extensions/filters/http/common:factory_base_lib",
],
)

envoy_cc_test(
name = "alpn_test",
srcs = [
"alpn_test.cc",
],
repository = "@envoy",
deps = [
":alpn_filter",
":config_lib",
"@envoy//test/mocks/http:http_mocks",
"@envoy//test/mocks/local_info:local_info_mocks",
"@envoy//test/mocks/network:network_mocks",
"@envoy//test/mocks/protobuf:protobuf_mocks",
],
)

envoy_cc_test(
name = "config_test",
srcs = [
"config_test.cc",
],
repository = "@envoy",
deps = [
":alpn_filter",
":config_lib",
"@envoy//test/mocks/server:server_mocks",
"@envoy//test/test_common:utility_lib",
],
)
46 changes: 46 additions & 0 deletions src/envoy/http/alpn/alpn_filter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* Copyright 2019 Istio Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "src/envoy/http/alpn/alpn_filter.h"

#include "common/network/application_protocol.h"

namespace Envoy {
namespace Http {
namespace Alpn {

AlpnFilterConfig::AlpnFilterConfig(
const istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig
&proto_config)
: alpn_override_(proto_config.alpn_override().begin(),
proto_config.alpn_override().end()) {}

Http::FilterHeadersStatus AlpnFilter::decodeHeaders(Http::HeaderMap &, bool) {
const auto &alpn_override = config_->getAlpnOverride();
if (!alpn_override.empty()) {
ENVOY_LOG(debug, "override with {} ALPNs", alpn_override.size());
decoder_callbacks_->streamInfo().filterState().setData(
Network::ApplicationProtocols::key(),
std::make_unique<Network::ApplicationProtocols>(alpn_override),
Envoy::StreamInfo::FilterState::StateType::ReadOnly);
} else {
ENVOY_LOG(debug, "ALPN override is empty");
}
return Http::FilterHeadersStatus::Continue;
}

} // namespace Alpn
} // namespace Http
} // namespace Envoy
58 changes: 58 additions & 0 deletions src/envoy/http/alpn/alpn_filter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* Copyright 2019 Istio Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include "envoy/config/filter/http/alpn/v2alpha1/config.pb.h"
#include "extensions/filters/http/common/pass_through_filter.h"

namespace Envoy {
namespace Http {
namespace Alpn {

class AlpnFilterConfig {
public:
AlpnFilterConfig() = default;
explicit AlpnFilterConfig(
const istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig
&proto_config);

const std::vector<std::string> &getAlpnOverride() const {
return alpn_override_;
}

private:
const std::vector<std::string> alpn_override_;
};

using AlpnFilterConfigSharedPtr = std::shared_ptr<AlpnFilterConfig>;

class AlpnFilter : public Http::PassThroughDecoderFilter,
Logger::Loggable<Logger::Id::filter> {
public:
explicit AlpnFilter(const AlpnFilterConfigSharedPtr &config)
: config_(config) {}

// Http::PassThroughDecoderFilter
Http::FilterHeadersStatus decodeHeaders(Http::HeaderMap &headers,
bool end_stream) override;

private:
const AlpnFilterConfigSharedPtr config_;
};

} // namespace Alpn
} // namespace Http
} // namespace Envoy
88 changes: 88 additions & 0 deletions src/envoy/http/alpn/alpn_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* Copyright 2019 Istio Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "common/network/application_protocol.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "src/envoy/http/alpn/alpn_filter.h"
#include "test/mocks/http/mocks.h"

using istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig;
using testing::NiceMock;
using testing::Return;
using testing::ReturnRef;

namespace Envoy {
namespace Http {
namespace Alpn {
namespace {

class AlpnFilterTest : public testing::Test {
public:
std::unique_ptr<AlpnFilter> makeDefaultFilter() {
auto default_config = std::make_shared<AlpnFilterConfig>();
auto filter = std::make_unique<AlpnFilter>(default_config);
filter->setDecoderFilterCallbacks(callbacks_);
return filter;
}

std::unique_ptr<AlpnFilter> makeAlpnOverrideFilter(
const std::vector<std::string> &alpn) {
FilterConfig proto_config;
for (const auto &protocol : alpn) {
proto_config.add_alpn_override(protocol);
}
auto config = std::make_shared<AlpnFilterConfig>(proto_config);
auto filter = std::make_unique<AlpnFilter>(config);
filter->setDecoderFilterCallbacks(callbacks_);
return filter;
}

protected:
NiceMock<Http::MockStreamDecoderFilterCallbacks> callbacks_;
Http::TestHeaderMapImpl headers_;
};

TEST_F(AlpnFilterTest, NoAlpnOverride) {
NiceMock<StreamInfo::MockStreamInfo> stream_info;
ON_CALL(callbacks_, streamInfo()).WillByDefault(ReturnRef(stream_info));
auto filter = makeDefaultFilter();
EXPECT_CALL(stream_info, filterState()).Times(0);
EXPECT_EQ(filter->decodeHeaders(headers_, false),
Http::FilterHeadersStatus::Continue);
}

TEST_F(AlpnFilterTest, OverrideAlpn) {
NiceMock<StreamInfo::MockStreamInfo> stream_info;
ON_CALL(callbacks_, streamInfo()).WillByDefault(ReturnRef(stream_info));
std::vector<std::string> alpn{"foo", "bar", "baz"};
auto filter = makeAlpnOverrideFilter(alpn);
Envoy::StreamInfo::FilterStateImpl filter_state;
EXPECT_CALL(stream_info, filterState()).WillOnce(ReturnRef(filter_state));
EXPECT_EQ(filter->decodeHeaders(headers_, false),
Http::FilterHeadersStatus::Continue);
EXPECT_TRUE(filter_state.hasData<Network::ApplicationProtocols>(
Network::ApplicationProtocols::key()));
auto alpn_override = filter_state
.getDataReadOnly<Network::ApplicationProtocols>(
Network::ApplicationProtocols::key())
.value();
EXPECT_EQ(alpn_override, alpn);
}

} // namespace
} // namespace Alpn
} // namespace Http
} // namespace Envoy
67 changes: 67 additions & 0 deletions src/envoy/http/alpn/config.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* Copyright 2019 Istio Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "src/envoy/http/alpn/config.h"

#include "common/protobuf/message_validator_impl.h"
#include "src/envoy/http/alpn/alpn_filter.h"
#include "src/envoy/utils/filter_names.h"

using istio::envoy::config::filter::http::alpn::v2alpha1::FilterConfig;

namespace Envoy {
namespace Http {
namespace Alpn {

Http::FilterFactoryCb AlpnConfigFactory::createFilterFactory(
const Json::Object &config, const std::string &,
Server::Configuration::FactoryContext &) {
FilterConfig filter_config;
MessageUtil::loadFromJson(config.asJsonString(), filter_config,
ProtobufMessage::getNullValidationVisitor());
return createFilterFactory(filter_config);
}

Http::FilterFactoryCb AlpnConfigFactory::createFilterFactoryFromProto(
const Protobuf::Message &config, const std::string &,
Server::Configuration::FactoryContext &) {
return createFilterFactory(dynamic_cast<const FilterConfig &>(config));
}

ProtobufTypes::MessagePtr AlpnConfigFactory::createEmptyConfigProto() {
return ProtobufTypes::MessagePtr{new FilterConfig};
}

std::string AlpnConfigFactory::name() { return Utils::IstioFilterName::kAlpn; }

Http::FilterFactoryCb AlpnConfigFactory::createFilterFactory(
const FilterConfig &proto_config) {
AlpnFilterConfigSharedPtr filter_config{
std::make_shared<AlpnFilterConfig>(proto_config)};
return [filter_config](Http::FilterChainFactoryCallbacks &callbacks) -> void {
callbacks.addStreamDecoderFilter(
std::make_unique<AlpnFilter>(filter_config));
};
}

/**
* Static registration for the alpn override filter. @see RegisterFactory.
*/
REGISTER_FACTORY(AlpnConfigFactory,
Server::Configuration::NamedHttpFilterConfigFactory);

} // namespace Alpn
} // namespace Http
} // namespace Envoy
Loading

0 comments on commit def0974

Please sign in to comment.