From 55ea9a2c33de4283d015459fd4abdd46e8c6230b Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 16 May 2019 11:42:01 -0700 Subject: [PATCH 001/109] Add 'age' and 'expires' inline headers to support HTTP caching (#868). Signed-off-by: Todd Greer --- include/envoy/http/header_map.h | 2 ++ source/common/http/headers.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/envoy/http/header_map.h b/include/envoy/http/header_map.h index 2b92fa573ba24..25c57ebc3410d 100644 --- a/include/envoy/http/header_map.h +++ b/include/envoy/http/header_map.h @@ -273,6 +273,7 @@ class HeaderEntry { HEADER_FUNC(AccessControlAllowCredentials) \ HEADER_FUNC(AccessControlExposeHeaders) \ HEADER_FUNC(AccessControlMaxAge) \ + HEADER_FUNC(Age) \ HEADER_FUNC(Authorization) \ HEADER_FUNC(CacheControl) \ HEADER_FUNC(ClientTraceId) \ @@ -310,6 +311,7 @@ class HeaderEntry { HEADER_FUNC(EnvoyUpstreamServiceTime) \ HEADER_FUNC(Etag) \ HEADER_FUNC(Expect) \ + HEADER_FUNC(Expires) \ HEADER_FUNC(ForwardedClientCert) \ HEADER_FUNC(ForwardedFor) \ HEADER_FUNC(ForwardedProto) \ diff --git a/source/common/http/headers.h b/source/common/http/headers.h index 3a74c1eb277ef..fd2a7251579f5 100644 --- a/source/common/http/headers.h +++ b/source/common/http/headers.h @@ -24,6 +24,7 @@ class HeaderValues { const LowerCaseString AccessControlExposeHeaders{"access-control-expose-headers"}; const LowerCaseString AccessControlMaxAge{"access-control-max-age"}; const LowerCaseString AccessControlAllowCredentials{"access-control-allow-credentials"}; + const LowerCaseString Age{"age"}; const LowerCaseString Authorization{"authorization"}; const LowerCaseString ProxyAuthenticate{"proxy-authenticate"}; const LowerCaseString ProxyAuthorization{"proxy-authorization"}; @@ -72,6 +73,7 @@ class HeaderValues { const LowerCaseString EnvoyDecoratorOperation{"x-envoy-decorator-operation"}; const LowerCaseString Etag{"etag"}; const LowerCaseString Expect{"expect"}; + const LowerCaseString Expires{"expires"}; const LowerCaseString ForwardedClientCert{"x-forwarded-client-cert"}; const LowerCaseString ForwardedFor{"x-forwarded-for"}; const LowerCaseString ForwardedHost{"x-forwarded-host"}; From f1a89f4d8412861f68b6b767c20fe656c0cfd5aa Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 6 Jun 2019 11:43:23 -0700 Subject: [PATCH 002/109] Add an incomplete pluggable HTTP caching filter (CacheFilter). (#868) Signed-off-by: Todd Greer --- api/docs/BUILD | 1 + .../config/filter/http/cache/v2alpha/BUILD | 8 + .../filter/http/cache/v2alpha/cache.proto | 47 +++ docs/build.sh | 1 + source/extensions/extensions_build_config.bzl | 1 + source/extensions/filters/http/cache/BUILD | 81 +++++ .../filters/http/cache/cache_filter.cc | 206 ++++++++++++ .../filters/http/cache/cache_filter.h | 68 ++++ .../extensions/filters/http/cache/config.cc | 37 +++ source/extensions/filters/http/cache/config.h | 27 ++ .../filters/http/cache/http_cache.cc | 138 ++++++++ .../filters/http/cache/http_cache.h | 302 ++++++++++++++++++ .../filters/http/cache/http_cache_utils.cc | 172 ++++++++++ .../filters/http/cache/http_cache_utils.h | 25 ++ .../extensions/filters/http/cache/key.proto | 16 + .../filters/http/cache/simple_http_cache.cc | 153 +++++++++ .../filters/http/cache/simple_http_cache.h | 44 +++ test/extensions/filters/http/cache/BUILD | 43 +++ .../filters/http/cache/cache_filter_test.cc | 62 ++++ .../filters/http/cache/http_cache_test.cc | 54 ++++ .../http/cache/http_cache_utils_test.cc | 47 +++ .../http/cache/simple_http_cache_test.cc | 248 ++++++++++++++ 22 files changed, 1781 insertions(+) create mode 100644 api/envoy/config/filter/http/cache/v2alpha/BUILD create mode 100644 api/envoy/config/filter/http/cache/v2alpha/cache.proto create mode 100644 source/extensions/filters/http/cache/BUILD create mode 100644 source/extensions/filters/http/cache/cache_filter.cc create mode 100644 source/extensions/filters/http/cache/cache_filter.h create mode 100644 source/extensions/filters/http/cache/config.cc create mode 100644 source/extensions/filters/http/cache/config.h create mode 100644 source/extensions/filters/http/cache/http_cache.cc create mode 100644 source/extensions/filters/http/cache/http_cache.h create mode 100644 source/extensions/filters/http/cache/http_cache_utils.cc create mode 100644 source/extensions/filters/http/cache/http_cache_utils.h create mode 100644 source/extensions/filters/http/cache/key.proto create mode 100644 source/extensions/filters/http/cache/simple_http_cache.cc create mode 100644 source/extensions/filters/http/cache/simple_http_cache.h create mode 100644 test/extensions/filters/http/cache/BUILD create mode 100644 test/extensions/filters/http/cache/cache_filter_test.cc create mode 100644 test/extensions/filters/http/cache/http_cache_test.cc create mode 100644 test/extensions/filters/http/cache/http_cache_utils_test.cc create mode 100644 test/extensions/filters/http/cache/simple_http_cache_test.cc diff --git a/api/docs/BUILD b/api/docs/BUILD index c8637e678dca2..702fe8b840eb1 100644 --- a/api/docs/BUILD +++ b/api/docs/BUILD @@ -38,6 +38,7 @@ proto_library( "//envoy/config/filter/accesslog/v2:accesslog", "//envoy/config/filter/dubbo/router/v2alpha1:router", "//envoy/config/filter/http/buffer/v2:buffer", + "//envoy/config/filter/http/cache/v2alpha:cache", "//envoy/config/filter/http/csrf/v2:csrf", "//envoy/config/filter/http/ext_authz/v2:ext_authz", "//envoy/config/filter/http/fault/v2:fault", diff --git a/api/envoy/config/filter/http/cache/v2alpha/BUILD b/api/envoy/config/filter/http/cache/v2alpha/BUILD new file mode 100644 index 0000000000000..a898df5a624b4 --- /dev/null +++ b/api/envoy/config/filter/http/cache/v2alpha/BUILD @@ -0,0 +1,8 @@ +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_library_internal") + +licenses(["notice"]) # Apache 2 + +api_proto_library_internal( + name = "cache", + srcs = ["cache.proto"], +) diff --git a/api/envoy/config/filter/http/cache/v2alpha/cache.proto b/api/envoy/config/filter/http/cache/v2alpha/cache.proto new file mode 100644 index 0000000000000..85d3d2374d97e --- /dev/null +++ b/api/envoy/config/filter/http/cache/v2alpha/cache.proto @@ -0,0 +1,47 @@ +syntax = "proto3"; + +package envoy.config.filter.http.cache.v2alpha; + +option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2alpha"; +option java_outer_classname = "CacheProto"; +option java_multiple_files = true; +// [#protodoc-title: HTTP Cache Filter] +// `Design`_ +// [#proto-status: experimental] + +message Cache { + + // Name of cache implementation to use. + string name = 1; + + // List of allowed Vary headers. These headers will be provided to caches in lookups, and the + // cache filter will only insert responses whose Vary headers (if any) are all in this list. For + // example, if an origin supplies "vary:user-agent" in a response, the response will only be + // inserted if "user-agent" is in this list. + repeated string allowed_vary_headers = 2; + + message KeyCreatorParams { + // If true, exclude the URL scheme from the cache key. + bool exclude_scheme = 1; + + // If true, exclude the host from the cache key. + bool exclude_host = 2; + + // If true, treat query_string_keys as a whitelist. + // If false, treat query_string_keys as a blacklist. + bool query_string_keys_whitelist = 3; + + // The list of query string keys, to be treated as a whitelist/blacklist + // depending on the value of query_string_keys_whitelist. + // If your origins ignore all but a few query params, list them here and set + // query_string_keys_whitelist to true. + // If your origins only pay attention to a few params, list them here and + // leave query_string_keys_whitelist as false. + // If your origins ignore the query string entirely, leave this empty and + // set query_string_keys_whitelist to true. + // If you don't know what query params your origins may pay attention to, + // leave both query_string fields at their false/empty defaults. + repeated string query_string_keys = 4; + } + KeyCreatorParams key_creator_params = 3; +} diff --git a/docs/build.sh b/docs/build.sh index f20a79a6f8a8f..c0425fde5fca1 100755 --- a/docs/build.sh +++ b/docs/build.sh @@ -100,6 +100,7 @@ PROTO_RST=" /envoy/config/filter/http/health_check/v2/health_check/envoy/config/filter/http/health_check/v2/health_check.proto.rst /envoy/config/filter/http/header_to_metadata/v2/header_to_metadata/envoy/config/filter/http/header_to_metadata/v2/header_to_metadata.proto.rst /envoy/config/filter/http/ip_tagging/v2/ip_tagging/envoy/config/filter/http/ip_tagging/v2/ip_tagging.proto.rst + /envoy/config/filter/http/cache/v2alpha/cache/envoy/config/filter/http/cache/v2alpha/cache.proto.rst /envoy/config/filter/http/jwt_authn/v2alpha/jwt_authn/envoy/config/filter/http/jwt_authn/v2alpha/config.proto.rst /envoy/config/filter/http/lua/v2/lua/envoy/config/filter/http/lua/v2/lua.proto.rst /envoy/config/filter/http/original_src/v2alpha1/original_src/envoy/config/filter/http/original_src/v2alpha1/original_src.proto.rst diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 93fd4108aa8bc..dca02b4fe26e3 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -29,6 +29,7 @@ EXTENSIONS = { # "envoy.filters.http.buffer": "//source/extensions/filters/http/buffer:config", + "envoy.filters.http.cache": "//source/extensions/filters/http/cache:config", "envoy.filters.http.cors": "//source/extensions/filters/http/cors:config", "envoy.filters.http.csrf": "//source/extensions/filters/http/csrf:config", "envoy.filters.http.dynamo": "//source/extensions/filters/http/dynamo:config", diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD new file mode 100644 index 0000000000000..55630ce15598a --- /dev/null +++ b/source/extensions/filters/http/cache/BUILD @@ -0,0 +1,81 @@ +licenses(["notice"]) # Apache 2 + +## Pluggable HTTP cache filter + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", + "envoy_proto_library", +) + +envoy_package() + +envoy_cc_library( + name = "cache_filter_lib", + srcs = ["cache_filter.cc"], + hdrs = ["cache_filter.h"], + deps = [ + ":http_cache_lib", + "//include/envoy/registry", + "//source/common/common:macros", + "//source/common/http:header_map_lib", + "//source/common/http:headers_lib", + "//source/common/protobuf", + "//source/extensions/filters/http/common:pass_through_filter_lib", + "@envoy_api//envoy/config/filter/http/cache/v2alpha:cache_cc", + ], +) + +envoy_proto_library( + name = "key", + srcs = ["key.proto"], +) + +envoy_cc_library( + name = "http_cache_lib", + srcs = ["http_cache.cc"], + hdrs = ["http_cache.h"], + deps = [ + ":http_cache_utils_lib", + ":key_cc", + "//include/envoy/buffer:buffer_interface", + "//include/envoy/common:time_interface", + "//include/envoy/http:header_map_interface", + "//source/common/common:assert_lib", + "//source/common/http:headers_lib", + "//source/common/protobuf:utility_lib", + ], +) + +envoy_cc_library( + name = "http_cache_utils_lib", + srcs = ["http_cache_utils.cc"], + hdrs = ["http_cache_utils.h"], + deps = [ + "//include/envoy/http:header_map_interface", + "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/time", + ], +) + +envoy_cc_library( + name = "simple_http_cache_lib", + srcs = ["simple_http_cache.cc"], + hdrs = ["simple_http_cache.h"], + deps = [ + ":http_cache_lib", + "//include/envoy/registry", + "//include/envoy/runtime:runtime_interface", + "//source/common/buffer:buffer_lib", + "//source/common/common:macros", + "//source/common/common:thread_lib", + "//source/common/http:header_map_lib", + "//source/common/http:headers_lib", + "//source/common/protobuf", + "//source/extensions/filters/http/common:pass_through_filter_lib", + "@com_google_absl//absl/base:core_headers", + "@envoy_api//envoy/config/filter/http/cache/v2alpha:cache_cc", + ], +) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc new file mode 100644 index 0000000000000..b3fff73f831ff --- /dev/null +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -0,0 +1,206 @@ +#include "extensions/filters/http/cache/cache_filter.h" + +#include "envoy/registry/registry.h" + +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" + +using absl::string_view; +using absl::WrapUnique; +using Envoy::Http::FilterDataStatus; +using Envoy::Http::FilterHeadersStatus; +using Envoy::Http::HeaderEntry; +using Envoy::Http::HeaderMap; +using Envoy::Http::HeaderMapPtr; +using Envoy::Registry::FactoryRegistry; +using Envoy::Stats::Scope; +using std::function; +using std::make_shared; +using std::string; +using std::vector; + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { +namespace { + +bool isCacheableRequest(HeaderMap& headers) { + const HeaderEntry* method = headers.Method(); + // TODO(toddmgreer) Also serve HEAD requests from cache. + // TODO(toddmgreer) Check all the other cache-related headers. + return ((method != nullptr) && method->value().getStringView() == "GET"); +} + +bool isCacheableResponse(HeaderMap& headers) { + const HeaderEntry* cache_control = headers.CacheControl(); + // TODO(toddmgreer) fully check for cacheability. See for example + // https://github.com/apache/incubator-pagespeed-mod/blob/master/pagespeed/kernel/http/caching_headers.h. + return (cache_control != nullptr) && + (cache_control->value().getStringView().find("private") == string_view::npos); +} + +HttpCache& getCache(const envoy::config::filter::http::cache::v2alpha::Cache& config) { + HttpCacheFactory* factory = + Registry::FactoryRegistry::getFactory(config.name()); + if (!factory) { + throw EnvoyException( + fmt::format("Didn't find a registered HttpCacheFactory for '{}'", config.name())); + } + return factory->getCache(); +} +} // namespace + +CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v2alpha::Cache& config, + const string&, Stats::Scope&, TimeSource& time_source) + : time_source_(time_source), cache_(getCache(config)) {} + +void CacheFilter::onDestroy() { + lookup_ = nullptr; + insert_ = nullptr; +} + +FilterHeadersStatus CacheFilter::decodeHeaders(HeaderMap& headers, bool) { + if (!isCacheableRequest(headers)) { + return FilterHeadersStatus::Continue; + } + ASSERT(decoder_callbacks_); + lookup_ = cache_.makeLookupContext(LookupRequest(headers, time_source_.systemTime())); + ASSERT(lookup_); + + CacheFilterSharedPtr self = shared_from_this(); + lookup_->getHeaders([self](LookupResult&& result) { onHeadersAsync(self, std::move(result)); }); + return FilterHeadersStatus::StopIteration; +} + +FilterHeadersStatus CacheFilter::encodeHeaders(HeaderMap& headers, bool end_stream) { + if (lookup_ && isCacheableResponse(headers)) { + insert_ = cache_.makeInsertContext(std::move(lookup_)); + insert_->insertHeaders(headers, end_stream); + } + return FilterHeadersStatus::Continue; +} + +FilterDataStatus CacheFilter::encodeData(Buffer::Instance& data, bool end_stream) { + if (insert_) { + // TODO(toddmgreer) Wait for the cache if necessary. + insert_->insertBody( + data, [](bool) {}, end_stream); + } + return FilterDataStatus::Continue; +} + +void CacheFilter::onOkHeaders(HeaderMapPtr&& headers, vector&& response_ranges, + uint64_t content_length, bool has_trailers) { + if (!lookup_) { + return; + } + response_has_trailers_ = has_trailers; + const bool end_stream = (content_length == 0 && !response_has_trailers_); + decoder_callbacks_->encodeHeaders(std::move(headers), end_stream); + if (end_stream) { + return; + } + if (content_length > 0) { + remaining_body_ = std::move(response_ranges); + // TODO(toddmgreer) handle multi-range requests. + ASSERT(remaining_body_.size() <= 1); + if (remaining_body_.empty()) { + remaining_body_.emplace_back(0, content_length - 1); + } + getBody(); + } else { + lookup_->getTrailers([self = shared_from_this()](HeaderMapPtr&& trailers) { + onTrailersAsync(self, std::move(trailers)); + }); + } +} + +void CacheFilter::onUnusableHeaders() { + if (lookup_) { + decoder_callbacks_->continueDecoding(); + } +} + +void CacheFilter::onHeadersAsync(CacheFilterSharedPtr self, LookupResult&& result) { + switch (result.cache_entry_status) { + case CacheEntryStatus::RequiresValidation: + case CacheEntryStatus::FoundNotModified: + case CacheEntryStatus::UnsatisfiableRange: + ASSERT(false); // We don't yet return or support these codes. + // FALLTHROUGH + case CacheEntryStatus::Unusable: { + self->post([self] { self->onUnusableHeaders(); }); + return; + } + case CacheEntryStatus::Ok: + self->post([self, headers = result.headers.release(), + response_ranges = std::move(result.response_ranges), + content_length = result.content_length, + has_trailers = result.has_trailers]() mutable { + self->onOkHeaders(WrapUnique(headers), std::move(response_ranges), content_length, + has_trailers); + }); + } +} + +void CacheFilter::getBody() { + ASSERT(!remaining_body_.empty()); + CacheFilterSharedPtr self = shared_from_this(); + lookup_->getBody(remaining_body_[0], + [self](Buffer::InstancePtr&& body) { self->onBody(std::move(body)); }); +} + +void CacheFilter::onBodyAsync(CacheFilterSharedPtr self, Buffer::InstancePtr&& body) { + self->post([self, body = body.release()] { self->onBody(WrapUnique(body)); }); +} + +void CacheFilter::onBody(Buffer::InstancePtr&& body) { + if (!lookup_) { + return; + } + ASSERT(!remaining_body_.empty()); + if (!body) { + decoder_callbacks_->resetStream(); + return; + } + + const uint64_t bytes_from_cache = body->length(); + if (bytes_from_cache < remaining_body_[0].length()) { + remaining_body_[0].trimFront(bytes_from_cache); + } else if (bytes_from_cache == remaining_body_[0].length()) { + remaining_body_.erase(remaining_body_.begin()); + } else { + ASSERT(false, "Received oversized body from cache."); + decoder_callbacks_->resetStream(); + return; + } + + const bool end_stream = remaining_body_.empty() && !response_has_trailers_; + decoder_callbacks_->encodeData(*body, end_stream); + if (!remaining_body_.empty()) { + getBody(); + } else if (response_has_trailers_) { + lookup_->getTrailers([self = shared_from_this()](HeaderMapPtr&& trailers) { + onTrailersAsync(self, std::move(trailers)); + }); + } +} + +void CacheFilter::onTrailers(HeaderMapPtr&& trailers) { + if (lookup_) { + decoder_callbacks_->encodeTrailers(std::move(trailers)); + } +} + +void CacheFilter::onTrailersAsync(CacheFilterSharedPtr self, HeaderMapPtr&& trailers) { + self->post([self, trailers = trailers.release()] { self->onTrailers(WrapUnique(trailers)); }); +} + +void CacheFilter::post(function f) const { + decoder_callbacks_->dispatcher().post(std::move(f)); +} +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h new file mode 100644 index 0000000000000..f071838b46384 --- /dev/null +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include +#include + +#include "envoy/config/filter/http/cache/v2alpha/cache.pb.h" + +#include "extensions/filters/http/cache/http_cache.h" +#include "extensions/filters/http/common/pass_through_filter.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { + +/** + * A filter that caches responses and attempts to satisfy requests from cache. + * Must be owned by a shared_ptr. + */ +class CacheFilter; +using CacheFilterSharedPtr = std::shared_ptr; +class CacheFilter : public Http::PassThroughFilter, + public std::enable_shared_from_this { +public: + // Throws EnvoyException if no registered HttpCacheFactory for config.name. + CacheFilter(const envoy::config::filter::http::cache::v2alpha::Cache& config, + const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source); + + // Http::StreamFilterBase + void onDestroy() override; + // Http::StreamDecoderFilter + Http::FilterHeadersStatus decodeHeaders(Http::HeaderMap& headers, bool end_stream) override; + // Http::StreamEncoderFilter + Http::FilterHeadersStatus encodeHeaders(Http::HeaderMap& headers, bool end_stream) override; + Http::FilterDataStatus encodeData(Buffer::Instance& buffer, bool end_stream) override; + +private: + void getBody(); + void onOkHeaders(Http::HeaderMapPtr&& headers, std::vector&& response_ranges, + uint64_t content_length, bool has_trailers); + void onUnusableHeaders(); + void onBody(Envoy::Buffer::InstancePtr&& body); + void onTrailers(Http::HeaderMapPtr&& trailers); + static void onHeadersAsync(CacheFilterSharedPtr self, LookupResult&& result); + static void onBodyAsync(CacheFilterSharedPtr self, Envoy::Buffer::InstancePtr&& body); + static void onTrailersAsync(CacheFilterSharedPtr self, Http::HeaderMapPtr&& trailers); + void post(std::function f) const; + + TimeSource& time_source_; + HttpCache& cache_; + LookupContextPtr lookup_; + InsertContextPtr insert_; + + // Tracks what body bytes still need to be read from the cache. This is + // currently only one Range, but will expand when full range support is added. + std::vector remaining_body_; + + // True if the response has trailers. + // TODO(toddmgreer) cache trailers. + bool response_has_trailers_; +}; + +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc new file mode 100644 index 0000000000000..4595490389e1e --- /dev/null +++ b/source/extensions/filters/http/cache/config.cc @@ -0,0 +1,37 @@ +#include "extensions/filters/http/cache/config.h" + +#include + +#include "envoy/common/time.h" +#include "envoy/config/filter/http/cache/v2alpha/cache.pb.validate.h" +#include "envoy/registry/registry.h" +#include "envoy/stats/scope.h" + +#include "extensions/filters/http/cache/cache_filter.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { + +using Http::FilterChainFactoryCallbacks; +using Server::Configuration::FactoryContext; +using Server::Configuration::NamedHttpFilterConfigFactory; +using std::make_shared; +using std::string; + +Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( + const envoy::config::filter::http::cache::v2alpha::Cache& config_proto, + const string& stats_prefix, FactoryContext& context) { + return [config, stats_prefix, &context](FilterChainFactoryCallbacks& callbacks) -> void { + callbacks.addStreamFilter( + make_shared(config, stats_prefix, context.scope(), context.timeSource())); + }; +} + +REGISTER_FACTORY(CacheFilterFactory, NamedHttpFilterConfigFactory); + +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/cache/config.h b/source/extensions/filters/http/cache/config.h new file mode 100644 index 0000000000000..3d26ed71afb0e --- /dev/null +++ b/source/extensions/filters/http/cache/config.h @@ -0,0 +1,27 @@ +#pragma once + +#include "envoy/config/filter/http/cache/v2alpha/cache.pb.h" + +#include "extensions/filters/http/cache/cache_filter.h" +#include "extensions/filters/http/common/factory_base.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { + +class CacheFilterFactory + : public Common::FactoryBase { +public: + CacheFilterFactory() : FactoryBase("envoy.filters.http.cache") {} + +private: + Http::FilterFactoryCb createFilterFactoryFromProtoTyped( + const envoy::config::filter::http::cache::v2alpha::Cache& config, + const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; +}; + +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc new file mode 100644 index 0000000000000..4fed11c22540a --- /dev/null +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -0,0 +1,138 @@ +#include "extensions/filters/http/cache/http_cache.h" + +#include + +#include "common/protobuf/utility.h" + +#include "extensions/filters/http/cache/http_cache_utils.h" + +#include "absl/time/time.h" + +using absl::Duration; +using absl::FromChrono; +using absl::Seconds; +using Envoy::Extensions::HttpFilters::Cache::Internal::effectiveMaxAge; +using Envoy::Extensions::HttpFilters::Cache::Internal::httpTime; +using Envoy::Http::HeaderEntry; +using Envoy::Http::HeaderMap; +using Envoy::Http::HeaderMapPtr; +using Envoy::Http::HeaderString; +using Envoy::Http::LowerCaseString; +using std::string; + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { + +LookupRequest::LookupRequest(const HeaderMap& request_headers, SystemTime timestamp) + : timestamp_(timestamp), + request_cache_control_(request_headers.CacheControl() == nullptr + ? "" + : request_headers.CacheControl()->value().getStringView()) { + RELEASE_ASSERT(request_headers.Path(), + "Can't form cache lookup key for malformed HeaderMap with null Path."); + RELEASE_ASSERT(request_headers.Scheme(), + "Can't form cache lookup key for malformed HeaderMap with null Scheme."); + RELEASE_ASSERT(request_headers.Host(), + "Can't form cache lookup key for malformed HeaderMap with null Host."); + // TODO(toddmgreer) Let config determine whether to include scheme, host, and + // query params. + // TODO(toddmgreer) get cluster name. + // TODO(toddmgreer) Parse Range header into request_range_spec_. + key_.set_cluster_name("cluster_name_goes_here"); + key_.set_host(string(request_headers.Host()->value().getStringView())); + key_.set_path(string(request_headers.Path()->value().getStringView())); + const HeaderString& scheme = request_headers.Scheme()->value(); + ASSERT(scheme == "http" || scheme == "https"); + key_.set_clear_http(scheme == "http"); +} + +// Unless this API is still alpha, calls to stableHashKey() must always return +// the same result, or a way must be provided to deal with a complete cache +// flush. localHashKey however, can be changed at will. +size_t stableHashKey(const Key& key) { return MessageUtil::hash(key); } +size_t localHashKey(const Key& key) { return stableHashKey(key); } + +// Returns true if response_headers is fresh. +bool LookupRequest::fresh(const HeaderMap& response_headers) const { + const HeaderEntry* cache_control_header = response_headers.get(LowerCaseString("cache-control")); + if (cache_control_header) { + const Duration effective_max_age = + effectiveMaxAge(cache_control_header->value().getStringView()); + if (effective_max_age >= Seconds(0)) { + return FromChrono(timestamp_) - httpTime(response_headers.Date()) < effective_max_age; + } + } + + // We didn't find a cache-control header with enough info to determine + // freshness, so fall back to the expires header. + return FromChrono(timestamp_) <= httpTime(response_headers.get(LowerCaseString("expires"))); +} + +LookupResult LookupRequest::makeLookupResult(HeaderMapPtr&& response_headers, + uint64_t content_length) const { + // TODO(toddmgreer) Implement all HTTP caching semantics. + ASSERT(response_headers); + LookupResult result; + if (!fresh(*response_headers)) { + result.cache_entry_status = CacheEntryStatus::RequiresValidation; + return result; + } + + result.headers = std::move(response_headers); + result.content_length = content_length; + if (adjustByteRangeSet(result.response_ranges, content_length)) { + result.cache_entry_status = CacheEntryStatus::Ok; + } else { + result.cache_entry_status = CacheEntryStatus::UnsatisfiableRange; + } + return result; +} + +// Adjusts response_ranges to fit a cached response of size content_length. +// Returns true if response_ranges is satisfiable (empty is considered +// satisfiable, as it denotes the entire body). +// TODO(toddmgreer) Merge/reorder ranges where appropriate. +bool LookupRequest::adjustByteRangeSet(std::vector& response_ranges, + uint64_t content_length) const { + if (request_range_spec_.empty()) { + // No range header, so the request can proceed. + return true; + } + + if (content_length == 0) { + // There is a range header, but it's unsatisfiable. + return false; + } + + for (const auto& spec : request_range_spec_) { + if (spec.isSuffix()) { + // spec is a suffix-byte-range-spec + if (spec.suffixLength() >= content_length) { + // All bytes are being requested, so we may as well send a normal '200 + // OK' response. + response_ranges.clear(); + return true; + } + response_ranges.emplace_back(content_length - spec.suffixLength(), content_length - 1); + } else { + // spec is a byte-range-spec + if (spec.firstBytePos() >= content_length) { + // This range is unsatisfiable, so skip it. + continue; + } + response_ranges.emplace_back(spec.firstBytePos(), + std::min(spec.lastBytePos(), content_length - 1)); + } + } + if (response_ranges.empty()) { + // All ranges were unsatisfiable. + return false; + } + return true; +} +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h new file mode 100644 index 0000000000000..7d7df0ab46d86 --- /dev/null +++ b/source/extensions/filters/http/cache/http_cache.h @@ -0,0 +1,302 @@ +#pragma once + +#include +#include + +#include "envoy/buffer/buffer.h" +#include "envoy/common/time.h" +#include "envoy/http/header_map.h" + +#include "common/common/assert.h" + +#include "source/extensions/filters/http/cache/key.pb.h" + +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { +// Whether a given cache entry is good for the current request. +enum CacheEntryStatus { + // This entry is fresh, and an appropriate response to the request. + Ok, + // No usable entry was found. If this was generated for a cache entry, the + // cache should delete that entry. + Unusable, + // This entry is stale, but appropriate for validating + RequiresValidation, + // This entry is fresh, and an appropriate basis for a 304 Not Modified + // response. + FoundNotModified, + // This entry is fresh, but can't satisfy the requested range(s). + UnsatisfiableRange, +}; + +// Byte range from an HTTP request. +class RawByteRange { +public: + // - If first==UINT64_MAX, construct a RawByteRange requesting the final last + // body bytes. + // - Otherwise, construct a RawByteRange requesting the [first,last] body + // bytes. Prereq: first == UINT64_MAX || first <= last Invariant: isSuffix() || + // firstBytePos() <= lastBytePos + RawByteRange(uint64_t first, uint64_t last) + : first_byte_pos_(first), last_byte_pos_(last) { + RELEASE_ASSERT(isSuffix() || first <= last, "Illegal byte range."); + } + bool isSuffix() const { return first_byte_pos_ == UINT64_MAX; } + uint64_t firstBytePos() const { + ASSERT(!isSuffix()); + return first_byte_pos_; + } + uint64_t lastBytePos() const { + ASSERT(!isSuffix()); + return last_byte_pos_; + } + uint64_t suffixLength() const { + ASSERT(isSuffix()); + return last_byte_pos_; + } + +private: + uint64_t first_byte_pos_; + uint64_t last_byte_pos_; +}; + +// Byte range from an HTTP request, adjusted for a known response body size. +class AdjustedByteRange { +public: + // Construct an AdjustedByteRange representing the [first,last] bytes in the + // response body. Prereq: first <= last Invariant: firstBytePos() <= + // lastBytePos() + AdjustedByteRange(uint64_t first, uint64_t last) + : first_byte_pos_(first), last_byte_pos_(last) { + ASSERT(first <= last); + } + uint64_t firstBytePos() const { return first_byte_pos_; } + uint64_t lastBytePos() const { return last_byte_pos_; } + uint64_t length() const { return last_byte_pos_ - first_byte_pos_; } + void trimFront(uint64_t n) { + RELEASE_ASSERT(n < length(), "Attempt to trim too much from range."); + first_byte_pos_ += n; + } + +private: + uint64_t first_byte_pos_; + uint64_t last_byte_pos_; +}; + +// Result of a lookup operation, including cached headers and information needed +// to serve a response based on it, or to attempt to validate. +struct LookupResult { + // If cache_entry_status == Unusable, none of the other members are + // meaningful. + CacheEntryStatus cache_entry_status = CacheEntryStatus::Unusable; + + // Headers of the cached response. + Http::HeaderMapPtr headers; + + // Size of the full response body. Cache filter will generate a content-length + // header with this value, replacing any preexisting content-length header. + // (This lets us dechunk responses as we insert them, then later serve them + // with a content-length header.) + uint64_t content_length; + + // Represents the subset of the cached response body that should be served to + // the client. If response_ranges.empty(), the entire body should be served. + // Otherwise, each Range in response_ranges specifies an exact set of bytes to + // serve from the cached response's body. All byte positions in + // response_ranges must be in the range [0,content_length). Caches should + // ensure that they can efficiently serve these ranges, and may merge and/or + // reorder ranges as appropriate, or may clear() response_ranges entirely. + std::vector response_ranges; + + // TODO(toddmgreer) Implement trailer support. + // True if the cached response has trailers. + bool has_trailers = false; +}; + +// Produces a hash of key that is consistent across restarts, architectures, +// builds, and configurations. Caches that store persistent entries based on a +// 64-bit hash should (but are not required to) use stableHashKey. Once this API +// leaves alpha, any improvements to stableHashKey that would change its output +// for existing callers is a breaking change. +// +// For non-persistent storage, use MessageUtil, which has no long-term stability +// guarantees. +// +// When providing a cached response, Caches must ensure that the keys (and not +// just their hashes) match. +// +// TODO(toddmgreer) Ensure that stability guarantees above are accurate. +size_t stableHashKey(const Key& key); + +// LookupRequest holds everything about a request that's needed to look for a +// response in a cache, to evaluate whether an entry from a cache is usable, and +// to determine what ranges are needed. +class LookupRequest { +public: + using HeaderVector = std::vector; + + // Prereq: request_headers's Path(), Scheme(), and Host() are nonnull. + LookupRequest(const Http::HeaderMap& request_headers, SystemTime timestamp); + + // Caches may modify the key accoring to local needs, though care must be + // taken to ensure that meaningfully distinct responses have distinct keys. + const Key& key() const { return key_; } + Key& key() { return key_; } + + // Returns the subset of this request's headers that are listed in + // envoy::config::filter::http::cache::v2alpha::Cache::allowed_vary_headers. + // Only needed if the cache implementation finds a variant response. + HeaderVector& vary_headers() { return vary_headers_; } + const HeaderVector& vary_headers() const { return vary_headers_; } + + // Time when this LookupRequest was created (in response to an HTTP request). + SystemTime timestamp() const { return timestamp_; } + + // WARNING: Incomplete--do not use in production (yet). + // Returns a LookupResult suitable for sending to the cache filter's + // LookupHeadersCallback. Specifically, + // - LookupResult::cache_entry_status is set according to HTTP cache + // validation logic. + // - LookupResult::headers takes ownership of response_headers. + // - LookupResult::content_length == content_length. + // - LookupResult::response_ranges entries are satisifiable (as documented + // there). + LookupResult makeLookupResult(Http::HeaderMapPtr&& response_headers, + uint64_t content_length) const; + +private: + bool fresh(const Http::HeaderMap& response_headers) const; + bool adjustByteRangeSet(std::vector& response_ranges, + uint64_t content_length) const; + + Key key_; + std::vector request_range_spec_; + SystemTime timestamp_; + HeaderVector vary_headers_; + const std::string request_cache_control_; +}; + +// Statically known information about a cache. +struct CacheInfo { + absl::string_view name_; + bool supports_range_requests_ = false; +}; + +using LookupBodyCallback = std::function; +using LookupHeadersCallback = std::function; +using LookupTrailersCallback = std::function; +using InsertCallback = std::function; + +// Manages the lifetime of an insertion. +class InsertContext { +public: + // Accepts response_headers for caching. Only called once. + virtual void insertHeaders(const Http::HeaderMap& response_headers, bool end_stream) = 0; + + // The insertion is streamed into the cache in chunks whose size is determined + // by the client, but with a pace determined by the cache. To avoid streaming + // data into cache too fast for the cache to handle, clients should wait for + // the cache to call ready_for_next_chunk() before streaming the next chunk. + // + // The client can abort the streaming insertion by dropping the + // InsertContextPtr. A cache can abort the insertion by passing 'false' into + // ready_for_next_chunk. + virtual void insertBody(const Buffer::Instance& chunk, InsertCallback ready_for_next_chunk, + bool end_stream) = 0; + + // Inserts trailers into the cache. + virtual void insertTrailers(const Http::HeaderMap& trailers) = 0; + + virtual ~InsertContext() = default; +}; +using InsertContextPtr = std::unique_ptr; + +// Lookup context manages the lifetime of a lookup, helping clients to pull data +// from the cache at a pace that works for them. At any time a client can abort +// an in-progress lookup by simply dropping the LookupContextPtr. +class LookupContext { +public: + virtual ~LookupContext() = default; + + // Get the headers from the cache. It is a programming error to call this + // twice. + virtual void getHeaders(LookupHeadersCallback&& cb) = 0; + + // Reads the next chunk from the cache, calling cb when the chunk is ready. + // + // The cache must call cb with a range of bytes starting at range.start() and + // ending at or before range.end(). Caller is responsible for tracking what + // ranges have been received, what to request next, and when to stop. A cache + // can report an error, and cause the response to be aborted, by calling cb + // with nullptr. + // + // If a cache happens to load data in chunks of a set size, it may be + // efficient to respond with fewer than the requested number of bytes. For + // example, assuming a 24 byte full-bodied response from a cache that reads in + // absurdly small 10 byte chunks: + // + // getBody requests bytes 0-23 .......... callback with bytes 0-9 + // getBody requests bytes 10-23 .......... callback with bytes 10-19 + // getBody requests bytes 20-23 .......... callback with bytes 20-23 + virtual void getBody(const AdjustedByteRange& range, LookupBodyCallback&& cb) = 0; + + // Get the trailers from the cache. Only called if LookupResult::has_trailers + // == true. + virtual void getTrailers(LookupTrailersCallback&& cb) = 0; +}; +using LookupContextPtr = std::unique_ptr; + +// Implement this interface to provide a cache implementation for use by +// CacheFilter. +class HttpCache { +public: + // Returns a LookupContextPtr to manage the state of a cache lookup. On a cache + // miss, the returned LookupContext will be given to the insert call (if any). + virtual LookupContextPtr makeLookupContext(LookupRequest&& request) = 0; + + // Returns an InsertContextPtr to manage the state of a cache insertion. + // Responses with a chunked transfer-encoding must be dechunked before + // insertion. + virtual InsertContextPtr makeInsertContext( + LookupContextPtr&& lookup_context) = 0; + + // Precondition: lookup_context represents a prior cache lookup that required + // validation. + // + // Update the headers of that cache entry to match response_headers. The cache + // entry's body and trailers (if any) will not be modified. + // + // This is called when an expired cache entry is successfully validated, to + // update the cache entry. + virtual void updateHeaders(LookupContextPtr&& lookup_context, + Http::HeaderMapPtr&& response_headers) = 0; + + // Returns statically known information about a cache. + virtual CacheInfo cacheInfo() const = 0; + + virtual ~HttpCache() = default; +}; + +// Factory interface for cache implementations to implement and register. +class HttpCacheFactory { +public: + explicit HttpCacheFactory(std::string name) : name_(std::move(name)) {} + const std::string& name() const { return name_; } + + // Returns an HttpCache that will remain valid indefinitely (at least as long + // as the calling CacheFilter). + virtual HttpCache& getCache() = 0; + virtual ~HttpCacheFactory() = default; + +private: + std::string name_; +}; +using HttpCacheFactoryPtr = std::unique_ptr; +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc new file mode 100644 index 0000000000000..b82cbe0dce322 --- /dev/null +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -0,0 +1,172 @@ +#include "extensions/filters/http/cache/http_cache_utils.h" + +#include + +#include "absl/algorithm/container.h" +#include "absl/strings/ascii.h" +#include "absl/strings/numbers.h" +#include "absl/strings/strip.h" + +using absl::ascii_isalnum; +using absl::ascii_isdigit; +using absl::Duration; +using absl::InfiniteDuration; +using absl::InfinitePast; +using absl::ParseTime; +using absl::Seconds; +using absl::SimpleAtoi; +using absl::string_view; +using absl::Time; +using Envoy::Http::HeaderEntry; +using std::string; + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { +namespace Internal { + +namespace { + +bool tchar(char c) { + switch (c) { + case '!': + case '#': + case '$': + case '%': + case '&': + case '*': + case '+': + case '-': + case '.': + case '^': + case '_': + case '`': + case '|': + case '~': + return true; + } + return ascii_isalnum(c); +} + +bool eatToken(string_view& s) { + const string_view::iterator token_end = c_find_if_not(s, &tchar); + if (token_end == s.begin()) { + return false; + } + s.remove_prefix(token_end - s.begin()); + return true; +} + +void eatDirectiveArgument(string_view& s) { + if (s.empty()) { + return; + } + if (s.front() == '"') { + // TODO(toddmgreer) handle \-escaped quotes + const size_t closing_quote = s.find('"', 1); + s.remove_prefix(closing_quote); + } else { + eatToken(s); + } +} +} // namespace + +// If s is nonnull and begins with decimal digits, return Eat leading digits in +// *s, if any +Duration eatLeadingDuration(string_view* s) { + const string_view::iterator digits_end = c_find_if_not(*s, &ascii_isdigit); + const size_t digits_length = digits_end - s->begin(); + if (digits_length == 0) { + return Seconds(0); + } + const string_view digits(s->begin(), digits_length); + s->remove_prefix(digits_length); + uint64_t num; + return SimpleAtoi(digits, &num) ? Seconds(num) : InfiniteDuration(); +} + +Duration effectiveMaxAge(string_view cache_control) { + // The grammar for This Cache-Control header value should be: + // Cache-Control = 1#cache-directive + // cache-directive = token [ "=" ( token / quoted-string ) ] + // token = 1*tchar + // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" + // / "-" / "." / "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA + // quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE + // qdtext = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text + // obs-text = %x80-FF + // quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) + // VCHAR = %x21-7E ; visible (printing) characters + Duration max_age = -InfiniteDuration(); + bool found_s_maxage = false; + while (!cache_control.empty()) { + // Each time through the loop, we eat one cache-directive. Each branch + // either returns or completely eats a cache-directive. + if (ConsumePrefix(&cache_control, "no-cache")) { + if (eatToken(cache_control)) { + // The token wasn't no-cache; it just started that way, so we must + // finish eating this cache-directive. + if (ConsumePrefix(&cache_control, "=")) { + eatDirectiveArgument(cache_control); + } + } else { + // Found a no-cache directive, so validation is required. + return -InfiniteDuration(); + } + } else if (ConsumePrefix(&cache_control, "s-maxage=")) { + max_age = eatLeadingDuration(&cache_control); + found_s_maxage = true; + cache_control = StripLeadingAsciiWhitespace(cache_control); + if (!cache_control.empty() && cache_control[0] != ',') { + // Unexpected text at end of directive + return -InfiniteDuration(); + } + } else if (!found_s_maxage && ConsumePrefix(&cache_control, "max-age=")) { + max_age = eatLeadingDuration(&cache_control); + if (!cache_control.empty() && cache_control[0] != ',') { + // Unexpected text at end of directive + return -InfiniteDuration(); + } + } else if (eatToken(cache_control)) { + // Unknown directive--ignore. + if (ConsumePrefix(&cache_control, "=")) { + eatDirectiveArgument(cache_control); + } + } else { + // This directive starts with illegal characters. Require validation. + return -InfiniteDuration(); + } + // Whichever branch we took should have consumed the entire cache-directive, + // so we just need to eat the delimiter and optional whitespace. + ConsumePrefix(&cache_control, ","); + cache_control = StripLeadingAsciiWhitespace(cache_control); + } + return max_age; +} + +Time httpTime(const HeaderEntry* header_entry) { + if (!header_entry) { + return InfinitePast(); + } + Time time; + const string input(header_entry->value().getStringView()); + + // RFC 7231 7.1.1.1: Acceptable Date/Time Formats: + // Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate + // Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format + // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + const string rfc7231_date_formats[] = {"%a, %d %b %Y %H:%M:%S GMT", "%A, %d-%b-%y %H:%M:%S GMT", + "%a %b %e %H:%M:%S %Y"}; + for (const string& format : rfc7231_date_formats) { + if (ParseTime(format, input, &time, nullptr)) { + return time; + } + } + return InfinitePast(); +} +} // namespace Internal +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/cache/http_cache_utils.h b/source/extensions/filters/http/cache/http_cache_utils.h new file mode 100644 index 0000000000000..9e0f5945d3283 --- /dev/null +++ b/source/extensions/filters/http/cache/http_cache_utils.h @@ -0,0 +1,25 @@ +#pragma once + +#include "envoy/http/header_map.h" + +#include "absl/strings/string_view.h" +#include "absl/time/time.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { +namespace Internal { +// Parses and returns max-age or s-maxage (with s-maxage taking precedence), +// parsed into a Duration. Returns -InfiniteDuration if neither is present, or +// there is a no-cache directive, or if max-age or s-maxage is malformed. +absl::Duration effectiveMaxAge(absl::string_view cache_control); + +// Parses header_entry as an HTTP time. Returns InifinitePast() if header_entry +// is null or malformed. +absl::Time httpTime(const Http::HeaderEntry* header_entry); +} // namespace Internal +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/cache/key.proto b/source/extensions/filters/http/cache/key.proto new file mode 100644 index 0000000000000..e81a1b71cd752 --- /dev/null +++ b/source/extensions/filters/http/cache/key.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package Envoy.Extensions.HttpFilters.Cache; + +// Cache key for lookups and inserts. +message Key { + string cluster_name = 1; + string host = 2; + string path = 3; + string query = 4; + // True for http://, false for https://. + bool clear_http = 5; + // Cache implementations can store arbitrary content in these fields; never set by cache filter. + repeated bytes custom_fields = 6; + repeated int64 custom_ints = 7; +}; diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc new file mode 100644 index 0000000000000..a7b4c971b072b --- /dev/null +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -0,0 +1,153 @@ +#include "extensions/filters/http/cache/simple_http_cache.h" + +#include "envoy/registry/registry.h" + +#include "common/common/lock_guard.h" +#include "common/http/header_map_impl.h" + +using Envoy::Http::HeaderMap; +using Envoy::Http::HeaderMapImpl; +using Envoy::Http::HeaderMapImplPtr; +using Envoy::Http::HeaderMapPtr; +using Envoy::Registry::RegisterFactory; +using Envoy::Thread::LockGuard; +using std::make_unique; +using std::string; + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { +namespace { + +class SimpleLookupContext : public LookupContext { +public: + explicit SimpleLookupContext(SimpleHttpCache& cache, LookupRequest&& request) + : cache_(cache), request_(std::move(request)) {} + + void getHeaders(LookupHeadersCallback&& cb) override { + auto entry = cache_.lookup(request_); + body_ = std::move(entry.body); + cb(entry.response_headers + ? request_.makeLookupResult(std::move(entry.response_headers), body_.size()) + : LookupResult{}); + } + + void getBody(const AdjustedByteRange& range, LookupBodyCallback&& cb) override { + RELEASE_ASSERT(range.lastBytePos() < body_.length(), "Attempt to read past end of body."); + cb(make_unique(&body_[range.firstBytePos()], + range.lastBytePos() - range.firstBytePos() + 1)); + } + + void getTrailers(LookupTrailersCallback&& cb) override { + // TODO(toddmgreer) Support trailers. + ASSERT(false, "We didn't say there were trailers."); + cb(nullptr); + } + + const LookupRequest& request() const { return request_; } + +private: + SimpleHttpCache& cache_; + const LookupRequest request_; + string body_; +}; + +class SimpleInsertContext : public InsertContext { +public: + SimpleInsertContext(LookupContext& lookup_context, SimpleHttpCache& cache) + : key_(dynamic_cast(lookup_context).request().key()), cache_(cache) {} + + void insertHeaders(const HeaderMap& response_headers, bool end_stream) override { + ASSERT(!committed_); + response_headers_ = make_unique(response_headers); + if (end_stream) { + commit(); + } + } + + void insertBody(const Buffer::Instance& chunk, InsertCallback ready_for_next_chunk, + bool end_stream) override { + ASSERT(!committed_); + ASSERT(ready_for_next_chunk || end_stream); + + body_.add(chunk); + if (end_stream) { + commit(); + } else { + ready_for_next_chunk(true); + } + } + + void insertTrailers(const HeaderMap&) override { + ASSERT(false); // TODO(toddmgreer) support trailers + } + +private: + void commit() { + committed_ = true; + cache_.insert(key_, std::move(response_headers_), body_.toString()); + } + + Key key_; + HeaderMapImplPtr response_headers_; + SimpleHttpCache& cache_; + Buffer::OwnedImpl body_; + bool committed_ = false; +}; +} // namespace + +LookupContextPtr SimpleHttpCache::makeLookupContext(LookupRequest&& request) { + return make_unique(*this, std::move(request)); +} + +void SimpleHttpCache::updateHeaders(LookupContextPtr&& lookup_context, + HeaderMapPtr&& response_headers) { + ASSERT(lookup_context); + ASSERT(response_headers); + // TODO(toddmgreer) Support updating headers. + ASSERT(false); +} + +SimpleHttpCache::Entry SimpleHttpCache::lookup(const LookupRequest& request) { + LockGuard lock(mutex_); + auto iter = map_.find(request.key()); + if (iter == map_.end()) { + return Entry{}; + } + ASSERT(iter->second.response_headers); + return SimpleHttpCache::Entry{make_unique(*iter->second.response_headers), + iter->second.body}; +} + +void SimpleHttpCache::insert(const Key& key, HeaderMapPtr&& response_headers, string&& body) { + LockGuard lock(mutex_); + map_[key] = SimpleHttpCache::Entry{std::move(response_headers), std::move(body)}; +} + +InsertContextPtr SimpleHttpCache::makeInsertContext(LookupContextPtr&& lookup_context) { + ASSERT(lookup_context != nullptr); + return make_unique(*lookup_context, *this); +} + +CacheInfo SimpleHttpCache::cacheInfo() const { + CacheInfo cache_info; + cache_info.name_ = "SimpleHttpCache"; + return cache_info; +} + +class SimpleHttpCacheFactory : public HttpCacheFactory { +public: + SimpleHttpCacheFactory() : HttpCacheFactory("SimpleHttpCache") {} + HttpCache& getCache() override { return cache_; } + +private: + SimpleHttpCache cache_; +}; + +static RegisterFactory register_; + +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/cache/simple_http_cache.h b/source/extensions/filters/http/cache/simple_http_cache.h new file mode 100644 index 0000000000000..16e2651ffe6b7 --- /dev/null +++ b/source/extensions/filters/http/cache/simple_http_cache.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include "common/buffer/buffer_impl.h" +#include "common/common/thread.h" +#include "common/protobuf/utility.h" + +#include "extensions/filters/http/cache/http_cache.h" + +#include "absl/base/thread_annotations.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { + +// Example cache backend that never evicts. Not suitable for production use. +class SimpleHttpCache : public HttpCache { +private: + struct Entry { + Http::HeaderMapPtr response_headers; + std::string body; + }; + +public: + // HttpCache + LookupContextPtr makeLookupContext(LookupRequest&& request) override; + InsertContextPtr makeInsertContext(LookupContextPtr&& lookup_context) override; + void updateHeaders(LookupContextPtr&& lookup_context, + Http::HeaderMapPtr&& response_headers) override; + CacheInfo cacheInfo() const override; + + Entry lookup(const LookupRequest& request); + void insert(const Key& key, Http::HeaderMapPtr&& response_headers, std::string&& body); + + mutable Thread::MutexBasicLockable mutex_; + std::unordered_map map_ GUARDED_BY(mutex_); +}; + +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/http/cache/BUILD b/test/extensions/filters/http/cache/BUILD new file mode 100644 index 0000000000000..1584a016d5fd9 --- /dev/null +++ b/test/extensions/filters/http/cache/BUILD @@ -0,0 +1,43 @@ +licenses(["notice"]) # Apache 2 + +load("//bazel:envoy_build_system.bzl", "envoy_package") +load( + "//test/extensions:extensions_build_system.bzl", + "envoy_extension_cc_test", +) + +envoy_package() + +envoy_extension_cc_test( + name = "http_cache_test", + srcs = ["http_cache_test.cc"], + extension_name = "envoy.filters.http.cache", + deps = [ + "//source/extensions/filters/http/cache:http_cache_lib", + "//test/test_common:simulated_time_system_lib", + "//test/test_common:utility_lib", + ], +) + +envoy_extension_cc_test( + name = "http_cache_utils_test", + srcs = ["http_cache_utils_test.cc"], + extension_name = "envoy.filters.http.cache", + deps = [ + "//include/envoy/http:header_map_interface", + "//source/common/http:header_map_lib", + "//source/extensions/filters/http/cache:http_cache_utils_lib", + "//test/test_common:utility_lib", + ], +) + +envoy_extension_cc_test( + name = "simple_http_cache_test", + srcs = ["simple_http_cache_test.cc"], + extension_name = "envoy.filters.http.cache", + deps = [ + "//source/extensions/filters/http/cache:simple_http_cache_lib", + "//test/test_common:simulated_time_system_lib", + "//test/test_common:utility_lib", + ], +) diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc new file mode 100644 index 0000000000000..898e0d78aa463 --- /dev/null +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -0,0 +1,62 @@ +#include "common/protobuf/utility.h" + +#include "extensions/filters/http/cache/cache_filter.h" + +#include "test/mocks/http/mocks.h" +#include "test/mocks/runtime/mocks.h" +#include "test/mocks/stats/mocks.h" +#include "test/test_common/simulated_time_system.h" +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" + +using testing::Return; + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { + +class CacheFilterTest : public testing::Test { +protected: + CacheFilterTest() { + ON_CALL(runtime_.snapshot_, featureEnabled("cache.filter_enabled", 100)) + .WillByDefault(Return(true)); + } + + void SetUp() override { setUpFilter(R"EOF({"name": "SimpleHttpCache"})EOF"); } + + // CacheFilterTest Helpers + void setUpFilter(std::string&& json) { + Json::ObjectSharedPtr config = Json::Factory::loadFromString(json); + envoy::config::filter::http::cache::v2alpha::Cache cache_options; + MessageUtil::loadFromJson(json, cache_options); + config_ = + std::make_shared(cache_options, "test.", stats_, runtime_, time_system_); + filter_ = absl::make_unique(config_); + } + + void feedBuffer(uint64_t size) { + TestUtility::feedBufferWithRandomCharacters(data_, size); + expected_str_ += data_.toString(); + } + + void doRequest(Http::TestHeaderMapImpl&& headers, bool end_stream) { + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers, end_stream)); + } + + Event::SimulatedTimeSystem time_system_; + CacheFilterConfigSharedPtr config_; + std::unique_ptr filter_; + Buffer::OwnedImpl data_; + std::string expected_str_; + Stats::IsolatedStoreImpl stats_; + NiceMock runtime_; +}; + +TEST_F(CacheFilterTest, basic) {} + +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc new file mode 100644 index 0000000000000..83595020d2295 --- /dev/null +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -0,0 +1,54 @@ +#include "extensions/filters/http/cache/http_cache.h" + +#include "test/test_common/simulated_time_system.h" +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { + +using Event::SimulatedTimeSystem; +using Http::HeaderMapPtr; +using Http::makeHeaderMap; +using Http::TestHeaderMapImpl; + +class LookupRequestTest : public testing::Test { +protected: + SimulatedTimeSystem time_source_; + SystemTime current_time_ = time_source_.systemTime(); + DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; + const TestHeaderMapImpl request_headers_{ + {":path", "/"}, {":scheme", "http"}, {":authority", "example.com"}}; + const LookupRequest lookup_request_{request_headers_, current_time_}; +}; + +TEST_F(LookupRequestTest, makeLookupResult) { + HeaderMapPtr response_headers = makeHeaderMap( + {{"date", formatter_.fromTime(current_time_)}, {"cache-control", "public, max-age=3600"}}); + const LookupResult lookup_response = + lookup_request_.makeLookupResult(std::move(response_headers), 0); + EXPECT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status) + << formatter_.fromTime(current_time_); +} + +TEST_F(LookupRequestTest, PrivateResponse) { + HeaderMapPtr response_headers = makeHeaderMap({{"age", "2"}, + {"cache-control", "private, max-age=3600"}, + {"date", formatter_.fromTime(current_time_)}}); + const LookupResult lookup_response = + lookup_request_.makeLookupResult(std::move(response_headers), 0); + + // We must make sure at cache insertion time, private responses must not be + // inserted. However, if the insertion did happen, it would be served at the + // time of lookup. (Nothing should rely on this.) + EXPECT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status) + << formatter_.fromTime(current_time_); +} + +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/http/cache/http_cache_utils_test.cc b/test/extensions/filters/http/cache/http_cache_utils_test.cc new file mode 100644 index 0000000000000..662a7f18b3c37 --- /dev/null +++ b/test/extensions/filters/http/cache/http_cache_utils_test.cc @@ -0,0 +1,47 @@ +#include "common/http/header_map_impl.h" + +#include "extensions/filters/http/cache/http_cache_utils.h" + +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { +namespace Internal { +namespace { + +using absl::InfinitePast; +using absl::UTCTimeZone; +using Http::TestHeaderMapImpl; +using std::string; +using testing::TestWithParam; +using testing::ValuesIn; + +class HttpTimeTest : public TestWithParam { +protected: + TestHeaderMapImpl response_headers_{{"date", GetParam()}}; +}; + +const string ok_times[] = { + "Sun, 06 Nov 1994 08:49:37 GMT", // IMF-fixdate + "Sunday, 06-Nov-94 08:49:37 GMT", // obsolete RFC 850 format + "Sun Nov 6 08:49:37 1994" // ANSI C's asctime() format +}; + +INSTANTIATE_TEST_SUITE_P(Ok, HttpTimeTest, ValuesIn(ok_times)); + +TEST_P(HttpTimeTest, Ok) { + EXPECT_EQ(FormatTime(httpTime(response_headers_.Date()), UTCTimeZone()), + "1994-11-06T08:49:37+00:00"); +} + +TEST(HttpTime, Null) { EXPECT_EQ(httpTime(nullptr), InfinitePast()); } +} // namespace +} // namespace Internal +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc new file mode 100644 index 0000000000000..7631ab168c0ab --- /dev/null +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -0,0 +1,248 @@ +#include "envoy/http/header_map.h" +#include "envoy/registry/registry.h" + +#include "common/buffer/buffer_impl.h" + +#include "extensions/filters/http/cache/simple_http_cache.h" + +#include "test/test_common/simulated_time_system.h" +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { + +using absl::string_view; +using Event::SimulatedTimeSystem; +using Http::Headers; +using Http::TestHeaderMapImpl; +using Registry::FactoryRegistry; +using std::string; +using testing::Combine; +using testing::get; +using testing::tuple; +using testing::Values; +using testing::ValuesIn; +using testing::WithParamInterface; + +static constexpr char kEpochDate[] = "Thu, 01 Jan 1970 00:00:00 GMT"; + +class SimpleHttpCacheTest : public testing::Test { +protected: + SimpleHttpCacheTest() { + request_headers_.insertMethod().value(string_view(("GET"))); + request_headers_.insertHost().value(string_view(("example.com"))); + request_headers_.insertScheme().value(string_view(("https"))); + request_headers_.insertCacheControl().value(string_view(("max-age=3600"))); + } + + // Performs a cache lookup. + LookupContextPtr lookup(string_view request_path) { + LookupRequest request = makeLookupRequest(request_path); + LookupContextPtr context = cache_.makeLookupContext(std::move(request)); + context->getHeaders([this](LookupResult&& result) { lookup_result_ = std::move(result); }); + return context; + } + + // Inserts a value into the cache. + void insert(LookupContextPtr lookup, const TestHeaderMapImpl& response_headers, + const string_view response_body) { + InsertContextPtr inserter = cache_.makeInsertContext(move(lookup)); + inserter->insertHeaders(response_headers, false); + inserter->insertBody(Buffer::OwnedImpl(response_body), nullptr, true); + } + + void insert(string_view request_path, const TestHeaderMapImpl& response_headers, + const string_view response_body) { + insert(lookup(request_path), response_headers, response_body); + } + + string getBody(LookupContext& context, int start, int end) { + AdjustedByteRange range(start, end); + string body; + context.getBody(range, [&body](Buffer::InstancePtr&& data) { + EXPECT_NE(data, nullptr); + if (data) { + body = data->toString(); + } + }); + return body; + } + + LookupRequest makeLookupRequest(string_view request_path) { + request_headers_.insertPath().value(request_path); + return LookupRequest(request_headers_, current_time_); + }; + + AssertionResult ExpectLookupSuccessWithBody(LookupContext* lookup_context, string_view body) { + if (lookup_result_.cache_entry_status != CacheEntryStatus::Ok) { + return AssertionFailure() << "Expected: lookup_result_.cache_entry_status == " + "CacheEntryStatus::Ok\n Actual: " + << lookup_result_.cache_entry_status; + } + if (!lookup_result_.headers) { + return AssertionFailure() << "Expected nonnull lookup_result_.headers"; + } + if (!lookup_context) { + return AssertionFailure() << "Expected nonnull lookup_context"; + } + const string actual_body = getBody(*lookup_context, 0, body.size() - 1); + if (body != actual_body) { + return AssertionFailure() << "Expected body == " << body << "\n Actual: " << actual_body; + } + return AssertionSuccess(); + } + + SimpleHttpCache cache_; + LookupResult lookup_result_; + TestHeaderMapImpl request_headers_; + SimulatedTimeSystem time_source_; + SystemTime current_time_ = time_source_.systemTime(); + DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; +}; + +// Simple flow of putting in an item, getting it, deleting it. +TEST_F(SimpleHttpCacheTest, PutGet) { + const string kRequestPath1("Name"); + LookupContextPtr name_lookup_context = lookup(kRequestPath1); + EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); + + TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, + {"cache-control", "public, max-age=3600"}}; + + const string kBody1("Value"); + insert(move(name_lookup_context), response_headers, kBody1); + name_lookup_context = lookup(kRequestPath1); + EXPECT_TRUE(ExpectLookupSuccessWithBody(name_lookup_context.get(), kBody1)); + + const string& kRequestPath2("Another Name"); + LookupContextPtr another_name_lookup_context = lookup(kRequestPath2); + EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); + + const string kNewBody1("NewValue"); + insert(move(name_lookup_context), response_headers, kNewBody1); + EXPECT_TRUE(ExpectLookupSuccessWithBody(lookup(kRequestPath1).get(), kNewBody1)); +} + +TEST_F(SimpleHttpCacheTest, PrivateResponse) { + TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, + {"age", "2"}, + {"cache-control", "private, max-age=3600"}}; + const string request_path("Name"); + + LookupContextPtr name_lookup_context = lookup(request_path); + EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); + + const string kBody("Value"); + // We must make sure at cache insertion time, private responses must not be + // inserted. However, if the insertion did happen, it would be served at the + // time of lookup. + insert(move(name_lookup_context), response_headers, kBody); + EXPECT_TRUE(ExpectLookupSuccessWithBody(lookup(request_path).get(), kBody)); +} + +TEST_F(SimpleHttpCacheTest, Miss) { + LookupContextPtr name_lookup_context = lookup("Name"); + EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); +} + +class SimpleHttpCacheTest1Response + : public SimpleHttpCacheTest, + public WithParamInterface> { +protected: + CacheEntryStatus expectedStatus() const { return get(GetParam()); } + const TestHeaderMapImpl& responseHeaders() const { return get(GetParam()); } +}; + +TEST_P(SimpleHttpCacheTest1Response, Expired) { + TestHeaderMapImpl response_headers = responseHeaders(); + // TODO(toddmgreer) Test with various date headers. + response_headers.addCopy("date", formatter_.fromTime(current_time_)); + insert("/", response_headers, ""); + lookup("/"); + EXPECT_EQ(expectedStatus(), lookup_result_.cache_entry_status); +} + +auto expiredHeaders() { + const TestHeaderMapImpl expired_headers[] = { + {{"date", "Thu, 01 Jan 2100 00:00:00 GMT"}, {"cache-control", "public, max-age=3600"}}, + {{"cache-control", "public, s-max-age=-1"}}, + {{"cache-control", "public, max-age=-1"}}, + {{"date", "foo"}}, + {{"date", kEpochDate}, {"expires", "foo"}}, + {{"expires", kEpochDate}, {"cache-control", "public"}}, + {{"age", "2"}, {"cache-control", "public"}}, + {{"age", "6000"}}}; + return ValuesIn(expired_headers); +} + +auto okHeaders() { + const TestHeaderMapImpl ok_headers[] = {{{"cache-control", "public, max-age=3600"}}}; + return ValuesIn(ok_headers); +}; + +INSTANTIATE_TEST_SUITE_P(Expired, SimpleHttpCacheTest1Response, + Combine(Values(CacheEntryStatus::RequiresValidation), expiredHeaders())); + +INSTANTIATE_TEST_SUITE_P(Ok, SimpleHttpCacheTest1Response, + Combine(Values(CacheEntryStatus::Ok), okHeaders())); + +TEST_F(SimpleHttpCacheTest, RequestSmallMinFresh) { + request_headers_.setReferenceKey(Headers::get().CacheControl, "min-fresh=1000"); + const string request_path("Name"); + LookupContextPtr name_lookup_context = lookup(request_path); + EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); + + TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, + {"age", "6000"}, + {"cache-control", "public, max-age=9000"}}; + const string kBody("Value"); + insert(move(name_lookup_context), response_headers, kBody); + EXPECT_TRUE(ExpectLookupSuccessWithBody(lookup(request_path).get(), kBody)); +} + +TEST_F(SimpleHttpCacheTest, ResponseStaleWithRequestLargeMaxStale) { + request_headers_.setReferenceKey(Headers::get().CacheControl, "max-stale=9000"); + + const string request_path("Name"); + LookupContextPtr name_lookup_context = lookup(request_path); + EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); + + TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, + {"age", "7200"}, + {"cache-control", "public, max-age=3600"}}; + + const string kBody("Value"); + insert(move(name_lookup_context), response_headers, kBody); + EXPECT_TRUE(ExpectLookupSuccessWithBody(lookup(request_path).get(), kBody)); +} + +TEST_F(SimpleHttpCacheTest, StreamingPut) { + TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, + {"age", "2"}, + {"cache-control", "public, max-age=3600"}}; + InsertContextPtr inserter = cache_.makeInsertContext(lookup("request_path")); + inserter->insertHeaders(response_headers, false); + inserter->insertBody( + Buffer::OwnedImpl("Hello, "), [](bool ready) { EXPECT_TRUE(ready); }, false); + inserter->insertBody(Buffer::OwnedImpl("World!"), nullptr, true); + LookupContextPtr name_lookup_context = lookup("request_path"); + EXPECT_EQ(CacheEntryStatus::Ok, lookup_result_.cache_entry_status); + EXPECT_NE(nullptr, lookup_result_.headers); + ASSERT_EQ(13, lookup_result_.content_length); + EXPECT_EQ("Hello, World!", getBody(*name_lookup_context, 0, 12)); +} + +TEST(Registration, getFactory) { + HttpCacheFactory* factory = FactoryRegistry::getFactory("SimpleHttpCache"); + ASSERT_NE(factory, nullptr); + EXPECT_EQ(factory->getCache().cacheInfo().name_, "SimpleHttpCache"); +} + +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy From d5bb1c5345bb1e4b6f26ed21316c724437a676fa Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 7 Jun 2019 11:55:25 -0700 Subject: [PATCH 003/109] Add cache/config to BUILD file. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/BUILD | 14 ++++++++++++++ source/extensions/filters/http/cache/config.cc | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index 55630ce15598a..1e22ffabb9e2c 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -27,6 +27,20 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "config_lib", + srcs = ["config.cc"], + hdrs = ["config.h"], + deps = [ + ":cache_filter_lib", + ":key_cc", + "//include/envoy/common:time_interface", + "//include/envoy/registry", + "//include/envoy/stats:stats_interface", + "//source/extensions/filters/http/common:factory_base_lib", + ], +) + envoy_proto_library( name = "key", srcs = ["key.proto"], diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index 4595490389e1e..2e3873d802fc2 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -21,7 +21,7 @@ using std::make_shared; using std::string; Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v2alpha::Cache& config_proto, + const envoy::config::filter::http::cache::v2alpha::Cache& config, const string& stats_prefix, FactoryContext& context) { return [config, stats_prefix, &context](FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter( From 72f3beaf53d3e89954a2a8d5db02ab0e85546811 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 7 Jun 2019 11:58:52 -0700 Subject: [PATCH 004/109] Fix formatting. Signed-off-by: Todd Greer --- .../extensions/filters/http/cache/config.cc | 4 +- .../filters/http/cache/http_cache.h | 77 +++++++++---------- .../filters/http/cache/simple_http_cache.cc | 2 +- .../filters/http/cache/cache_filter_test.cc | 2 +- 4 files changed, 41 insertions(+), 44 deletions(-) diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index 2e3873d802fc2..699135bb3e148 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -21,8 +21,8 @@ using std::make_shared; using std::string; Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v2alpha::Cache& config, - const string& stats_prefix, FactoryContext& context) { + const envoy::config::filter::http::cache::v2alpha::Cache& config, const string& stats_prefix, + FactoryContext& context) { return [config, stats_prefix, &context](FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter( make_shared(config, stats_prefix, context.scope(), context.timeSource())); diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 7d7df0ab46d86..c72ef56d34b15 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -36,14 +36,13 @@ enum CacheEntryStatus { // Byte range from an HTTP request. class RawByteRange { public: - // - If first==UINT64_MAX, construct a RawByteRange requesting the final last - // body bytes. - // - Otherwise, construct a RawByteRange requesting the [first,last] body - // bytes. Prereq: first == UINT64_MAX || first <= last Invariant: isSuffix() || - // firstBytePos() <= lastBytePos - RawByteRange(uint64_t first, uint64_t last) - : first_byte_pos_(first), last_byte_pos_(last) { - RELEASE_ASSERT(isSuffix() || first <= last, "Illegal byte range."); + // - If first==UINT64_MAX, construct a RawByteRange requesting the final last + // body bytes. + // - Otherwise, construct a RawByteRange requesting the [first,last] body + // bytes. Prereq: first == UINT64_MAX || first <= last Invariant: isSuffix() || + // firstBytePos() <= lastBytePos + RawByteRange(uint64_t first, uint64_t last) : first_byte_pos_(first), last_byte_pos_(last) { + RELEASE_ASSERT(isSuffix() || first <= last, "Illegal byte range."); } bool isSuffix() const { return first_byte_pos_ == UINT64_MAX; } uint64_t firstBytePos() const { @@ -67,12 +66,11 @@ class RawByteRange { // Byte range from an HTTP request, adjusted for a known response body size. class AdjustedByteRange { public: - // Construct an AdjustedByteRange representing the [first,last] bytes in the - // response body. Prereq: first <= last Invariant: firstBytePos() <= - // lastBytePos() - AdjustedByteRange(uint64_t first, uint64_t last) - : first_byte_pos_(first), last_byte_pos_(last) { - ASSERT(first <= last); + // Construct an AdjustedByteRange representing the [first,last] bytes in the + // response body. Prereq: first <= last Invariant: firstBytePos() <= + // lastBytePos() + AdjustedByteRange(uint64_t first, uint64_t last) : first_byte_pos_(first), last_byte_pos_(last) { + ASSERT(first <= last); } uint64_t firstBytePos() const { return first_byte_pos_; } uint64_t lastBytePos() const { return last_byte_pos_; } @@ -244,7 +242,7 @@ class LookupContext { // getBody requests bytes 20-23 .......... callback with bytes 20-23 virtual void getBody(const AdjustedByteRange& range, LookupBodyCallback&& cb) = 0; - // Get the trailers from the cache. Only called if LookupResult::has_trailers + // Get the trailers from the cache. Only called if LookupResult::has_trailers // == true. virtual void getTrailers(LookupTrailersCallback&& cb) = 0; }; @@ -254,31 +252,30 @@ using LookupContextPtr = std::unique_ptr; // CacheFilter. class HttpCache { public: - // Returns a LookupContextPtr to manage the state of a cache lookup. On a cache - // miss, the returned LookupContext will be given to the insert call (if any). - virtual LookupContextPtr makeLookupContext(LookupRequest&& request) = 0; - - // Returns an InsertContextPtr to manage the state of a cache insertion. - // Responses with a chunked transfer-encoding must be dechunked before - // insertion. - virtual InsertContextPtr makeInsertContext( - LookupContextPtr&& lookup_context) = 0; - - // Precondition: lookup_context represents a prior cache lookup that required - // validation. - // - // Update the headers of that cache entry to match response_headers. The cache - // entry's body and trailers (if any) will not be modified. - // - // This is called when an expired cache entry is successfully validated, to - // update the cache entry. - virtual void updateHeaders(LookupContextPtr&& lookup_context, - Http::HeaderMapPtr&& response_headers) = 0; - - // Returns statically known information about a cache. - virtual CacheInfo cacheInfo() const = 0; - - virtual ~HttpCache() = default; + // Returns a LookupContextPtr to manage the state of a cache lookup. On a cache + // miss, the returned LookupContext will be given to the insert call (if any). + virtual LookupContextPtr makeLookupContext(LookupRequest&& request) = 0; + + // Returns an InsertContextPtr to manage the state of a cache insertion. + // Responses with a chunked transfer-encoding must be dechunked before + // insertion. + virtual InsertContextPtr makeInsertContext(LookupContextPtr&& lookup_context) = 0; + + // Precondition: lookup_context represents a prior cache lookup that required + // validation. + // + // Update the headers of that cache entry to match response_headers. The cache + // entry's body and trailers (if any) will not be modified. + // + // This is called when an expired cache entry is successfully validated, to + // update the cache entry. + virtual void updateHeaders(LookupContextPtr&& lookup_context, + Http::HeaderMapPtr&& response_headers) = 0; + + // Returns statically known information about a cache. + virtual CacheInfo cacheInfo() const = 0; + + virtual ~HttpCache() = default; }; // Factory interface for cache implementations to implement and register. diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index a7b4c971b072b..c742019b15cee 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -80,7 +80,7 @@ class SimpleInsertContext : public InsertContext { } void insertTrailers(const HeaderMap&) override { - ASSERT(false); // TODO(toddmgreer) support trailers + ASSERT(false); // TODO(toddmgreer) support trailers } private: diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index 898e0d78aa463..0ca699d5b1e43 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -33,7 +33,7 @@ class CacheFilterTest : public testing::Test { MessageUtil::loadFromJson(json, cache_options); config_ = std::make_shared(cache_options, "test.", stats_, runtime_, time_system_); - filter_ = absl::make_unique(config_); + filter_ = std::make_unique(config_); } void feedBuffer(uint64_t size) { From 0611beddcd93663601628af75ee9f82a4b84151d Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 7 Jun 2019 15:54:51 -0700 Subject: [PATCH 005/109] Fix spelling, and expand spellcheck's dictionary. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/http_cache.h | 4 ++-- tools/spelling_dictionary.txt | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index c72ef56d34b15..d78ba8d788461 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -140,7 +140,7 @@ class LookupRequest { // Prereq: request_headers's Path(), Scheme(), and Host() are nonnull. LookupRequest(const Http::HeaderMap& request_headers, SystemTime timestamp); - // Caches may modify the key accoring to local needs, though care must be + // Caches may modify the key according to local needs, though care must be // taken to ensure that meaningfully distinct responses have distinct keys. const Key& key() const { return key_; } Key& key() { return key_; } @@ -161,7 +161,7 @@ class LookupRequest { // validation logic. // - LookupResult::headers takes ownership of response_headers. // - LookupResult::content_length == content_length. - // - LookupResult::response_ranges entries are satisifiable (as documented + // - LookupResult::response_ranges entries are satisfiable (as documented // there). LookupResult makeLookupResult(Http::HeaderMapPtr&& response_headers, uint64_t content_length) const; diff --git a/tools/spelling_dictionary.txt b/tools/spelling_dictionary.txt index ced4cb24a3d71..51e09a5a3da04 100644 --- a/tools/spelling_dictionary.txt +++ b/tools/spelling_dictionary.txt @@ -284,7 +284,11 @@ WS Welford's Werror XDS +dechunk +dechunked +satisfiable sendto +unsatisfiable vip xDSes XFCC From 312397183400e1e1185afec9fc5506cf449d5a72 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 7 Jun 2019 19:14:25 -0700 Subject: [PATCH 006/109] Rename config_lib to config. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index 1e22ffabb9e2c..fedbfa5319151 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -28,7 +28,7 @@ envoy_cc_library( ) envoy_cc_library( - name = "config_lib", + name = "config", srcs = ["config.cc"], hdrs = ["config.h"], deps = [ From 424e5bcb4051775820fc15054f52b1ca074ffe7a Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 7 Jun 2019 19:30:15 -0700 Subject: [PATCH 007/109] Add some RFC terms to spelling_dictionary.txt. Signed-off-by: Todd Greer --- tools/spelling_dictionary.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/spelling_dictionary.txt b/tools/spelling_dictionary.txt index 51e09a5a3da04..ae93e69f9e93e 100644 --- a/tools/spelling_dictionary.txt +++ b/tools/spelling_dictionary.txt @@ -53,6 +53,7 @@ DGRAM DHE DLOG DNS +DQUOTE DRYs DS DST @@ -109,6 +110,7 @@ HCM HDS HMAC HPACK +HTAB HTML HTTP HTTPS @@ -126,6 +128,7 @@ IPV IPs IPv ITOA +InifinitePast Isode JSON JSONs @@ -275,6 +278,7 @@ UTF UUID UUIDs VC +VCHAR VH VHDS VLOG @@ -284,10 +288,13 @@ WS Welford's Werror XDS +asctime dechunk dechunked +qdtext satisfiable sendto +tchar unsatisfiable vip xDSes From d77ff8c8921d03acb82511de0d308e1b53bdcd8b Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Mon, 10 Jun 2019 16:54:32 -0700 Subject: [PATCH 008/109] Address clang-tidy findings. Signed-off-by: Todd Greer --- .../filters/http/cache/cache_filter.cc | 6 +-- .../filters/http/cache/cache_filter.h | 6 +-- source/extensions/filters/http/cache/config.h | 1 + .../filters/http/cache/http_cache.cc | 4 +- .../filters/http/cache/http_cache_utils.cc | 9 +++-- .../http/cache/http_cache_utils_test.cc | 4 +- .../http/cache/simple_http_cache_test.cc | 40 +++++++++---------- 7 files changed, 37 insertions(+), 33 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index b3fff73f831ff..0db06cc9e5e8a 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -122,7 +122,7 @@ void CacheFilter::onUnusableHeaders() { } } -void CacheFilter::onHeadersAsync(CacheFilterSharedPtr self, LookupResult&& result) { +void CacheFilter::onHeadersAsync(const CacheFilterSharedPtr& self, LookupResult&& result) { switch (result.cache_entry_status) { case CacheEntryStatus::RequiresValidation: case CacheEntryStatus::FoundNotModified: @@ -151,7 +151,7 @@ void CacheFilter::getBody() { [self](Buffer::InstancePtr&& body) { self->onBody(std::move(body)); }); } -void CacheFilter::onBodyAsync(CacheFilterSharedPtr self, Buffer::InstancePtr&& body) { +void CacheFilter::onBodyAsync(const CacheFilterSharedPtr& self, Buffer::InstancePtr&& body) { self->post([self, body = body.release()] { self->onBody(WrapUnique(body)); }); } @@ -193,7 +193,7 @@ void CacheFilter::onTrailers(HeaderMapPtr&& trailers) { } } -void CacheFilter::onTrailersAsync(CacheFilterSharedPtr self, HeaderMapPtr&& trailers) { +void CacheFilter::onTrailersAsync(const CacheFilterSharedPtr& self, HeaderMapPtr&& trailers) { self->post([self, trailers = trailers.release()] { self->onTrailers(WrapUnique(trailers)); }); } diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index f071838b46384..4171a0b95b936 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -43,9 +43,9 @@ class CacheFilter : public Http::PassThroughFilter, void onUnusableHeaders(); void onBody(Envoy::Buffer::InstancePtr&& body); void onTrailers(Http::HeaderMapPtr&& trailers); - static void onHeadersAsync(CacheFilterSharedPtr self, LookupResult&& result); - static void onBodyAsync(CacheFilterSharedPtr self, Envoy::Buffer::InstancePtr&& body); - static void onTrailersAsync(CacheFilterSharedPtr self, Http::HeaderMapPtr&& trailers); + static void onHeadersAsync(const CacheFilterSharedPtr& self, LookupResult&& result); + static void onBodyAsync(const CacheFilterSharedPtr& self, Envoy::Buffer::InstancePtr&& body); + static void onTrailersAsync(const CacheFilterSharedPtr& self, Http::HeaderMapPtr&& trailers); void post(std::function f) const; TimeSource& time_source_; diff --git a/source/extensions/filters/http/cache/config.h b/source/extensions/filters/http/cache/config.h index 3d26ed71afb0e..5b19f64a0da14 100644 --- a/source/extensions/filters/http/cache/config.h +++ b/source/extensions/filters/http/cache/config.h @@ -1,6 +1,7 @@ #pragma once #include "envoy/config/filter/http/cache/v2alpha/cache.pb.h" +#include "envoy/config/filter/http/cache/v2alpha/cache.pb.validate.h" #include "extensions/filters/http/cache/cache_filter.h" #include "extensions/filters/http/common/factory_base.h" diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index 4fed11c22540a..38b3bec3cf01a 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -10,7 +10,7 @@ using absl::Duration; using absl::FromChrono; -using absl::Seconds; +using absl::ZeroDuration; using Envoy::Extensions::HttpFilters::Cache::Internal::effectiveMaxAge; using Envoy::Extensions::HttpFilters::Cache::Internal::httpTime; using Envoy::Http::HeaderEntry; @@ -60,7 +60,7 @@ bool LookupRequest::fresh(const HeaderMap& response_headers) const { if (cache_control_header) { const Duration effective_max_age = effectiveMaxAge(cache_control_header->value().getStringView()); - if (effective_max_age >= Seconds(0)) { + if (effective_max_age >= ZeroDuration()) { return FromChrono(timestamp_) - httpTime(response_headers.Date()) < effective_max_age; } } diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index b82cbe0dce322..6345d79a3985c 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -1,5 +1,6 @@ #include "extensions/filters/http/cache/http_cache_utils.h" +#include #include #include "absl/algorithm/container.h" @@ -17,7 +18,9 @@ using absl::Seconds; using absl::SimpleAtoi; using absl::string_view; using absl::Time; +using absl::ZeroDuration; using Envoy::Http::HeaderEntry; +using std::array; using std::string; namespace Envoy { @@ -78,7 +81,7 @@ Duration eatLeadingDuration(string_view* s) { const string_view::iterator digits_end = c_find_if_not(*s, &ascii_isdigit); const size_t digits_length = digits_end - s->begin(); if (digits_length == 0) { - return Seconds(0); + return ZeroDuration(); } const string_view digits(s->begin(), digits_length); s->remove_prefix(digits_length); @@ -156,8 +159,8 @@ Time httpTime(const HeaderEntry* header_entry) { // Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate // Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format - const string rfc7231_date_formats[] = {"%a, %d %b %Y %H:%M:%S GMT", "%A, %d-%b-%y %H:%M:%S GMT", - "%a %b %e %H:%M:%S %Y"}; + const array rfc7231_date_formats = { + "%a, %d %b %Y %H:%M:%S GMT", "%A, %d-%b-%y %H:%M:%S GMT", "%a %b %e %H:%M:%S %Y"}; for (const string& format : rfc7231_date_formats) { if (ParseTime(format, input, &time, nullptr)) { return time; diff --git a/test/extensions/filters/http/cache/http_cache_utils_test.cc b/test/extensions/filters/http/cache/http_cache_utils_test.cc index 662a7f18b3c37..4909f2c3506b4 100644 --- a/test/extensions/filters/http/cache/http_cache_utils_test.cc +++ b/test/extensions/filters/http/cache/http_cache_utils_test.cc @@ -1,3 +1,5 @@ +#include + #include "common/http/header_map_impl.h" #include "extensions/filters/http/cache/http_cache_utils.h" @@ -25,7 +27,7 @@ class HttpTimeTest : public TestWithParam { TestHeaderMapImpl response_headers_{{"date", GetParam()}}; }; -const string ok_times[] = { +const std::vector ok_times = { "Sun, 06 Nov 1994 08:49:37 GMT", // IMF-fixdate "Sunday, 06-Nov-94 08:49:37 GMT", // obsolete RFC 850 format "Sun Nov 6 08:49:37 1994" // ANSI C's asctime() format diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index 7631ab168c0ab..bd9aa65097f13 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -14,6 +14,7 @@ namespace Envoy { namespace Extensions { namespace HttpFilters { namespace Cache { +namespace { using absl::string_view; using Event::SimulatedTimeSystem; @@ -21,6 +22,7 @@ using Http::Headers; using Http::TestHeaderMapImpl; using Registry::FactoryRegistry; using std::string; +using std::vector; using testing::Combine; using testing::get; using testing::tuple; @@ -28,7 +30,7 @@ using testing::Values; using testing::ValuesIn; using testing::WithParamInterface; -static constexpr char kEpochDate[] = "Thu, 01 Jan 1970 00:00:00 GMT"; +const string kEpochDate = "Thu, 01 Jan 1970 00:00:00 GMT"; class SimpleHttpCacheTest : public testing::Test { protected: @@ -60,7 +62,7 @@ class SimpleHttpCacheTest : public testing::Test { insert(lookup(request_path), response_headers, response_body); } - string getBody(LookupContext& context, int start, int end) { + string getBody(LookupContext& context, uint64_t start, uint64_t end) { AdjustedByteRange range(start, end); string body; context.getBody(range, [&body](Buffer::InstancePtr&& data) { @@ -89,7 +91,7 @@ class SimpleHttpCacheTest : public testing::Test { if (!lookup_context) { return AssertionFailure() << "Expected nonnull lookup_context"; } - const string actual_body = getBody(*lookup_context, 0, body.size() - 1); + const string actual_body = getBody(*lookup_context, 0, body.size() - 1u); if (body != actual_body) { return AssertionFailure() << "Expected body == " << body << "\n Actual: " << actual_body; } @@ -166,29 +168,24 @@ TEST_P(SimpleHttpCacheTest1Response, Expired) { EXPECT_EQ(expectedStatus(), lookup_result_.cache_entry_status); } -auto expiredHeaders() { - const TestHeaderMapImpl expired_headers[] = { - {{"date", "Thu, 01 Jan 2100 00:00:00 GMT"}, {"cache-control", "public, max-age=3600"}}, - {{"cache-control", "public, s-max-age=-1"}}, - {{"cache-control", "public, max-age=-1"}}, - {{"date", "foo"}}, - {{"date", kEpochDate}, {"expires", "foo"}}, - {{"expires", kEpochDate}, {"cache-control", "public"}}, - {{"age", "2"}, {"cache-control", "public"}}, - {{"age", "6000"}}}; - return ValuesIn(expired_headers); -} +const vector expired_headers = { + {{"date", "Thu, 01 Jan 2100 00:00:00 GMT"}, {"cache-control", "public, max-age=3600"}}, + {{"cache-control", "public, s-max-age=-1"}}, + {{"cache-control", "public, max-age=-1"}}, + {{"date", "foo"}}, + {{"date", kEpochDate}, {"expires", "foo"}}, + {{"expires", kEpochDate}, {"cache-control", "public"}}, + {{"age", "2"}, {"cache-control", "public"}}, + {{"age", "6000"}}}; -auto okHeaders() { - const TestHeaderMapImpl ok_headers[] = {{{"cache-control", "public, max-age=3600"}}}; - return ValuesIn(ok_headers); -}; +const vector ok_headers = {{{"cache-control", "public, max-age=3600"}}}; INSTANTIATE_TEST_SUITE_P(Expired, SimpleHttpCacheTest1Response, - Combine(Values(CacheEntryStatus::RequiresValidation), expiredHeaders())); + Combine(Values(CacheEntryStatus::RequiresValidation), + ValuesIn(expired_headers))); INSTANTIATE_TEST_SUITE_P(Ok, SimpleHttpCacheTest1Response, - Combine(Values(CacheEntryStatus::Ok), okHeaders())); + Combine(Values(CacheEntryStatus::Ok), ValuesIn(ok_headers))); TEST_F(SimpleHttpCacheTest, RequestSmallMinFresh) { request_headers_.setReferenceKey(Headers::get().CacheControl, "min-fresh=1000"); @@ -242,6 +239,7 @@ TEST(Registration, getFactory) { EXPECT_EQ(factory->getCache().cacheInfo().name_, "SimpleHttpCache"); } +} // namespace } // namespace Cache } // namespace HttpFilters } // namespace Extensions From b144193f8a6cfdb9a491e33dc63e0621cbd194ce Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Mon, 10 Jun 2019 17:34:36 -0700 Subject: [PATCH 009/109] Remove cache_filter_test until it has actual tests to run. Signed-off-by: Todd Greer --- .../filters/http/cache/cache_filter_test.cc | 62 ------------------- 1 file changed, 62 deletions(-) delete mode 100644 test/extensions/filters/http/cache/cache_filter_test.cc diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc deleted file mode 100644 index 0ca699d5b1e43..0000000000000 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ /dev/null @@ -1,62 +0,0 @@ -#include "common/protobuf/utility.h" - -#include "extensions/filters/http/cache/cache_filter.h" - -#include "test/mocks/http/mocks.h" -#include "test/mocks/runtime/mocks.h" -#include "test/mocks/stats/mocks.h" -#include "test/test_common/simulated_time_system.h" -#include "test/test_common/utility.h" - -#include "gtest/gtest.h" - -using testing::Return; - -namespace Envoy { -namespace Extensions { -namespace HttpFilters { -namespace Cache { - -class CacheFilterTest : public testing::Test { -protected: - CacheFilterTest() { - ON_CALL(runtime_.snapshot_, featureEnabled("cache.filter_enabled", 100)) - .WillByDefault(Return(true)); - } - - void SetUp() override { setUpFilter(R"EOF({"name": "SimpleHttpCache"})EOF"); } - - // CacheFilterTest Helpers - void setUpFilter(std::string&& json) { - Json::ObjectSharedPtr config = Json::Factory::loadFromString(json); - envoy::config::filter::http::cache::v2alpha::Cache cache_options; - MessageUtil::loadFromJson(json, cache_options); - config_ = - std::make_shared(cache_options, "test.", stats_, runtime_, time_system_); - filter_ = std::make_unique(config_); - } - - void feedBuffer(uint64_t size) { - TestUtility::feedBufferWithRandomCharacters(data_, size); - expected_str_ += data_.toString(); - } - - void doRequest(Http::TestHeaderMapImpl&& headers, bool end_stream) { - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers, end_stream)); - } - - Event::SimulatedTimeSystem time_system_; - CacheFilterConfigSharedPtr config_; - std::unique_ptr filter_; - Buffer::OwnedImpl data_; - std::string expected_str_; - Stats::IsolatedStoreImpl stats_; - NiceMock runtime_; -}; - -TEST_F(CacheFilterTest, basic) {} - -} // namespace Cache -} // namespace HttpFilters -} // namespace Extensions -} // namespace Envoy From ee2431a9a787da7553a7445513003d4a0c6371bc Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 18 Jun 2019 20:13:44 -0700 Subject: [PATCH 010/109] Address first batch of comments. More to come. * source/common/http/headers.h: Signed-off-by: Todd Greer --- source/common/http/headers.h | 1 + .../filters/http/cache/cache_filter.cc | 70 +++++----- .../extensions/filters/http/cache/config.cc | 16 +-- .../filters/http/cache/http_cache.cc | 48 +++---- .../filters/http/cache/http_cache_utils.cc | 61 ++++----- .../filters/http/cache/simple_http_cache.cc | 42 +++--- .../filters/http/cache/http_cache_test.cc | 18 +-- .../http/cache/http_cache_utils_test.cc | 19 +-- .../http/cache/simple_http_cache_test.cc | 127 +++++++++--------- 9 files changed, 172 insertions(+), 230 deletions(-) diff --git a/source/common/http/headers.h b/source/common/http/headers.h index fd2a7251579f5..e611f867a7ee0 100644 --- a/source/common/http/headers.h +++ b/source/common/http/headers.h @@ -124,6 +124,7 @@ class HeaderValues { const std::string NoCache{"no-cache"}; const std::string NoCacheMaxAge0{"no-cache, max-age=0"}; const std::string NoTransform{"no-transform"}; + const std::string Private{"private"}; } CacheControlValues; struct { diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 0db06cc9e5e8a..26a3cbaa1abf4 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -2,42 +2,34 @@ #include "envoy/registry/registry.h" +#include "common/http/headers.h" + #include "absl/memory/memory.h" #include "absl/strings/string_view.h" -using absl::string_view; -using absl::WrapUnique; -using Envoy::Http::FilterDataStatus; -using Envoy::Http::FilterHeadersStatus; -using Envoy::Http::HeaderEntry; -using Envoy::Http::HeaderMap; -using Envoy::Http::HeaderMapPtr; -using Envoy::Registry::FactoryRegistry; -using Envoy::Stats::Scope; -using std::function; -using std::make_shared; -using std::string; -using std::vector; - namespace Envoy { namespace Extensions { namespace HttpFilters { namespace Cache { namespace { -bool isCacheableRequest(HeaderMap& headers) { - const HeaderEntry* method = headers.Method(); +bool isCacheableRequest(Http::HeaderMap& headers) { + const Http::HeaderEntry* method = headers.Method(); // TODO(toddmgreer) Also serve HEAD requests from cache. // TODO(toddmgreer) Check all the other cache-related headers. - return ((method != nullptr) && method->value().getStringView() == "GET"); + return ((method != nullptr) && + method->value().getStringView() == Http::Headers::get().MethodValues.Head); } -bool isCacheableResponse(HeaderMap& headers) { - const HeaderEntry* cache_control = headers.CacheControl(); +bool isCacheableResponse(Http::HeaderMap& headers) { + const Http::HeaderEntry* cache_control = headers.CacheControl(); // TODO(toddmgreer) fully check for cacheability. See for example // https://github.com/apache/incubator-pagespeed-mod/blob/master/pagespeed/kernel/http/caching_headers.h. - return (cache_control != nullptr) && - (cache_control->value().getStringView().find("private") == string_view::npos); + if (cache_control) { + return !StringUtil::caseFindToken(cache_control->value().getStringView(), ",", + Http::Headers::get().CacheControlValues.Private); + } + return false; } HttpCache& getCache(const envoy::config::filter::http::cache::v2alpha::Cache& config) { @@ -52,7 +44,7 @@ HttpCache& getCache(const envoy::config::filter::http::cache::v2alpha::Cache& co } // namespace CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v2alpha::Cache& config, - const string&, Stats::Scope&, TimeSource& time_source) + const std::string&, Stats::Scope&, TimeSource& time_source) : time_source_(time_source), cache_(getCache(config)) {} void CacheFilter::onDestroy() { @@ -60,9 +52,9 @@ void CacheFilter::onDestroy() { insert_ = nullptr; } -FilterHeadersStatus CacheFilter::decodeHeaders(HeaderMap& headers, bool) { +Http::FilterHeadersStatus CacheFilter::decodeHeaders(Http::HeaderMap& headers, bool) { if (!isCacheableRequest(headers)) { - return FilterHeadersStatus::Continue; + return Http::FilterHeadersStatus::Continue; } ASSERT(decoder_callbacks_); lookup_ = cache_.makeLookupContext(LookupRequest(headers, time_source_.systemTime())); @@ -70,27 +62,28 @@ FilterHeadersStatus CacheFilter::decodeHeaders(HeaderMap& headers, bool) { CacheFilterSharedPtr self = shared_from_this(); lookup_->getHeaders([self](LookupResult&& result) { onHeadersAsync(self, std::move(result)); }); - return FilterHeadersStatus::StopIteration; + return Http::FilterHeadersStatus::StopIteration; } -FilterHeadersStatus CacheFilter::encodeHeaders(HeaderMap& headers, bool end_stream) { +Http::FilterHeadersStatus CacheFilter::encodeHeaders(Http::HeaderMap& headers, bool end_stream) { if (lookup_ && isCacheableResponse(headers)) { insert_ = cache_.makeInsertContext(std::move(lookup_)); insert_->insertHeaders(headers, end_stream); } - return FilterHeadersStatus::Continue; + return Http::FilterHeadersStatus::Continue; } -FilterDataStatus CacheFilter::encodeData(Buffer::Instance& data, bool end_stream) { +Http::FilterDataStatus CacheFilter::encodeData(Buffer::Instance& data, bool end_stream) { if (insert_) { // TODO(toddmgreer) Wait for the cache if necessary. insert_->insertBody( data, [](bool) {}, end_stream); } - return FilterDataStatus::Continue; + return Http::FilterDataStatus::Continue; } -void CacheFilter::onOkHeaders(HeaderMapPtr&& headers, vector&& response_ranges, +void CacheFilter::onOkHeaders(Http::HeaderMapPtr&& headers, + std::vector&& response_ranges, uint64_t content_length, bool has_trailers) { if (!lookup_) { return; @@ -110,7 +103,7 @@ void CacheFilter::onOkHeaders(HeaderMapPtr&& headers, vector& } getBody(); } else { - lookup_->getTrailers([self = shared_from_this()](HeaderMapPtr&& trailers) { + lookup_->getTrailers([self = shared_from_this()](Http::HeaderMapPtr&& trailers) { onTrailersAsync(self, std::move(trailers)); }); } @@ -138,7 +131,7 @@ void CacheFilter::onHeadersAsync(const CacheFilterSharedPtr& self, LookupResult& response_ranges = std::move(result.response_ranges), content_length = result.content_length, has_trailers = result.has_trailers]() mutable { - self->onOkHeaders(WrapUnique(headers), std::move(response_ranges), content_length, + self->onOkHeaders(absl::WrapUnique(headers), std::move(response_ranges), content_length, has_trailers); }); } @@ -152,7 +145,7 @@ void CacheFilter::getBody() { } void CacheFilter::onBodyAsync(const CacheFilterSharedPtr& self, Buffer::InstancePtr&& body) { - self->post([self, body = body.release()] { self->onBody(WrapUnique(body)); }); + self->post([self, body = body.release()] { self->onBody(absl::WrapUnique(body)); }); } void CacheFilter::onBody(Buffer::InstancePtr&& body) { @@ -181,23 +174,24 @@ void CacheFilter::onBody(Buffer::InstancePtr&& body) { if (!remaining_body_.empty()) { getBody(); } else if (response_has_trailers_) { - lookup_->getTrailers([self = shared_from_this()](HeaderMapPtr&& trailers) { + lookup_->getTrailers([self = shared_from_this()](Http::HeaderMapPtr&& trailers) { onTrailersAsync(self, std::move(trailers)); }); } } -void CacheFilter::onTrailers(HeaderMapPtr&& trailers) { +void CacheFilter::onTrailers(Http::HeaderMapPtr&& trailers) { if (lookup_) { decoder_callbacks_->encodeTrailers(std::move(trailers)); } } -void CacheFilter::onTrailersAsync(const CacheFilterSharedPtr& self, HeaderMapPtr&& trailers) { - self->post([self, trailers = trailers.release()] { self->onTrailers(WrapUnique(trailers)); }); +void CacheFilter::onTrailersAsync(const CacheFilterSharedPtr& self, Http::HeaderMapPtr&& trailers) { + self->post( + [self, trailers = trailers.release()] { self->onTrailers(absl::WrapUnique(trailers)); }); } -void CacheFilter::post(function f) const { +void CacheFilter::post(std::function f) const { decoder_callbacks_->dispatcher().post(std::move(f)); } } // namespace Cache diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index 699135bb3e148..94450002c00ad 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -14,22 +14,16 @@ namespace Extensions { namespace HttpFilters { namespace Cache { -using Http::FilterChainFactoryCallbacks; -using Server::Configuration::FactoryContext; -using Server::Configuration::NamedHttpFilterConfigFactory; -using std::make_shared; -using std::string; - Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v2alpha::Cache& config, const string& stats_prefix, - FactoryContext& context) { - return [config, stats_prefix, &context](FilterChainFactoryCallbacks& callbacks) -> void { + const envoy::config::filter::http::cache::v2alpha::Cache& config, + const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { + return [config, stats_prefix, &context](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter( - make_shared(config, stats_prefix, context.scope(), context.timeSource())); + std::make_shared(config, stats_prefix, context.scope(), context.timeSource())); }; } -REGISTER_FACTORY(CacheFilterFactory, NamedHttpFilterConfigFactory); +REGISTER_FACTORY(CacheFilterFactory, Server::Configuration::NamedHttpFilterConfigFactory); } // namespace Cache } // namespace HttpFilters diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index 38b3bec3cf01a..24a9ecc6ca1d2 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -8,42 +8,33 @@ #include "absl/time/time.h" -using absl::Duration; -using absl::FromChrono; -using absl::ZeroDuration; -using Envoy::Extensions::HttpFilters::Cache::Internal::effectiveMaxAge; -using Envoy::Extensions::HttpFilters::Cache::Internal::httpTime; -using Envoy::Http::HeaderEntry; -using Envoy::Http::HeaderMap; -using Envoy::Http::HeaderMapPtr; -using Envoy::Http::HeaderString; -using Envoy::Http::LowerCaseString; -using std::string; - namespace Envoy { namespace Extensions { namespace HttpFilters { namespace Cache { -LookupRequest::LookupRequest(const HeaderMap& request_headers, SystemTime timestamp) +LookupRequest::LookupRequest(const Http::HeaderMap& request_headers, SystemTime timestamp) : timestamp_(timestamp), request_cache_control_(request_headers.CacheControl() == nullptr ? "" : request_headers.CacheControl()->value().getStringView()) { RELEASE_ASSERT(request_headers.Path(), - "Can't form cache lookup key for malformed HeaderMap with null Path."); + "Can't form cache lookup key for malformed Http::HeaderMap " + "with null Path."); RELEASE_ASSERT(request_headers.Scheme(), - "Can't form cache lookup key for malformed HeaderMap with null Scheme."); + "Can't form cache lookup key for malformed Http::HeaderMap " + "with null Scheme."); RELEASE_ASSERT(request_headers.Host(), - "Can't form cache lookup key for malformed HeaderMap with null Host."); + "Can't form cache lookup key for malformed Http::HeaderMap " + "with null Host."); // TODO(toddmgreer) Let config determine whether to include scheme, host, and // query params. // TODO(toddmgreer) get cluster name. // TODO(toddmgreer) Parse Range header into request_range_spec_. key_.set_cluster_name("cluster_name_goes_here"); - key_.set_host(string(request_headers.Host()->value().getStringView())); - key_.set_path(string(request_headers.Path()->value().getStringView())); - const HeaderString& scheme = request_headers.Scheme()->value(); + key_.set_host(std::string(request_headers.Host()->value().getStringView())); + key_.set_path(std::string(request_headers.Path()->value().getStringView())); + const Http::HeaderString& scheme = request_headers.Scheme()->value(); ASSERT(scheme == "http" || scheme == "https"); key_.set_clear_http(scheme == "http"); } @@ -55,22 +46,25 @@ size_t stableHashKey(const Key& key) { return MessageUtil::hash(key); } size_t localHashKey(const Key& key) { return stableHashKey(key); } // Returns true if response_headers is fresh. -bool LookupRequest::fresh(const HeaderMap& response_headers) const { - const HeaderEntry* cache_control_header = response_headers.get(LowerCaseString("cache-control")); +bool LookupRequest::fresh(const Http::HeaderMap& response_headers) const { + const Http::HeaderEntry* cache_control_header = + response_headers.get(Http::LowerCaseString("cache-control")); if (cache_control_header) { - const Duration effective_max_age = - effectiveMaxAge(cache_control_header->value().getStringView()); - if (effective_max_age >= ZeroDuration()) { - return FromChrono(timestamp_) - httpTime(response_headers.Date()) < effective_max_age; + const absl::Duration effective_max_age = + Internal::effectiveMaxAge(cache_control_header->value().getStringView()); + if (effective_max_age >= absl::ZeroDuration()) { + return absl::FromChrono(timestamp_) - Internal::httpTime(response_headers.Date()) < + effective_max_age; } } // We didn't find a cache-control header with enough info to determine // freshness, so fall back to the expires header. - return FromChrono(timestamp_) <= httpTime(response_headers.get(LowerCaseString("expires"))); + return absl::FromChrono(timestamp_) <= + Internal::httpTime(response_headers.get(Http::LowerCaseString("expires"))); } -LookupResult LookupRequest::makeLookupResult(HeaderMapPtr&& response_headers, +LookupResult LookupRequest::makeLookupResult(Http::HeaderMapPtr&& response_headers, uint64_t content_length) const { // TODO(toddmgreer) Implement all HTTP caching semantics. ASSERT(response_headers); diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index 6345d79a3985c..cb600b3ec7cbb 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -8,21 +8,6 @@ #include "absl/strings/numbers.h" #include "absl/strings/strip.h" -using absl::ascii_isalnum; -using absl::ascii_isdigit; -using absl::Duration; -using absl::InfiniteDuration; -using absl::InfinitePast; -using absl::ParseTime; -using absl::Seconds; -using absl::SimpleAtoi; -using absl::string_view; -using absl::Time; -using absl::ZeroDuration; -using Envoy::Http::HeaderEntry; -using std::array; -using std::string; - namespace Envoy { namespace Extensions { namespace HttpFilters { @@ -49,11 +34,11 @@ bool tchar(char c) { case '~': return true; } - return ascii_isalnum(c); + return absl::ascii_isalnum(c); } -bool eatToken(string_view& s) { - const string_view::iterator token_end = c_find_if_not(s, &tchar); +bool eatToken(absl::string_view& s) { + const absl::string_view::iterator token_end = c_find_if_not(s, &tchar); if (token_end == s.begin()) { return false; } @@ -61,7 +46,7 @@ bool eatToken(string_view& s) { return true; } -void eatDirectiveArgument(string_view& s) { +void eatDirectiveArgument(absl::string_view& s) { if (s.empty()) { return; } @@ -77,19 +62,19 @@ void eatDirectiveArgument(string_view& s) { // If s is nonnull and begins with decimal digits, return Eat leading digits in // *s, if any -Duration eatLeadingDuration(string_view* s) { - const string_view::iterator digits_end = c_find_if_not(*s, &ascii_isdigit); +absl::Duration eatLeadingDuration(absl::string_view* s) { + const absl::string_view::iterator digits_end = c_find_if_not(*s, &absl::ascii_isdigit); const size_t digits_length = digits_end - s->begin(); if (digits_length == 0) { - return ZeroDuration(); + return absl::ZeroDuration(); } - const string_view digits(s->begin(), digits_length); + const absl::string_view digits(s->begin(), digits_length); s->remove_prefix(digits_length); uint64_t num; - return SimpleAtoi(digits, &num) ? Seconds(num) : InfiniteDuration(); + return absl::SimpleAtoi(digits, &num) ? absl::Seconds(num) : absl::InfiniteDuration(); } -Duration effectiveMaxAge(string_view cache_control) { +absl::Duration effectiveMaxAge(absl::string_view cache_control) { // The grammar for This Cache-Control header value should be: // Cache-Control = 1#cache-directive // cache-directive = token [ "=" ( token / quoted-string ) ] @@ -101,7 +86,7 @@ Duration effectiveMaxAge(string_view cache_control) { // obs-text = %x80-FF // quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) // VCHAR = %x21-7E ; visible (printing) characters - Duration max_age = -InfiniteDuration(); + absl::Duration max_age = -absl::InfiniteDuration(); bool found_s_maxage = false; while (!cache_control.empty()) { // Each time through the loop, we eat one cache-directive. Each branch @@ -115,7 +100,7 @@ Duration effectiveMaxAge(string_view cache_control) { } } else { // Found a no-cache directive, so validation is required. - return -InfiniteDuration(); + return -absl::InfiniteDuration(); } } else if (ConsumePrefix(&cache_control, "s-maxage=")) { max_age = eatLeadingDuration(&cache_control); @@ -123,13 +108,13 @@ Duration effectiveMaxAge(string_view cache_control) { cache_control = StripLeadingAsciiWhitespace(cache_control); if (!cache_control.empty() && cache_control[0] != ',') { // Unexpected text at end of directive - return -InfiniteDuration(); + return -absl::InfiniteDuration(); } } else if (!found_s_maxage && ConsumePrefix(&cache_control, "max-age=")) { max_age = eatLeadingDuration(&cache_control); if (!cache_control.empty() && cache_control[0] != ',') { // Unexpected text at end of directive - return -InfiniteDuration(); + return -absl::InfiniteDuration(); } } else if (eatToken(cache_control)) { // Unknown directive--ignore. @@ -138,7 +123,7 @@ Duration effectiveMaxAge(string_view cache_control) { } } else { // This directive starts with illegal characters. Require validation. - return -InfiniteDuration(); + return -absl::InfiniteDuration(); } // Whichever branch we took should have consumed the entire cache-directive, // so we just need to eat the delimiter and optional whitespace. @@ -148,25 +133,25 @@ Duration effectiveMaxAge(string_view cache_control) { return max_age; } -Time httpTime(const HeaderEntry* header_entry) { +absl::Time httpTime(const Http::HeaderEntry* header_entry) { if (!header_entry) { - return InfinitePast(); + return absl::InfinitePast(); } - Time time; - const string input(header_entry->value().getStringView()); + absl::Time time; + const std::string input(header_entry->value().getStringView()); // RFC 7231 7.1.1.1: Acceptable Date/Time Formats: // Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate // Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format - const array rfc7231_date_formats = { + const std::array rfc7231_date_formats = { "%a, %d %b %Y %H:%M:%S GMT", "%A, %d-%b-%y %H:%M:%S GMT", "%a %b %e %H:%M:%S %Y"}; - for (const string& format : rfc7231_date_formats) { - if (ParseTime(format, input, &time, nullptr)) { + for (const std::string& format : rfc7231_date_formats) { + if (absl::ParseTime(format, input, &time, nullptr)) { return time; } } - return InfinitePast(); + return absl::InfinitePast(); } } // namespace Internal } // namespace Cache diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index c742019b15cee..3b55e95c1fe72 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -5,15 +5,6 @@ #include "common/common/lock_guard.h" #include "common/http/header_map_impl.h" -using Envoy::Http::HeaderMap; -using Envoy::Http::HeaderMapImpl; -using Envoy::Http::HeaderMapImplPtr; -using Envoy::Http::HeaderMapPtr; -using Envoy::Registry::RegisterFactory; -using Envoy::Thread::LockGuard; -using std::make_unique; -using std::string; - namespace Envoy { namespace Extensions { namespace HttpFilters { @@ -35,8 +26,8 @@ class SimpleLookupContext : public LookupContext { void getBody(const AdjustedByteRange& range, LookupBodyCallback&& cb) override { RELEASE_ASSERT(range.lastBytePos() < body_.length(), "Attempt to read past end of body."); - cb(make_unique(&body_[range.firstBytePos()], - range.lastBytePos() - range.firstBytePos() + 1)); + cb(std::make_unique(&body_[range.firstBytePos()], + range.lastBytePos() - range.firstBytePos() + 1)); } void getTrailers(LookupTrailersCallback&& cb) override { @@ -50,7 +41,7 @@ class SimpleLookupContext : public LookupContext { private: SimpleHttpCache& cache_; const LookupRequest request_; - string body_; + std::string body_; }; class SimpleInsertContext : public InsertContext { @@ -58,9 +49,9 @@ class SimpleInsertContext : public InsertContext { SimpleInsertContext(LookupContext& lookup_context, SimpleHttpCache& cache) : key_(dynamic_cast(lookup_context).request().key()), cache_(cache) {} - void insertHeaders(const HeaderMap& response_headers, bool end_stream) override { + void insertHeaders(const Http::HeaderMap& response_headers, bool end_stream) override { ASSERT(!committed_); - response_headers_ = make_unique(response_headers); + response_headers_ = std::make_unique(response_headers); if (end_stream) { commit(); } @@ -79,7 +70,7 @@ class SimpleInsertContext : public InsertContext { } } - void insertTrailers(const HeaderMap&) override { + void insertTrailers(const Http::HeaderMap&) override { ASSERT(false); // TODO(toddmgreer) support trailers } @@ -90,7 +81,7 @@ class SimpleInsertContext : public InsertContext { } Key key_; - HeaderMapImplPtr response_headers_; + Http::HeaderMapImplPtr response_headers_; SimpleHttpCache& cache_; Buffer::OwnedImpl body_; bool committed_ = false; @@ -98,11 +89,11 @@ class SimpleInsertContext : public InsertContext { } // namespace LookupContextPtr SimpleHttpCache::makeLookupContext(LookupRequest&& request) { - return make_unique(*this, std::move(request)); + return std::make_unique(*this, std::move(request)); } void SimpleHttpCache::updateHeaders(LookupContextPtr&& lookup_context, - HeaderMapPtr&& response_headers) { + Http::HeaderMapPtr&& response_headers) { ASSERT(lookup_context); ASSERT(response_headers); // TODO(toddmgreer) Support updating headers. @@ -110,24 +101,25 @@ void SimpleHttpCache::updateHeaders(LookupContextPtr&& lookup_context, } SimpleHttpCache::Entry SimpleHttpCache::lookup(const LookupRequest& request) { - LockGuard lock(mutex_); + Thread::LockGuard lock(mutex_); auto iter = map_.find(request.key()); if (iter == map_.end()) { return Entry{}; } ASSERT(iter->second.response_headers); - return SimpleHttpCache::Entry{make_unique(*iter->second.response_headers), - iter->second.body}; + return SimpleHttpCache::Entry{ + std::make_unique(*iter->second.response_headers), iter->second.body}; } -void SimpleHttpCache::insert(const Key& key, HeaderMapPtr&& response_headers, string&& body) { - LockGuard lock(mutex_); +void SimpleHttpCache::insert(const Key& key, Http::HeaderMapPtr&& response_headers, + std::string&& body) { + Thread::LockGuard lock(mutex_); map_[key] = SimpleHttpCache::Entry{std::move(response_headers), std::move(body)}; } InsertContextPtr SimpleHttpCache::makeInsertContext(LookupContextPtr&& lookup_context) { ASSERT(lookup_context != nullptr); - return make_unique(*lookup_context, *this); + return std::make_unique(*lookup_context, *this); } CacheInfo SimpleHttpCache::cacheInfo() const { @@ -145,7 +137,7 @@ class SimpleHttpCacheFactory : public HttpCacheFactory { SimpleHttpCache cache_; }; -static RegisterFactory register_; +static Registry::RegisterFactory register_; } // namespace Cache } // namespace HttpFilters diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index 83595020d2295..19bfbc8990b7c 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -10,23 +10,18 @@ namespace Extensions { namespace HttpFilters { namespace Cache { -using Event::SimulatedTimeSystem; -using Http::HeaderMapPtr; -using Http::makeHeaderMap; -using Http::TestHeaderMapImpl; - class LookupRequestTest : public testing::Test { protected: - SimulatedTimeSystem time_source_; + Event::SimulatedTimeSystem time_source_; SystemTime current_time_ = time_source_.systemTime(); DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; - const TestHeaderMapImpl request_headers_{ + const Http::TestHeaderMapImpl request_headers_{ {":path", "/"}, {":scheme", "http"}, {":authority", "example.com"}}; const LookupRequest lookup_request_{request_headers_, current_time_}; }; TEST_F(LookupRequestTest, makeLookupResult) { - HeaderMapPtr response_headers = makeHeaderMap( + Http::HeaderMapPtr response_headers = Http::makeHeaderMap( {{"date", formatter_.fromTime(current_time_)}, {"cache-control", "public, max-age=3600"}}); const LookupResult lookup_response = lookup_request_.makeLookupResult(std::move(response_headers), 0); @@ -35,9 +30,10 @@ TEST_F(LookupRequestTest, makeLookupResult) { } TEST_F(LookupRequestTest, PrivateResponse) { - HeaderMapPtr response_headers = makeHeaderMap({{"age", "2"}, - {"cache-control", "private, max-age=3600"}, - {"date", formatter_.fromTime(current_time_)}}); + Http::HeaderMapPtr response_headers = + Http::makeHeaderMap({{"age", "2"}, + {"cache-control", "private, max-age=3600"}, + {"date", formatter_.fromTime(current_time_)}}); const LookupResult lookup_response = lookup_request_.makeLookupResult(std::move(response_headers), 0); diff --git a/test/extensions/filters/http/cache/http_cache_utils_test.cc b/test/extensions/filters/http/cache/http_cache_utils_test.cc index 4909f2c3506b4..fdf3467868e49 100644 --- a/test/extensions/filters/http/cache/http_cache_utils_test.cc +++ b/test/extensions/filters/http/cache/http_cache_utils_test.cc @@ -15,32 +15,25 @@ namespace Cache { namespace Internal { namespace { -using absl::InfinitePast; -using absl::UTCTimeZone; -using Http::TestHeaderMapImpl; -using std::string; -using testing::TestWithParam; -using testing::ValuesIn; - -class HttpTimeTest : public TestWithParam { +class HttpTimeTest : public testing::TestWithParam { protected: - TestHeaderMapImpl response_headers_{{"date", GetParam()}}; + Http::TestHeaderMapImpl response_headers_{{"date", GetParam()}}; }; -const std::vector ok_times = { +const std::vector ok_times = { "Sun, 06 Nov 1994 08:49:37 GMT", // IMF-fixdate "Sunday, 06-Nov-94 08:49:37 GMT", // obsolete RFC 850 format "Sun Nov 6 08:49:37 1994" // ANSI C's asctime() format }; -INSTANTIATE_TEST_SUITE_P(Ok, HttpTimeTest, ValuesIn(ok_times)); +INSTANTIATE_TEST_SUITE_P(Ok, HttpTimeTest, testing::ValuesIn(ok_times)); TEST_P(HttpTimeTest, Ok) { - EXPECT_EQ(FormatTime(httpTime(response_headers_.Date()), UTCTimeZone()), + EXPECT_EQ(FormatTime(httpTime(response_headers_.Date()), absl::UTCTimeZone()), "1994-11-06T08:49:37+00:00"); } -TEST(HttpTime, Null) { EXPECT_EQ(httpTime(nullptr), InfinitePast()); } +TEST(HttpTime, Null) { EXPECT_EQ(httpTime(nullptr), absl::InfinitePast()); } } // namespace } // namespace Internal } // namespace Cache diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index bd9aa65097f13..4abfb14719881 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -16,33 +16,19 @@ namespace HttpFilters { namespace Cache { namespace { -using absl::string_view; -using Event::SimulatedTimeSystem; -using Http::Headers; -using Http::TestHeaderMapImpl; -using Registry::FactoryRegistry; -using std::string; -using std::vector; -using testing::Combine; -using testing::get; -using testing::tuple; -using testing::Values; -using testing::ValuesIn; -using testing::WithParamInterface; - -const string kEpochDate = "Thu, 01 Jan 1970 00:00:00 GMT"; +const std::string kEpochDate = "Thu, 01 Jan 1970 00:00:00 GMT"; class SimpleHttpCacheTest : public testing::Test { protected: SimpleHttpCacheTest() { - request_headers_.insertMethod().value(string_view(("GET"))); - request_headers_.insertHost().value(string_view(("example.com"))); - request_headers_.insertScheme().value(string_view(("https"))); - request_headers_.insertCacheControl().value(string_view(("max-age=3600"))); + request_headers_.insertMethod().value(absl::string_view(("GET"))); + request_headers_.insertHost().value(absl::string_view(("example.com"))); + request_headers_.insertScheme().value(absl::string_view(("https"))); + request_headers_.insertCacheControl().value(absl::string_view(("max-age=3600"))); } // Performs a cache lookup. - LookupContextPtr lookup(string_view request_path) { + LookupContextPtr lookup(absl::string_view request_path) { LookupRequest request = makeLookupRequest(request_path); LookupContextPtr context = cache_.makeLookupContext(std::move(request)); context->getHeaders([this](LookupResult&& result) { lookup_result_ = std::move(result); }); @@ -50,21 +36,21 @@ class SimpleHttpCacheTest : public testing::Test { } // Inserts a value into the cache. - void insert(LookupContextPtr lookup, const TestHeaderMapImpl& response_headers, - const string_view response_body) { + void insert(LookupContextPtr lookup, const Http::TestHeaderMapImpl& response_headers, + const absl::string_view response_body) { InsertContextPtr inserter = cache_.makeInsertContext(move(lookup)); inserter->insertHeaders(response_headers, false); inserter->insertBody(Buffer::OwnedImpl(response_body), nullptr, true); } - void insert(string_view request_path, const TestHeaderMapImpl& response_headers, - const string_view response_body) { + void insert(absl::string_view request_path, const Http::TestHeaderMapImpl& response_headers, + const absl::string_view response_body) { insert(lookup(request_path), response_headers, response_body); } - string getBody(LookupContext& context, uint64_t start, uint64_t end) { + std::string getBody(LookupContext& context, uint64_t start, uint64_t end) { AdjustedByteRange range(start, end); - string body; + std::string body; context.getBody(range, [&body](Buffer::InstancePtr&& data) { EXPECT_NE(data, nullptr); if (data) { @@ -74,12 +60,13 @@ class SimpleHttpCacheTest : public testing::Test { return body; } - LookupRequest makeLookupRequest(string_view request_path) { + LookupRequest makeLookupRequest(absl::string_view request_path) { request_headers_.insertPath().value(request_path); return LookupRequest(request_headers_, current_time_); }; - AssertionResult ExpectLookupSuccessWithBody(LookupContext* lookup_context, string_view body) { + AssertionResult ExpectLookupSuccessWithBody(LookupContext* lookup_context, + absl::string_view body) { if (lookup_result_.cache_entry_status != CacheEntryStatus::Ok) { return AssertionFailure() << "Expected: lookup_result_.cache_entry_status == " "CacheEntryStatus::Ok\n Actual: " @@ -91,7 +78,7 @@ class SimpleHttpCacheTest : public testing::Test { if (!lookup_context) { return AssertionFailure() << "Expected nonnull lookup_context"; } - const string actual_body = getBody(*lookup_context, 0, body.size() - 1u); + const std::string actual_body = getBody(*lookup_context, 0, body.size() - 1u); if (body != actual_body) { return AssertionFailure() << "Expected body == " << body << "\n Actual: " << actual_body; } @@ -100,45 +87,45 @@ class SimpleHttpCacheTest : public testing::Test { SimpleHttpCache cache_; LookupResult lookup_result_; - TestHeaderMapImpl request_headers_; - SimulatedTimeSystem time_source_; + Http::TestHeaderMapImpl request_headers_; + Event::SimulatedTimeSystem time_source_; SystemTime current_time_ = time_source_.systemTime(); DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; }; // Simple flow of putting in an item, getting it, deleting it. TEST_F(SimpleHttpCacheTest, PutGet) { - const string kRequestPath1("Name"); + const std::string kRequestPath1("Name"); LookupContextPtr name_lookup_context = lookup(kRequestPath1); EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); - TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, - {"cache-control", "public, max-age=3600"}}; + Http::TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, + {"cache-control", "public, max-age=3600"}}; - const string kBody1("Value"); + const std::string kBody1("Value"); insert(move(name_lookup_context), response_headers, kBody1); name_lookup_context = lookup(kRequestPath1); EXPECT_TRUE(ExpectLookupSuccessWithBody(name_lookup_context.get(), kBody1)); - const string& kRequestPath2("Another Name"); + const std::string& kRequestPath2("Another Name"); LookupContextPtr another_name_lookup_context = lookup(kRequestPath2); EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); - const string kNewBody1("NewValue"); + const std::string kNewBody1("NewValue"); insert(move(name_lookup_context), response_headers, kNewBody1); EXPECT_TRUE(ExpectLookupSuccessWithBody(lookup(kRequestPath1).get(), kNewBody1)); } TEST_F(SimpleHttpCacheTest, PrivateResponse) { - TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, - {"age", "2"}, - {"cache-control", "private, max-age=3600"}}; - const string request_path("Name"); + Http::TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, + {"age", "2"}, + {"cache-control", "private, max-age=3600"}}; + const std::string request_path("Name"); LookupContextPtr name_lookup_context = lookup(request_path); EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); - const string kBody("Value"); + const std::string kBody("Value"); // We must make sure at cache insertion time, private responses must not be // inserted. However, if the insertion did happen, it would be served at the // time of lookup. @@ -153,14 +140,17 @@ TEST_F(SimpleHttpCacheTest, Miss) { class SimpleHttpCacheTest1Response : public SimpleHttpCacheTest, - public WithParamInterface> { + public testing::WithParamInterface< + testing::tuple> { protected: - CacheEntryStatus expectedStatus() const { return get(GetParam()); } - const TestHeaderMapImpl& responseHeaders() const { return get(GetParam()); } + CacheEntryStatus expectedStatus() const { return testing::get(GetParam()); } + const Http::TestHeaderMapImpl& responseHeaders() const { + return testing::get(GetParam()); + } }; TEST_P(SimpleHttpCacheTest1Response, Expired) { - TestHeaderMapImpl response_headers = responseHeaders(); + Http::TestHeaderMapImpl response_headers = responseHeaders(); // TODO(toddmgreer) Test with various date headers. response_headers.addCopy("date", formatter_.fromTime(current_time_)); insert("/", response_headers, ""); @@ -168,7 +158,7 @@ TEST_P(SimpleHttpCacheTest1Response, Expired) { EXPECT_EQ(expectedStatus(), lookup_result_.cache_entry_status); } -const vector expired_headers = { +const std::vector expired_headers = { {{"date", "Thu, 01 Jan 2100 00:00:00 GMT"}, {"cache-control", "public, max-age=3600"}}, {{"cache-control", "public, s-max-age=-1"}}, {{"cache-control", "public, max-age=-1"}}, @@ -178,49 +168,51 @@ const vector expired_headers = { {{"age", "2"}, {"cache-control", "public"}}, {{"age", "6000"}}}; -const vector ok_headers = {{{"cache-control", "public, max-age=3600"}}}; +const std::vector ok_headers = { + {{"cache-control", "public, max-age=3600"}}}; INSTANTIATE_TEST_SUITE_P(Expired, SimpleHttpCacheTest1Response, - Combine(Values(CacheEntryStatus::RequiresValidation), - ValuesIn(expired_headers))); + testing::Combine(testing::Values(CacheEntryStatus::RequiresValidation), + testing::ValuesIn(expired_headers))); INSTANTIATE_TEST_SUITE_P(Ok, SimpleHttpCacheTest1Response, - Combine(Values(CacheEntryStatus::Ok), ValuesIn(ok_headers))); + testing::Combine(testing::Values(CacheEntryStatus::Ok), + testing::ValuesIn(ok_headers))); TEST_F(SimpleHttpCacheTest, RequestSmallMinFresh) { - request_headers_.setReferenceKey(Headers::get().CacheControl, "min-fresh=1000"); - const string request_path("Name"); + request_headers_.setReferenceKey(Http::Headers::get().CacheControl, "min-fresh=1000"); + const std::string request_path("Name"); LookupContextPtr name_lookup_context = lookup(request_path); EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); - TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, - {"age", "6000"}, - {"cache-control", "public, max-age=9000"}}; - const string kBody("Value"); + Http::TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, + {"age", "6000"}, + {"cache-control", "public, max-age=9000"}}; + const std::string kBody("Value"); insert(move(name_lookup_context), response_headers, kBody); EXPECT_TRUE(ExpectLookupSuccessWithBody(lookup(request_path).get(), kBody)); } TEST_F(SimpleHttpCacheTest, ResponseStaleWithRequestLargeMaxStale) { - request_headers_.setReferenceKey(Headers::get().CacheControl, "max-stale=9000"); + request_headers_.setReferenceKey(Http::Headers::get().CacheControl, "max-stale=9000"); - const string request_path("Name"); + const std::string request_path("Name"); LookupContextPtr name_lookup_context = lookup(request_path); EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); - TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, - {"age", "7200"}, - {"cache-control", "public, max-age=3600"}}; + Http::TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, + {"age", "7200"}, + {"cache-control", "public, max-age=3600"}}; - const string kBody("Value"); + const std::string kBody("Value"); insert(move(name_lookup_context), response_headers, kBody); EXPECT_TRUE(ExpectLookupSuccessWithBody(lookup(request_path).get(), kBody)); } TEST_F(SimpleHttpCacheTest, StreamingPut) { - TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, - {"age", "2"}, - {"cache-control", "public, max-age=3600"}}; + Http::TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, + {"age", "2"}, + {"cache-control", "public, max-age=3600"}}; InsertContextPtr inserter = cache_.makeInsertContext(lookup("request_path")); inserter->insertHeaders(response_headers, false); inserter->insertBody( @@ -234,7 +226,8 @@ TEST_F(SimpleHttpCacheTest, StreamingPut) { } TEST(Registration, getFactory) { - HttpCacheFactory* factory = FactoryRegistry::getFactory("SimpleHttpCache"); + HttpCacheFactory* factory = + Registry::FactoryRegistry::getFactory("SimpleHttpCache"); ASSERT_NE(factory, nullptr); EXPECT_EQ(factory->getCache().cacheInfo().name_, "SimpleHttpCache"); } From f432612ccb52b6c730086e13204e06cf803b23c6 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 21 Jun 2019 12:31:30 -0700 Subject: [PATCH 011/109] Style cleanups requested in first review, and a few more unit tests. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/BUILD | 1 + .../filters/http/cache/cache_filter.cc | 4 +- .../filters/http/cache/cache_filter.h | 4 +- .../filters/http/cache/http_cache.cc | 41 ++++++++----------- .../filters/http/cache/http_cache.h | 24 +++++------ .../filters/http/cache/http_cache_utils.cc | 26 ++++++------ .../filters/http/cache/http_cache_utils.h | 14 ++++--- .../filters/http/cache/http_cache_test.cc | 9 ++++ .../http/cache/http_cache_utils_test.cc | 17 ++++++-- .../http/cache/simple_http_cache_test.cc | 20 ++++----- 10 files changed, 85 insertions(+), 75 deletions(-) diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index fedbfa5319151..134da35e549c0 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -67,6 +67,7 @@ envoy_cc_library( srcs = ["http_cache_utils.cc"], hdrs = ["http_cache_utils.h"], deps = [ + "//include/envoy/common:time_interface", "//include/envoy/http:header_map_interface", "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/strings", diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 26a3cbaa1abf4..db0f9270e21c3 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -17,8 +17,8 @@ bool isCacheableRequest(Http::HeaderMap& headers) { const Http::HeaderEntry* method = headers.Method(); // TODO(toddmgreer) Also serve HEAD requests from cache. // TODO(toddmgreer) Check all the other cache-related headers. - return ((method != nullptr) && - method->value().getStringView() == Http::Headers::get().MethodValues.Head); + return method && headers.Path() && headers.Host() && + (method->value().getStringView() == Http::Headers::get().MethodValues.Get); } bool isCacheableResponse(Http::HeaderMap& headers) { diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index 4171a0b95b936..eaba194eaa03c 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -41,10 +41,10 @@ class CacheFilter : public Http::PassThroughFilter, void onOkHeaders(Http::HeaderMapPtr&& headers, std::vector&& response_ranges, uint64_t content_length, bool has_trailers); void onUnusableHeaders(); - void onBody(Envoy::Buffer::InstancePtr&& body); + void onBody(Buffer::InstancePtr&& body); void onTrailers(Http::HeaderMapPtr&& trailers); static void onHeadersAsync(const CacheFilterSharedPtr& self, LookupResult&& result); - static void onBodyAsync(const CacheFilterSharedPtr& self, Envoy::Buffer::InstancePtr&& body); + static void onBodyAsync(const CacheFilterSharedPtr& self, Buffer::InstancePtr&& body); static void onTrailersAsync(const CacheFilterSharedPtr& self, Http::HeaderMapPtr&& trailers); void post(std::function f) const; diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index 24a9ecc6ca1d2..1f73359218a26 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -2,6 +2,7 @@ #include +#include "common/http/headers.h" #include "common/protobuf/utility.h" #include "extensions/filters/http/cache/http_cache_utils.h" @@ -18,15 +19,12 @@ LookupRequest::LookupRequest(const Http::HeaderMap& request_headers, SystemTime request_cache_control_(request_headers.CacheControl() == nullptr ? "" : request_headers.CacheControl()->value().getStringView()) { - RELEASE_ASSERT(request_headers.Path(), - "Can't form cache lookup key for malformed Http::HeaderMap " - "with null Path."); - RELEASE_ASSERT(request_headers.Scheme(), - "Can't form cache lookup key for malformed Http::HeaderMap " - "with null Scheme."); - RELEASE_ASSERT(request_headers.Host(), - "Can't form cache lookup key for malformed Http::HeaderMap " - "with null Host."); + ASSERT(request_headers.Path(), "Can't form cache lookup key for malformed Http::HeaderMap " + "with null Path."); + ASSERT(request_headers.Scheme(), "Can't form cache lookup key for malformed Http::HeaderMap " + "with null Scheme."); + ASSERT(request_headers.Host(), "Can't form cache lookup key for malformed Http::HeaderMap " + "with null Host."); // TODO(toddmgreer) Let config determine whether to include scheme, host, and // query params. // TODO(toddmgreer) get cluster name. @@ -35,8 +33,9 @@ LookupRequest::LookupRequest(const Http::HeaderMap& request_headers, SystemTime key_.set_host(std::string(request_headers.Host()->value().getStringView())); key_.set_path(std::string(request_headers.Path()->value().getStringView())); const Http::HeaderString& scheme = request_headers.Scheme()->value(); - ASSERT(scheme == "http" || scheme == "https"); - key_.set_clear_http(scheme == "http"); + ASSERT(scheme == Http::Headers::get().SchemeValues.Http || + scheme == Http::Headers::get().SchemeValues.Https); + key_.set_clear_http(scheme == Http::Headers::get().SchemeValues.Http); } // Unless this API is still alpha, calls to stableHashKey() must always return @@ -46,22 +45,16 @@ size_t stableHashKey(const Key& key) { return MessageUtil::hash(key); } size_t localHashKey(const Key& key) { return stableHashKey(key); } // Returns true if response_headers is fresh. -bool LookupRequest::fresh(const Http::HeaderMap& response_headers) const { - const Http::HeaderEntry* cache_control_header = - response_headers.get(Http::LowerCaseString("cache-control")); +bool LookupRequest::isFresh(const Http::HeaderMap& response_headers) const { + const Http::HeaderEntry* cache_control_header = response_headers.CacheControl(); if (cache_control_header) { - const absl::Duration effective_max_age = + const SystemTime::duration effective_max_age = Internal::effectiveMaxAge(cache_control_header->value().getStringView()); - if (effective_max_age >= absl::ZeroDuration()) { - return absl::FromChrono(timestamp_) - Internal::httpTime(response_headers.Date()) < - effective_max_age; - } + return timestamp_ - Internal::httpTime(response_headers.Date()) < effective_max_age; } - // We didn't find a cache-control header with enough info to determine // freshness, so fall back to the expires header. - return absl::FromChrono(timestamp_) <= - Internal::httpTime(response_headers.get(Http::LowerCaseString("expires"))); + return timestamp_ <= Internal::httpTime(response_headers.get(Http::LowerCaseString("expires"))); } LookupResult LookupRequest::makeLookupResult(Http::HeaderMapPtr&& response_headers, @@ -69,7 +62,7 @@ LookupResult LookupRequest::makeLookupResult(Http::HeaderMapPtr&& response_heade // TODO(toddmgreer) Implement all HTTP caching semantics. ASSERT(response_headers); LookupResult result; - if (!fresh(*response_headers)) { + if (!isFresh(*response_headers)) { result.cache_entry_status = CacheEntryStatus::RequiresValidation; return result; } @@ -100,7 +93,7 @@ bool LookupRequest::adjustByteRangeSet(std::vector& response_ return false; } - for (const auto& spec : request_range_spec_) { + for (const RawByteRange& spec : request_range_spec_) { if (spec.isSuffix()) { // spec is a suffix-byte-range-spec if (spec.suffixLength() >= content_length) { diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index d78ba8d788461..a48d1469c985d 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -167,7 +167,7 @@ class LookupRequest { uint64_t content_length) const; private: - bool fresh(const Http::HeaderMap& response_headers) const; + bool isFresh(const Http::HeaderMap& response_headers) const; bool adjustByteRangeSet(std::vector& response_ranges, uint64_t content_length) const; @@ -193,7 +193,7 @@ using InsertCallback = std::function; class InsertContext { public: // Accepts response_headers for caching. Only called once. - virtual void insertHeaders(const Http::HeaderMap& response_headers, bool end_stream) = 0; + virtual void insertHeaders(const Http::HeaderMap& response_headers, bool end_stream) PURE; // The insertion is streamed into the cache in chunks whose size is determined // by the client, but with a pace determined by the cache. To avoid streaming @@ -204,10 +204,10 @@ class InsertContext { // InsertContextPtr. A cache can abort the insertion by passing 'false' into // ready_for_next_chunk. virtual void insertBody(const Buffer::Instance& chunk, InsertCallback ready_for_next_chunk, - bool end_stream) = 0; + bool end_stream) PURE; // Inserts trailers into the cache. - virtual void insertTrailers(const Http::HeaderMap& trailers) = 0; + virtual void insertTrailers(const Http::HeaderMap& trailers) PURE; virtual ~InsertContext() = default; }; @@ -222,7 +222,7 @@ class LookupContext { // Get the headers from the cache. It is a programming error to call this // twice. - virtual void getHeaders(LookupHeadersCallback&& cb) = 0; + virtual void getHeaders(LookupHeadersCallback&& cb) PURE; // Reads the next chunk from the cache, calling cb when the chunk is ready. // @@ -240,11 +240,11 @@ class LookupContext { // getBody requests bytes 0-23 .......... callback with bytes 0-9 // getBody requests bytes 10-23 .......... callback with bytes 10-19 // getBody requests bytes 20-23 .......... callback with bytes 20-23 - virtual void getBody(const AdjustedByteRange& range, LookupBodyCallback&& cb) = 0; + virtual void getBody(const AdjustedByteRange& range, LookupBodyCallback&& cb) PURE; // Get the trailers from the cache. Only called if LookupResult::has_trailers // == true. - virtual void getTrailers(LookupTrailersCallback&& cb) = 0; + virtual void getTrailers(LookupTrailersCallback&& cb) PURE; }; using LookupContextPtr = std::unique_ptr; @@ -254,12 +254,12 @@ class HttpCache { public: // Returns a LookupContextPtr to manage the state of a cache lookup. On a cache // miss, the returned LookupContext will be given to the insert call (if any). - virtual LookupContextPtr makeLookupContext(LookupRequest&& request) = 0; + virtual LookupContextPtr makeLookupContext(LookupRequest&& request) PURE; // Returns an InsertContextPtr to manage the state of a cache insertion. // Responses with a chunked transfer-encoding must be dechunked before // insertion. - virtual InsertContextPtr makeInsertContext(LookupContextPtr&& lookup_context) = 0; + virtual InsertContextPtr makeInsertContext(LookupContextPtr&& lookup_context) PURE; // Precondition: lookup_context represents a prior cache lookup that required // validation. @@ -270,10 +270,10 @@ class HttpCache { // This is called when an expired cache entry is successfully validated, to // update the cache entry. virtual void updateHeaders(LookupContextPtr&& lookup_context, - Http::HeaderMapPtr&& response_headers) = 0; + Http::HeaderMapPtr&& response_headers) PURE; // Returns statically known information about a cache. - virtual CacheInfo cacheInfo() const = 0; + virtual CacheInfo cacheInfo() const PURE; virtual ~HttpCache() = default; }; @@ -286,7 +286,7 @@ class HttpCacheFactory { // Returns an HttpCache that will remain valid indefinitely (at least as long // as the calling CacheFilter). - virtual HttpCache& getCache() = 0; + virtual HttpCache& getCache() PURE; virtual ~HttpCacheFactory() = default; private: diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index cb600b3ec7cbb..9526abab93b07 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -62,19 +62,19 @@ void eatDirectiveArgument(absl::string_view& s) { // If s is nonnull and begins with decimal digits, return Eat leading digits in // *s, if any -absl::Duration eatLeadingDuration(absl::string_view* s) { +SystemTime::duration eatLeadingDuration(absl::string_view* s) { const absl::string_view::iterator digits_end = c_find_if_not(*s, &absl::ascii_isdigit); const size_t digits_length = digits_end - s->begin(); if (digits_length == 0) { - return absl::ZeroDuration(); + return SystemTime::duration::zero(); } const absl::string_view digits(s->begin(), digits_length); s->remove_prefix(digits_length); uint64_t num; - return absl::SimpleAtoi(digits, &num) ? absl::Seconds(num) : absl::InfiniteDuration(); + return absl::SimpleAtoi(digits, &num) ? std::chrono::seconds(num) : SystemTime::duration::max(); } -absl::Duration effectiveMaxAge(absl::string_view cache_control) { +SystemTime::duration effectiveMaxAge(absl::string_view cache_control) { // The grammar for This Cache-Control header value should be: // Cache-Control = 1#cache-directive // cache-directive = token [ "=" ( token / quoted-string ) ] @@ -86,7 +86,7 @@ absl::Duration effectiveMaxAge(absl::string_view cache_control) { // obs-text = %x80-FF // quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) // VCHAR = %x21-7E ; visible (printing) characters - absl::Duration max_age = -absl::InfiniteDuration(); + SystemTime::duration max_age = SystemTime::duration::zero(); bool found_s_maxage = false; while (!cache_control.empty()) { // Each time through the loop, we eat one cache-directive. Each branch @@ -100,7 +100,7 @@ absl::Duration effectiveMaxAge(absl::string_view cache_control) { } } else { // Found a no-cache directive, so validation is required. - return -absl::InfiniteDuration(); + return SystemTime::duration::zero(); } } else if (ConsumePrefix(&cache_control, "s-maxage=")) { max_age = eatLeadingDuration(&cache_control); @@ -108,13 +108,13 @@ absl::Duration effectiveMaxAge(absl::string_view cache_control) { cache_control = StripLeadingAsciiWhitespace(cache_control); if (!cache_control.empty() && cache_control[0] != ',') { // Unexpected text at end of directive - return -absl::InfiniteDuration(); + return SystemTime::duration::zero(); } } else if (!found_s_maxage && ConsumePrefix(&cache_control, "max-age=")) { max_age = eatLeadingDuration(&cache_control); if (!cache_control.empty() && cache_control[0] != ',') { // Unexpected text at end of directive - return -absl::InfiniteDuration(); + return SystemTime::duration::zero(); } } else if (eatToken(cache_control)) { // Unknown directive--ignore. @@ -123,7 +123,7 @@ absl::Duration effectiveMaxAge(absl::string_view cache_control) { } } else { // This directive starts with illegal characters. Require validation. - return -absl::InfiniteDuration(); + return SystemTime::duration::zero(); } // Whichever branch we took should have consumed the entire cache-directive, // so we just need to eat the delimiter and optional whitespace. @@ -133,9 +133,9 @@ absl::Duration effectiveMaxAge(absl::string_view cache_control) { return max_age; } -absl::Time httpTime(const Http::HeaderEntry* header_entry) { +SystemTime httpTime(const Http::HeaderEntry* header_entry) { if (!header_entry) { - return absl::InfinitePast(); + return SystemTime::min(); } absl::Time time; const std::string input(header_entry->value().getStringView()); @@ -148,10 +148,10 @@ absl::Time httpTime(const Http::HeaderEntry* header_entry) { "%a, %d %b %Y %H:%M:%S GMT", "%A, %d-%b-%y %H:%M:%S GMT", "%a %b %e %H:%M:%S %Y"}; for (const std::string& format : rfc7231_date_formats) { if (absl::ParseTime(format, input, &time, nullptr)) { - return time; + return ToChronoTime(time); } } - return absl::InfinitePast(); + return SystemTime::min(); } } // namespace Internal } // namespace Cache diff --git a/source/extensions/filters/http/cache/http_cache_utils.h b/source/extensions/filters/http/cache/http_cache_utils.h index 9e0f5945d3283..d1f3470bb27da 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.h +++ b/source/extensions/filters/http/cache/http_cache_utils.h @@ -1,5 +1,6 @@ #pragma once +#include "envoy/common/time.h" #include "envoy/http/header_map.h" #include "absl/strings/string_view.h" @@ -11,13 +12,14 @@ namespace HttpFilters { namespace Cache { namespace Internal { // Parses and returns max-age or s-maxage (with s-maxage taking precedence), -// parsed into a Duration. Returns -InfiniteDuration if neither is present, or -// there is a no-cache directive, or if max-age or s-maxage is malformed. -absl::Duration effectiveMaxAge(absl::string_view cache_control); +// parsed into a SystemTime::Duration. Returns SystemTime::Duration::zero if +// neither is present, or there is a no-cache directive, or if max-age or +// s-maxage is malformed. +SystemTime::duration effectiveMaxAge(absl::string_view cache_control); -// Parses header_entry as an HTTP time. Returns InifinitePast() if header_entry -// is null or malformed. -absl::Time httpTime(const Http::HeaderEntry* header_entry); +// Parses header_entry as an HTTP time. Returns SystemTime::max() if +// header_entry is null or malformed. +SystemTime httpTime(const Http::HeaderEntry* header_entry); } // namespace Internal } // namespace Cache } // namespace HttpFilters diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index 19bfbc8990b7c..2bfdb6824f6a2 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -44,6 +44,15 @@ TEST_F(LookupRequestTest, PrivateResponse) { << formatter_.fromTime(current_time_); } +TEST_F(LookupRequestTest, Expired) { + Http::HeaderMapPtr response_headers = Http::makeHeaderMap( + {{"cache-control", "public, max-age=3600"}, {"date", "Thu, 01 Jan 2019 00:00:00 GMT"}}); + const LookupResult lookup_response = + lookup_request_.makeLookupResult(std::move(response_headers), 0); + + EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status); +} + } // namespace Cache } // namespace HttpFilters } // namespace Extensions diff --git a/test/extensions/filters/http/cache/http_cache_utils_test.cc b/test/extensions/filters/http/cache/http_cache_utils_test.cc index fdf3467868e49..93e500144bfb3 100644 --- a/test/extensions/filters/http/cache/http_cache_utils_test.cc +++ b/test/extensions/filters/http/cache/http_cache_utils_test.cc @@ -29,11 +29,22 @@ const std::vector ok_times = { INSTANTIATE_TEST_SUITE_P(Ok, HttpTimeTest, testing::ValuesIn(ok_times)); TEST_P(HttpTimeTest, Ok) { - EXPECT_EQ(FormatTime(httpTime(response_headers_.Date()), absl::UTCTimeZone()), - "1994-11-06T08:49:37+00:00"); + const std::time_t time = SystemTime::clock::to_time_t(httpTime(response_headers_.Date())); + EXPECT_STREQ(ctime(&time), "Sun Nov 6 08:49:37 1994\n"); +} + +TEST(HttpTime, Null) { EXPECT_EQ(httpTime(nullptr), SystemTime::min()); } + +TEST(HttpTime, Ok) { EXPECT_EQ(httpTime(nullptr), SystemTime::min()); } + +TEST(EffectiveMaxAge, Ok) { + EXPECT_EQ(std::chrono::seconds(3600), effectiveMaxAge("public, max-age=3600")); +} + +TEST(EffectiveMaxAge, NegativeMaxAge) { + EXPECT_EQ(SystemTime::duration::zero(), effectiveMaxAge("public, max-age=-1")); } -TEST(HttpTime, Null) { EXPECT_EQ(httpTime(nullptr), absl::InfinitePast()); } } // namespace } // namespace Internal } // namespace Cache diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index 4abfb14719881..3014c52ab2261 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -100,7 +100,7 @@ TEST_F(SimpleHttpCacheTest, PutGet) { EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); Http::TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, - {"cache-control", "public, max-age=3600"}}; + {"cache-control", "public,max-age=3600"}}; const std::string kBody1("Value"); insert(move(name_lookup_context), response_headers, kBody1); @@ -119,7 +119,7 @@ TEST_F(SimpleHttpCacheTest, PutGet) { TEST_F(SimpleHttpCacheTest, PrivateResponse) { Http::TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, {"age", "2"}, - {"cache-control", "private, max-age=3600"}}; + {"cache-control", "private,max-age=3600"}}; const std::string request_path("Name"); LookupContextPtr name_lookup_context = lookup(request_path); @@ -149,27 +149,21 @@ class SimpleHttpCacheTest1Response } }; -TEST_P(SimpleHttpCacheTest1Response, Expired) { +TEST_P(SimpleHttpCacheTest1Response, Basic) { Http::TestHeaderMapImpl response_headers = responseHeaders(); // TODO(toddmgreer) Test with various date headers. - response_headers.addCopy("date", formatter_.fromTime(current_time_)); insert("/", response_headers, ""); lookup("/"); EXPECT_EQ(expectedStatus(), lookup_result_.cache_entry_status); } const std::vector expired_headers = { - {{"date", "Thu, 01 Jan 2100 00:00:00 GMT"}, {"cache-control", "public, max-age=3600"}}, - {{"cache-control", "public, s-max-age=-1"}}, - {{"cache-control", "public, max-age=-1"}}, - {{"date", "foo"}}, - {{"date", kEpochDate}, {"expires", "foo"}}, - {{"expires", kEpochDate}, {"cache-control", "public"}}, - {{"age", "2"}, {"cache-control", "public"}}, - {{"age", "6000"}}}; + {{"date", "Thu, 01 Jan 2019 00:00:00 GMT"}, {"cache-control", "public, max-age=3600"}}}; const std::vector ok_headers = { - {{"cache-control", "public, max-age=3600"}}}; + {{"cache-control", "public, max-age=3600"}} + // TODO(toddmgreer) {{"expires", kEpochDate}, {"cache-control", "public"}} +}; INSTANTIATE_TEST_SUITE_P(Expired, SimpleHttpCacheTest1Response, testing::Combine(testing::Values(CacheEntryStatus::RequiresValidation), From 1ac7e7a555301dffbf75f189abb8ae133b44f6d6 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 21 Jun 2019 14:51:17 -0700 Subject: [PATCH 012/109] Comment helper functions. Signed-off-by: Todd Greer --- .../filters/http/cache/http_cache_utils.cc | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index 9526abab93b07..9801305251f0c 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -16,6 +16,11 @@ namespace Internal { namespace { +// True for characters defined as tchars by +// https://tools.ietf.org/html/rfc7230#section-3.2.6 +// +// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" +// / "-" / "." / "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA bool tchar(char c) { switch (c) { case '!': @@ -37,6 +42,11 @@ bool tchar(char c) { return absl::ascii_isalnum(c); } +// Removes an initial HTTP header field value token, as defined by +// https://tools.ietf.org/html/rfc7230#section-3.2.6. Returns true if an initial +// token was present. +// +// token = 1*tchar bool eatToken(absl::string_view& s) { const absl::string_view::iterator token_end = c_find_if_not(s, &tchar); if (token_end == s.begin()) { @@ -46,6 +56,18 @@ bool eatToken(absl::string_view& s) { return true; } +// Removes an initial token or quoted-string (if present), as defined by +// https://tools.ietf.org/html/rfc7234#section-5.2. If a cache-control directive +// has an argument (as indicated by '='), it should be in this form. +// +// quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE +// qdtext = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text +// obs-text = %x80-FF +// quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) +// VCHAR = %x21-7E ; visible (printing) characters +// +// For example, the directive "my-extension=42" has an argument of "42", so an +// input of "public, my-extension=42, max-age=999" void eatDirectiveArgument(absl::string_view& s) { if (s.empty()) { return; @@ -60,8 +82,11 @@ void eatDirectiveArgument(absl::string_view& s) { } } // namespace -// If s is nonnull and begins with decimal digits, return Eat leading digits in -// *s, if any +// If s is nonnull and begins with a decimal number ([0-9]+), removes it from +// the input and returns a SystemTime::duration representing that many seconds. +// If s is null or doesn't begin with digits, returns +// SystemTime::duration::zero(). If parsing overflows, returns +// SystemTime::duration::max(). SystemTime::duration eatLeadingDuration(absl::string_view* s) { const absl::string_view::iterator digits_end = c_find_if_not(*s, &absl::ascii_isdigit); const size_t digits_length = digits_end - s->begin(); @@ -74,6 +99,9 @@ SystemTime::duration eatLeadingDuration(absl::string_view* s) { return absl::SimpleAtoi(digits, &num) ? std::chrono::seconds(num) : SystemTime::duration::max(); } +// Returns the effective max-age represented by cache-control. If the result is +// SystemTime::duration::zero(), or is less than the response's, the response +// should be validated. SystemTime::duration effectiveMaxAge(absl::string_view cache_control) { // The grammar for This Cache-Control header value should be: // Cache-Control = 1#cache-directive @@ -140,7 +168,9 @@ SystemTime httpTime(const Http::HeaderEntry* header_entry) { absl::Time time; const std::string input(header_entry->value().getStringView()); - // RFC 7231 7.1.1.1: Acceptable Date/Time Formats: + // Acceptable Date/Time Formats per + // https://tools.ietf.org/html/rfc7231#section-7.1.1.1 + // // Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate // Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format From 3bb20564db34d310d9f4abcadeec8807ffada2dd Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 21 Jun 2019 14:52:02 -0700 Subject: [PATCH 013/109] Use absl::flat_hash_map instead of std::unordered_map. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/BUILD | 1 + .../filters/http/cache/simple_http_cache.cc | 7 +++---- .../extensions/filters/http/cache/simple_http_cache.h | 11 ++++------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index 134da35e549c0..0e63f0645dd1c 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -91,6 +91,7 @@ envoy_cc_library( "//source/common/protobuf", "//source/extensions/filters/http/common:pass_through_filter_lib", "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/container:flat_hash_map", "@envoy_api//envoy/config/filter/http/cache/v2alpha:cache_cc", ], ) diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index 3b55e95c1fe72..93fde4ddbfed5 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -1,9 +1,8 @@ -#include "extensions/filters/http/cache/simple_http_cache.h" - -#include "envoy/registry/registry.h" - +#include "common/buffer/buffer_impl.h" #include "common/common/lock_guard.h" #include "common/http/header_map_impl.h" +#include "envoy/registry/registry.h" +#include "extensions/filters/http/cache/simple_http_cache.h" namespace Envoy { namespace Extensions { diff --git a/source/extensions/filters/http/cache/simple_http_cache.h b/source/extensions/filters/http/cache/simple_http_cache.h index 16e2651ffe6b7..99ea4c4dad6b8 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.h +++ b/source/extensions/filters/http/cache/simple_http_cache.h @@ -1,15 +1,11 @@ #pragma once -#include - -#include "common/buffer/buffer_impl.h" +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" #include "common/common/thread.h" #include "common/protobuf/utility.h" - #include "extensions/filters/http/cache/http_cache.h" -#include "absl/base/thread_annotations.h" - namespace Envoy { namespace Extensions { namespace HttpFilters { @@ -35,7 +31,8 @@ class SimpleHttpCache : public HttpCache { void insert(const Key& key, Http::HeaderMapPtr&& response_headers, std::string&& body); mutable Thread::MutexBasicLockable mutex_; - std::unordered_map map_ GUARDED_BY(mutex_); + absl::flat_hash_map map_ + GUARDED_BY(mutex_); }; } // namespace Cache From f490c86608c35fccd676636f1dabd8e32f035dcb Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 21 Jun 2019 14:55:27 -0700 Subject: [PATCH 014/109] Fix formatting and spelling. Signed-off-by: Todd Greer --- .../extensions/filters/http/cache/simple_http_cache.cc | 6 ++++-- source/extensions/filters/http/cache/simple_http_cache.h | 9 +++++---- tools/spelling_dictionary.txt | 1 + 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index 93fde4ddbfed5..9d71079e86a74 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -1,8 +1,10 @@ +#include "extensions/filters/http/cache/simple_http_cache.h" + +#include "envoy/registry/registry.h" + #include "common/buffer/buffer_impl.h" #include "common/common/lock_guard.h" #include "common/http/header_map_impl.h" -#include "envoy/registry/registry.h" -#include "extensions/filters/http/cache/simple_http_cache.h" namespace Envoy { namespace Extensions { diff --git a/source/extensions/filters/http/cache/simple_http_cache.h b/source/extensions/filters/http/cache/simple_http_cache.h index 99ea4c4dad6b8..ef797bbefe2eb 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.h +++ b/source/extensions/filters/http/cache/simple_http_cache.h @@ -1,11 +1,13 @@ #pragma once -#include "absl/base/thread_annotations.h" -#include "absl/container/flat_hash_map.h" #include "common/common/thread.h" #include "common/protobuf/utility.h" + #include "extensions/filters/http/cache/http_cache.h" +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" + namespace Envoy { namespace Extensions { namespace HttpFilters { @@ -31,8 +33,7 @@ class SimpleHttpCache : public HttpCache { void insert(const Key& key, Http::HeaderMapPtr&& response_headers, std::string&& body); mutable Thread::MutexBasicLockable mutex_; - absl::flat_hash_map map_ - GUARDED_BY(mutex_); + absl::flat_hash_map map_ GUARDED_BY(mutex_); }; } // namespace Cache diff --git a/tools/spelling_dictionary.txt b/tools/spelling_dictionary.txt index ae93e69f9e93e..d4427b99ffb70 100644 --- a/tools/spelling_dictionary.txt +++ b/tools/spelling_dictionary.txt @@ -295,6 +295,7 @@ qdtext satisfiable sendto tchar +tchars unsatisfiable vip xDSes From bfdf8ee0a35f6ce9e74caec4cb676064b5d58635 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 21 Jun 2019 15:28:28 -0700 Subject: [PATCH 015/109] Add scheme checks to isCacheableRequest; misc cleanup. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/cache_filter.cc | 8 ++++++-- source/extensions/filters/http/cache/http_cache.cc | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index db0f9270e21c3..004bc6a7a69a4 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -15,10 +15,14 @@ namespace { bool isCacheableRequest(Http::HeaderMap& headers) { const Http::HeaderEntry* method = headers.Method(); + const Http::HeaderEntry* scheme = headers.Scheme(); + const Http::HeaderValues& header_values = Http::Headers::get(); // TODO(toddmgreer) Also serve HEAD requests from cache. // TODO(toddmgreer) Check all the other cache-related headers. - return method && headers.Path() && headers.Host() && - (method->value().getStringView() == Http::Headers::get().MethodValues.Get); + return method && scheme && headers.Path() && headers.Host() && + (method->value() == Http::Headers::get().MethodValues.Get) && + (scheme->value() == header_values.SchemeValues.Http || + scheme->value() == header_values.SchemeValues.Https); } bool isCacheableResponse(Http::HeaderMap& headers) { diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index 1f73359218a26..0fbab656d57de 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -25,6 +25,9 @@ LookupRequest::LookupRequest(const Http::HeaderMap& request_headers, SystemTime "with null Scheme."); ASSERT(request_headers.Host(), "Can't form cache lookup key for malformed Http::HeaderMap " "with null Host."); + const Http::HeaderString& scheme = request_headers.Scheme()->value(); + const auto& scheme_values = Http::Headers::get().SchemeValues; + ASSERT(scheme == scheme_values.Http || scheme == scheme_values.Https); // TODO(toddmgreer) Let config determine whether to include scheme, host, and // query params. // TODO(toddmgreer) get cluster name. @@ -32,10 +35,7 @@ LookupRequest::LookupRequest(const Http::HeaderMap& request_headers, SystemTime key_.set_cluster_name("cluster_name_goes_here"); key_.set_host(std::string(request_headers.Host()->value().getStringView())); key_.set_path(std::string(request_headers.Path()->value().getStringView())); - const Http::HeaderString& scheme = request_headers.Scheme()->value(); - ASSERT(scheme == Http::Headers::get().SchemeValues.Http || - scheme == Http::Headers::get().SchemeValues.Https); - key_.set_clear_http(scheme == Http::Headers::get().SchemeValues.Http); + key_.set_clear_http(scheme == scheme_values.Http); } // Unless this API is still alpha, calls to stableHashKey() must always return From 1442b8c13302f15c371384682b622452feaf3332 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 21 Jun 2019 16:07:18 -0700 Subject: [PATCH 016/109] Add cache-control parsing TODO, and fix eatLeadingDuration's arg type. Signed-off-by: Todd Greer --- .../filters/http/cache/http_cache_utils.cc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index 9801305251f0c..e38b9130d91ee 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -87,14 +87,14 @@ void eatDirectiveArgument(absl::string_view& s) { // If s is null or doesn't begin with digits, returns // SystemTime::duration::zero(). If parsing overflows, returns // SystemTime::duration::max(). -SystemTime::duration eatLeadingDuration(absl::string_view* s) { - const absl::string_view::iterator digits_end = c_find_if_not(*s, &absl::ascii_isdigit); - const size_t digits_length = digits_end - s->begin(); +SystemTime::duration eatLeadingDuration(absl::string_view& s) { + const absl::string_view::iterator digits_end = c_find_if_not(s, &absl::ascii_isdigit); + const size_t digits_length = digits_end - s.begin(); if (digits_length == 0) { return SystemTime::duration::zero(); } - const absl::string_view digits(s->begin(), digits_length); - s->remove_prefix(digits_length); + const absl::string_view digits(s.begin(), digits_length); + s.remove_prefix(digits_length); uint64_t num; return absl::SimpleAtoi(digits, &num) ? std::chrono::seconds(num) : SystemTime::duration::max(); } @@ -102,6 +102,9 @@ SystemTime::duration eatLeadingDuration(absl::string_view* s) { // Returns the effective max-age represented by cache-control. If the result is // SystemTime::duration::zero(), or is less than the response's, the response // should be validated. +// +// TODO(toddmgreer) Write a CacheControl class to fully parse the cache-control +// header value. Consider sharing with the gzip filter. SystemTime::duration effectiveMaxAge(absl::string_view cache_control) { // The grammar for This Cache-Control header value should be: // Cache-Control = 1#cache-directive @@ -131,7 +134,7 @@ SystemTime::duration effectiveMaxAge(absl::string_view cache_control) { return SystemTime::duration::zero(); } } else if (ConsumePrefix(&cache_control, "s-maxage=")) { - max_age = eatLeadingDuration(&cache_control); + max_age = eatLeadingDuration(cache_control); found_s_maxage = true; cache_control = StripLeadingAsciiWhitespace(cache_control); if (!cache_control.empty() && cache_control[0] != ',') { @@ -139,7 +142,7 @@ SystemTime::duration effectiveMaxAge(absl::string_view cache_control) { return SystemTime::duration::zero(); } } else if (!found_s_maxage && ConsumePrefix(&cache_control, "max-age=")) { - max_age = eatLeadingDuration(&cache_control); + max_age = eatLeadingDuration(cache_control); if (!cache_control.empty() && cache_control[0] != ',') { // Unexpected text at end of directive return SystemTime::duration::zero(); From ef53f4cab824d1a4ed4e3c6750df17fb1414e0e3 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Mon, 24 Jun 2019 17:10:54 -0700 Subject: [PATCH 017/109] Fix SystemTime subtraction underflow, and improve tests. Signed-off-by: Todd Greer --- .../filters/http/cache/http_cache_utils.cc | 4 +- .../filters/http/cache/http_cache_utils.h | 2 +- .../filters/http/cache/http_cache_test.cc | 10 +++++ .../http/cache/http_cache_utils_test.cc | 4 +- .../http/cache/simple_http_cache_test.cc | 45 +++++++------------ 5 files changed, 30 insertions(+), 35 deletions(-) diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index e38b9130d91ee..8d9385c26e742 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -166,7 +166,7 @@ SystemTime::duration effectiveMaxAge(absl::string_view cache_control) { SystemTime httpTime(const Http::HeaderEntry* header_entry) { if (!header_entry) { - return SystemTime::min(); + return SystemTime(); } absl::Time time; const std::string input(header_entry->value().getStringView()); @@ -184,7 +184,7 @@ SystemTime httpTime(const Http::HeaderEntry* header_entry) { return ToChronoTime(time); } } - return SystemTime::min(); + return SystemTime(); } } // namespace Internal } // namespace Cache diff --git a/source/extensions/filters/http/cache/http_cache_utils.h b/source/extensions/filters/http/cache/http_cache_utils.h index d1f3470bb27da..1b956dfd813e7 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.h +++ b/source/extensions/filters/http/cache/http_cache_utils.h @@ -17,7 +17,7 @@ namespace Internal { // s-maxage is malformed. SystemTime::duration effectiveMaxAge(absl::string_view cache_control); -// Parses header_entry as an HTTP time. Returns SystemTime::max() if +// Parses header_entry as an HTTP time. Returns SystemTime() if // header_entry is null or malformed. SystemTime httpTime(const Http::HeaderEntry* header_entry); } // namespace Internal diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index 2bfdb6824f6a2..67905fb0bc392 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -29,6 +29,16 @@ TEST_F(LookupRequestTest, makeLookupResult) { << formatter_.fromTime(current_time_); } +TEST_F(LookupRequestTest, makeLookupResultNoDate) { + Http::HeaderMapPtr response_headers = + Http::makeHeaderMap({{"cache-control", "public, max-age=3600"}}); + const LookupResult lookup_response = + lookup_request_.makeLookupResult(std::move(response_headers), 0); + EXPECT_EQ(CacheEntryStatus::RequiresValidation, + lookup_response.cache_entry_status) + << formatter_.fromTime(current_time_); +} + TEST_F(LookupRequestTest, PrivateResponse) { Http::HeaderMapPtr response_headers = Http::makeHeaderMap({{"age", "2"}, diff --git a/test/extensions/filters/http/cache/http_cache_utils_test.cc b/test/extensions/filters/http/cache/http_cache_utils_test.cc index 93e500144bfb3..0c08c55ec4aa1 100644 --- a/test/extensions/filters/http/cache/http_cache_utils_test.cc +++ b/test/extensions/filters/http/cache/http_cache_utils_test.cc @@ -33,9 +33,7 @@ TEST_P(HttpTimeTest, Ok) { EXPECT_STREQ(ctime(&time), "Sun Nov 6 08:49:37 1994\n"); } -TEST(HttpTime, Null) { EXPECT_EQ(httpTime(nullptr), SystemTime::min()); } - -TEST(HttpTime, Ok) { EXPECT_EQ(httpTime(nullptr), SystemTime::min()); } +TEST(HttpTime, Null) { EXPECT_EQ(httpTime(nullptr), SystemTime()); } TEST(EffectiveMaxAge, Ok) { EXPECT_EQ(std::chrono::seconds(3600), effectiveMaxAge("public, max-age=3600")); diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index 3014c52ab2261..e218ab7536a97 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -138,40 +138,27 @@ TEST_F(SimpleHttpCacheTest, Miss) { EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); } -class SimpleHttpCacheTest1Response - : public SimpleHttpCacheTest, - public testing::WithParamInterface< - testing::tuple> { -protected: - CacheEntryStatus expectedStatus() const { return testing::get(GetParam()); } - const Http::TestHeaderMapImpl& responseHeaders() const { - return testing::get(GetParam()); - } -}; - -TEST_P(SimpleHttpCacheTest1Response, Basic) { - Http::TestHeaderMapImpl response_headers = responseHeaders(); +TEST_F(SimpleHttpCacheTest, Fresh) { + const Http::TestHeaderMapImpl response_headers = { + {"date", formatter_.fromTime(current_time_)}, + {"cache-control", "public, max-age=3600"}}; // TODO(toddmgreer) Test with various date headers. insert("/", response_headers, ""); + time_source_.sleep(std::chrono::seconds(3600)); lookup("/"); - EXPECT_EQ(expectedStatus(), lookup_result_.cache_entry_status); + EXPECT_EQ(CacheEntryStatus::Ok, lookup_result_.cache_entry_status); } -const std::vector expired_headers = { - {{"date", "Thu, 01 Jan 2019 00:00:00 GMT"}, {"cache-control", "public, max-age=3600"}}}; - -const std::vector ok_headers = { - {{"cache-control", "public, max-age=3600"}} - // TODO(toddmgreer) {{"expires", kEpochDate}, {"cache-control", "public"}} -}; - -INSTANTIATE_TEST_SUITE_P(Expired, SimpleHttpCacheTest1Response, - testing::Combine(testing::Values(CacheEntryStatus::RequiresValidation), - testing::ValuesIn(expired_headers))); - -INSTANTIATE_TEST_SUITE_P(Ok, SimpleHttpCacheTest1Response, - testing::Combine(testing::Values(CacheEntryStatus::Ok), - testing::ValuesIn(ok_headers))); +TEST_F(SimpleHttpCacheTest, Stale) { + const Http::TestHeaderMapImpl response_headers = { + {"date", formatter_.fromTime(current_time_)}, + {"cache-control", "public, max-age=3600"}}; + // TODO(toddmgreer) Test with various date headers. + insert("/", response_headers, ""); + time_source_.sleep(std::chrono::seconds(3601)); + lookup("/"); + EXPECT_EQ(CacheEntryStatus::Ok, lookup_result_.cache_entry_status); +} TEST_F(SimpleHttpCacheTest, RequestSmallMinFresh) { request_headers_.setReferenceKey(Http::Headers::get().CacheControl, "min-fresh=1000"); From b4ceab6a2e1f29ac661b070bb924846394c50cfe Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 26 Jun 2019 11:48:24 -0700 Subject: [PATCH 018/109] Add owners. Signed-off-by: Todd Greer --- CODEOWNERS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 5df7c9c8ea9d9..d1d130de6e626 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -33,4 +33,6 @@ # zookeeper_proxy extension /*/extensions/filters/network/zookeeper_proxy @rgs1 @snowp # redis cluster extension -/*/extensions/clusters/redis @msukalski @henryyyang @mattklein123 \ No newline at end of file +/*/extensions/clusters/redis @msukalski @henryyyang @mattklein123 +# HTTP caching extension +/*/extensions/filters/http/cache @toddmgreer @jmarantz \ No newline at end of file From 7bf20cd0819a813e1c9b0930fb6932af7d6dc379 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 26 Jun 2019 13:30:02 -0700 Subject: [PATCH 019/109] Fix formatting. Signed-off-by: Todd Greer --- test/extensions/filters/http/cache/http_cache_test.cc | 3 +-- .../filters/http/cache/simple_http_cache_test.cc | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index 67905fb0bc392..bc28920e5f035 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -34,8 +34,7 @@ TEST_F(LookupRequestTest, makeLookupResultNoDate) { Http::makeHeaderMap({{"cache-control", "public, max-age=3600"}}); const LookupResult lookup_response = lookup_request_.makeLookupResult(std::move(response_headers), 0); - EXPECT_EQ(CacheEntryStatus::RequiresValidation, - lookup_response.cache_entry_status) + EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status) << formatter_.fromTime(current_time_); } diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index e218ab7536a97..01f481157d655 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -139,9 +139,8 @@ TEST_F(SimpleHttpCacheTest, Miss) { } TEST_F(SimpleHttpCacheTest, Fresh) { - const Http::TestHeaderMapImpl response_headers = { - {"date", formatter_.fromTime(current_time_)}, - {"cache-control", "public, max-age=3600"}}; + const Http::TestHeaderMapImpl response_headers = {{"date", formatter_.fromTime(current_time_)}, + {"cache-control", "public, max-age=3600"}}; // TODO(toddmgreer) Test with various date headers. insert("/", response_headers, ""); time_source_.sleep(std::chrono::seconds(3600)); @@ -150,9 +149,8 @@ TEST_F(SimpleHttpCacheTest, Fresh) { } TEST_F(SimpleHttpCacheTest, Stale) { - const Http::TestHeaderMapImpl response_headers = { - {"date", formatter_.fromTime(current_time_)}, - {"cache-control", "public, max-age=3600"}}; + const Http::TestHeaderMapImpl response_headers = {{"date", formatter_.fromTime(current_time_)}, + {"cache-control", "public, max-age=3600"}}; // TODO(toddmgreer) Test with various date headers. insert("/", response_headers, ""); time_source_.sleep(std::chrono::seconds(3601)); From d1b272d13f886fdee442cbbc261a76f4f809399b Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 6 Sep 2019 15:48:43 -0700 Subject: [PATCH 020/109] Add an empty api_proto_package to please check_format. Signed-off-by: Todd Greer --- api/envoy/config/filter/http/cache/v2alpha/BUILD | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/envoy/config/filter/http/cache/v2alpha/BUILD b/api/envoy/config/filter/http/cache/v2alpha/BUILD index a898df5a624b4..ba476d88ebae6 100644 --- a/api/envoy/config/filter/http/cache/v2alpha/BUILD +++ b/api/envoy/config/filter/http/cache/v2alpha/BUILD @@ -1,7 +1,9 @@ -load("@envoy_api//bazel:api_build_system.bzl", "api_proto_library_internal") +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_library_internal", "api_proto_package") licenses(["notice"]) # Apache 2 +api_proto_package() + api_proto_library_internal( name = "cache", srcs = ["cache.proto"], From 923181415e50ad32018160da87095c4c1a8e9640 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 6 Sep 2019 16:37:01 -0700 Subject: [PATCH 021/109] Delete redundant absl deps. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/BUILD | 6 ------ 1 file changed, 6 deletions(-) diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index 0e63f0645dd1c..09da335d8de6a 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -69,9 +69,6 @@ envoy_cc_library( deps = [ "//include/envoy/common:time_interface", "//include/envoy/http:header_map_interface", - "@com_google_absl//absl/algorithm:container", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", ], ) @@ -90,8 +87,5 @@ envoy_cc_library( "//source/common/http:headers_lib", "//source/common/protobuf", "//source/extensions/filters/http/common:pass_through_filter_lib", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/container:flat_hash_map", - "@envoy_api//envoy/config/filter/http/cache/v2alpha:cache_cc", ], ) From 481225774f701fcb59ee10609218d4c010679f20 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 6 Sep 2019 19:31:07 -0700 Subject: [PATCH 022/109] Return '{}' instead of 'SystemTime()', per modernize-return-braced-init-list. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/http_cache_utils.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index 8d9385c26e742..243b41b57abde 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -166,7 +166,7 @@ SystemTime::duration effectiveMaxAge(absl::string_view cache_control) { SystemTime httpTime(const Http::HeaderEntry* header_entry) { if (!header_entry) { - return SystemTime(); + return {}; } absl::Time time; const std::string input(header_entry->value().getStringView()); @@ -184,7 +184,7 @@ SystemTime httpTime(const Http::HeaderEntry* header_entry) { return ToChronoTime(time); } } - return SystemTime(); + return {}; } } // namespace Internal } // namespace Cache From 4ffced5633be2f30c7febfc71a07d777c8935663 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Mon, 16 Sep 2019 17:53:03 -0700 Subject: [PATCH 023/109] Prevent creation of an HttpCacheFilter that isn't owned by a shared_ptr. Throw ProtoValidationException on config error. Signed-off-by: Todd Greer --- .../filters/http/cache/cache_filter.cc | 7 +++---- .../filters/http/cache/cache_filter.h | 17 ++++++++++++----- source/extensions/filters/http/cache/config.cc | 3 ++- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 004bc6a7a69a4..a62e808f3e616 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -40,8 +40,8 @@ HttpCache& getCache(const envoy::config::filter::http::cache::v2alpha::Cache& co HttpCacheFactory* factory = Registry::FactoryRegistry::getFactory(config.name()); if (!factory) { - throw EnvoyException( - fmt::format("Didn't find a registered HttpCacheFactory for '{}'", config.name())); + throw ProtoValidationException( + fmt::format("Didn't find a registered HttpCacheFactory for '{}'", config.name()), config); } return factory->getCache(); } @@ -80,8 +80,7 @@ Http::FilterHeadersStatus CacheFilter::encodeHeaders(Http::HeaderMap& headers, b Http::FilterDataStatus CacheFilter::encodeData(Buffer::Instance& data, bool end_stream) { if (insert_) { // TODO(toddmgreer) Wait for the cache if necessary. - insert_->insertBody( - data, [](bool) {}, end_stream); + insert_->insertBody(data, [](bool) {}, end_stream); } return Http::FilterDataStatus::Continue; } diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index eaba194eaa03c..2fce9c185f83a 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -17,17 +17,18 @@ namespace Cache { /** * A filter that caches responses and attempts to satisfy requests from cache. - * Must be owned by a shared_ptr. */ class CacheFilter; using CacheFilterSharedPtr = std::shared_ptr; class CacheFilter : public Http::PassThroughFilter, public std::enable_shared_from_this { public: - // Throws EnvoyException if no registered HttpCacheFactory for config.name. - CacheFilter(const envoy::config::filter::http::cache::v2alpha::Cache& config, - const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source); - + // Throws ProtoValidationException if no registered HttpCacheFactory for config.name. + static CacheFilterSharedPtr make(const envoy::config::filter::http::cache::v2alpha::Cache& config, + const std::string& stats_prefix, Stats::Scope& scope, + TimeSource& time_source) { + return std::shared_ptr(new CacheFilter(config, stats_prefix, scope, time_source)); + } // Http::StreamFilterBase void onDestroy() override; // Http::StreamDecoderFilter @@ -37,6 +38,12 @@ class CacheFilter : public Http::PassThroughFilter, Http::FilterDataStatus encodeData(Buffer::Instance& buffer, bool end_stream) override; private: + // Throws EnvoyException if no registered HttpCacheFactory for config.name. + // Constructor is private to enforce enable_shared_from_this's requirement that this must be owned + // by a shared_ptr. + CacheFilter(const envoy::config::filter::http::cache::v2alpha::Cache& config, + const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source); + void getBody(); void onOkHeaders(Http::HeaderMapPtr&& headers, std::vector&& response_ranges, uint64_t content_length, bool has_trailers); diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index 94450002c00ad..c4ce0d489d5cc 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -19,12 +19,13 @@ Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { return [config, stats_prefix, &context](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter( - std::make_shared(config, stats_prefix, context.scope(), context.timeSource())); + CacheFilter::make(config, stats_prefix, context.scope(), context.timeSource())); }; } REGISTER_FACTORY(CacheFilterFactory, Server::Configuration::NamedHttpFilterConfigFactory); + } // namespace Cache } // namespace HttpFilters } // namespace Extensions From 6f3a2212f2185579924817a1f3d5e3cd8712e62b Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Mon, 16 Sep 2019 17:55:53 -0700 Subject: [PATCH 024/109] Add CacheFilterFactory tests. Signed-off-by: Todd Greer --- test/extensions/filters/http/cache/BUILD | 14 ++++++ .../filters/http/cache/config_test.cc | 45 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 test/extensions/filters/http/cache/config_test.cc diff --git a/test/extensions/filters/http/cache/BUILD b/test/extensions/filters/http/cache/BUILD index 1584a016d5fd9..b8e83f8b6e751 100644 --- a/test/extensions/filters/http/cache/BUILD +++ b/test/extensions/filters/http/cache/BUILD @@ -19,6 +19,20 @@ envoy_extension_cc_test( ], ) +envoy_extension_cc_test( + name = "config_test", + srcs = ["config_test.cc"], + extension_name = "envoy.filters.http.cache", + deps = [ + "//source/extensions/filters/http/cache:config", + "//source/extensions/filters/http/cache:simple_http_cache_lib", + "//test/mocks/http:http_mocks", + "//test/mocks/server:server_mocks", + "//test/test_common:simulated_time_system_lib", + "//test/test_common:utility_lib", + ], +) + envoy_extension_cc_test( name = "http_cache_utils_test", srcs = ["http_cache_utils_test.cc"], diff --git a/test/extensions/filters/http/cache/config_test.cc b/test/extensions/filters/http/cache/config_test.cc new file mode 100644 index 0000000000000..c565a791d137e --- /dev/null +++ b/test/extensions/filters/http/cache/config_test.cc @@ -0,0 +1,45 @@ +#include "extensions/filters/http/cache/config.h" +#include "extensions/filters/http/cache/simple_http_cache.h" +#include "test/mocks/server/mocks.h" +#include "test/test_common/utility.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { +namespace { + +class CacheFilterFactoryTest:public ::testing::Test{ + protected: + envoy::config::filter::http::cache::v2alpha::Cache config_; + NiceMock context_; + CacheFilterFactory factory_; + Http::MockFilterChainFactoryCallbacks filter_callback_; +}; + +TEST_F(CacheFilterFactoryTest, Basic) { + config_.set_name("SimpleHttpCache"); + Http::FilterFactoryCb cb = factory_.createFilterFactoryFromProto(config_, "stats", context_); + Http::StreamFilterSharedPtr filter; + EXPECT_CALL(filter_callback_, addStreamFilter(_)).WillOnce(::testing::SaveArg<0>(&filter)); + cb(filter_callback_); + ASSERT(filter); + ASSERT(dynamic_cast(filter.get())); +} + +TEST_F(CacheFilterFactoryTest, NoName) { + Http::FilterFactoryCb cb = factory_.createFilterFactoryFromProto(config_, "stats", context_); + EXPECT_THROW(cb(filter_callback_), ProtoValidationException);; +} + +TEST_F(CacheFilterFactoryTest, UnregisteredName) { + config_.set_name("Wrong"); + Http::FilterFactoryCb cb = factory_.createFilterFactoryFromProto(config_, "stats", context_); + EXPECT_THROW(cb(filter_callback_), ProtoValidationException);; +} +} // namespace +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy From f2bf5cb1a1ae8db728bc5cc37af71ef12be7e3a8 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Mon, 16 Sep 2019 18:02:04 -0700 Subject: [PATCH 025/109] Fix formatting. Signed-off-by: Todd Greer --- .../extensions/filters/http/cache/cache_filter.cc | 4 ++-- source/extensions/filters/http/cache/config.cc | 4 +--- source/extensions/filters/http/cache/http_cache.cc | 3 +-- .../filters/http/cache/http_cache_utils.cc | 4 ++-- .../filters/http/cache/simple_http_cache.cc | 4 ++-- test/extensions/filters/http/cache/config_test.cc | 14 +++++++++----- .../filters/http/cache/simple_http_cache_test.cc | 3 +-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index a62e808f3e616..68a92b5c69805 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -1,9 +1,9 @@ -#include "extensions/filters/http/cache/cache_filter.h" - #include "envoy/registry/registry.h" #include "common/http/headers.h" +#include "extensions/filters/http/cache/cache_filter.h" + #include "absl/memory/memory.h" #include "absl/strings/string_view.h" diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index c4ce0d489d5cc..97124238c433a 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -1,5 +1,3 @@ -#include "extensions/filters/http/cache/config.h" - #include #include "envoy/common/time.h" @@ -8,6 +6,7 @@ #include "envoy/stats/scope.h" #include "extensions/filters/http/cache/cache_filter.h" +#include "extensions/filters/http/cache/config.h" namespace Envoy { namespace Extensions { @@ -25,7 +24,6 @@ Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( REGISTER_FACTORY(CacheFilterFactory, Server::Configuration::NamedHttpFilterConfigFactory); - } // namespace Cache } // namespace HttpFilters } // namespace Extensions diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index 0fbab656d57de..c821d1499258e 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -1,10 +1,9 @@ -#include "extensions/filters/http/cache/http_cache.h" - #include #include "common/http/headers.h" #include "common/protobuf/utility.h" +#include "extensions/filters/http/cache/http_cache.h" #include "extensions/filters/http/cache/http_cache_utils.h" #include "absl/time/time.h" diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index 243b41b57abde..788517a99b9de 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -1,8 +1,8 @@ -#include "extensions/filters/http/cache/http_cache_utils.h" - #include #include +#include "extensions/filters/http/cache/http_cache_utils.h" + #include "absl/algorithm/container.h" #include "absl/strings/ascii.h" #include "absl/strings/numbers.h" diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index 9d71079e86a74..1348dac364744 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -1,11 +1,11 @@ -#include "extensions/filters/http/cache/simple_http_cache.h" - #include "envoy/registry/registry.h" #include "common/buffer/buffer_impl.h" #include "common/common/lock_guard.h" #include "common/http/header_map_impl.h" +#include "extensions/filters/http/cache/simple_http_cache.h" + namespace Envoy { namespace Extensions { namespace HttpFilters { diff --git a/test/extensions/filters/http/cache/config_test.cc b/test/extensions/filters/http/cache/config_test.cc index c565a791d137e..f8f6f685fd02d 100644 --- a/test/extensions/filters/http/cache/config_test.cc +++ b/test/extensions/filters/http/cache/config_test.cc @@ -1,7 +1,9 @@ #include "extensions/filters/http/cache/config.h" #include "extensions/filters/http/cache/simple_http_cache.h" + #include "test/mocks/server/mocks.h" #include "test/test_common/utility.h" + #include "gtest/gtest.h" namespace Envoy { @@ -10,8 +12,8 @@ namespace HttpFilters { namespace Cache { namespace { -class CacheFilterFactoryTest:public ::testing::Test{ - protected: +class CacheFilterFactoryTest : public ::testing::Test { +protected: envoy::config::filter::http::cache::v2alpha::Cache config_; NiceMock context_; CacheFilterFactory factory_; @@ -30,15 +32,17 @@ TEST_F(CacheFilterFactoryTest, Basic) { TEST_F(CacheFilterFactoryTest, NoName) { Http::FilterFactoryCb cb = factory_.createFilterFactoryFromProto(config_, "stats", context_); - EXPECT_THROW(cb(filter_callback_), ProtoValidationException);; + EXPECT_THROW(cb(filter_callback_), ProtoValidationException); + ; } TEST_F(CacheFilterFactoryTest, UnregisteredName) { config_.set_name("Wrong"); Http::FilterFactoryCb cb = factory_.createFilterFactoryFromProto(config_, "stats", context_); - EXPECT_THROW(cb(filter_callback_), ProtoValidationException);; + EXPECT_THROW(cb(filter_callback_), ProtoValidationException); + ; } -} // namespace +} // namespace } // namespace Cache } // namespace HttpFilters } // namespace Extensions diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index 01f481157d655..836fff401a97c 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -194,8 +194,7 @@ TEST_F(SimpleHttpCacheTest, StreamingPut) { {"cache-control", "public, max-age=3600"}}; InsertContextPtr inserter = cache_.makeInsertContext(lookup("request_path")); inserter->insertHeaders(response_headers, false); - inserter->insertBody( - Buffer::OwnedImpl("Hello, "), [](bool ready) { EXPECT_TRUE(ready); }, false); + inserter->insertBody(Buffer::OwnedImpl("Hello, "), [](bool ready) { EXPECT_TRUE(ready); }, false); inserter->insertBody(Buffer::OwnedImpl("World!"), nullptr, true); LookupContextPtr name_lookup_context = lookup("request_path"); EXPECT_EQ(CacheEntryStatus::Ok, lookup_result_.cache_entry_status); From 828331b5aa191edbd35f725cfc7eabe0365f4c89 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Mon, 16 Sep 2019 18:04:30 -0700 Subject: [PATCH 026/109] Fix formatting. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/cache_filter.cc | 4 ++-- source/extensions/filters/http/cache/config.cc | 3 ++- source/extensions/filters/http/cache/http_cache.cc | 3 ++- source/extensions/filters/http/cache/http_cache_utils.cc | 4 ++-- source/extensions/filters/http/cache/simple_http_cache.cc | 4 ++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 68a92b5c69805..a62e808f3e616 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -1,9 +1,9 @@ +#include "extensions/filters/http/cache/cache_filter.h" + #include "envoy/registry/registry.h" #include "common/http/headers.h" -#include "extensions/filters/http/cache/cache_filter.h" - #include "absl/memory/memory.h" #include "absl/strings/string_view.h" diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index 97124238c433a..c4189f06735ff 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -1,3 +1,5 @@ +#include "extensions/filters/http/cache/config.h" + #include #include "envoy/common/time.h" @@ -6,7 +8,6 @@ #include "envoy/stats/scope.h" #include "extensions/filters/http/cache/cache_filter.h" -#include "extensions/filters/http/cache/config.h" namespace Envoy { namespace Extensions { diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index c821d1499258e..0fbab656d57de 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -1,9 +1,10 @@ +#include "extensions/filters/http/cache/http_cache.h" + #include #include "common/http/headers.h" #include "common/protobuf/utility.h" -#include "extensions/filters/http/cache/http_cache.h" #include "extensions/filters/http/cache/http_cache_utils.h" #include "absl/time/time.h" diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index 788517a99b9de..243b41b57abde 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -1,8 +1,8 @@ +#include "extensions/filters/http/cache/http_cache_utils.h" + #include #include -#include "extensions/filters/http/cache/http_cache_utils.h" - #include "absl/algorithm/container.h" #include "absl/strings/ascii.h" #include "absl/strings/numbers.h" diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index 1348dac364744..9d71079e86a74 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -1,11 +1,11 @@ +#include "extensions/filters/http/cache/simple_http_cache.h" + #include "envoy/registry/registry.h" #include "common/buffer/buffer_impl.h" #include "common/common/lock_guard.h" #include "common/http/header_map_impl.h" -#include "extensions/filters/http/cache/simple_http_cache.h" - namespace Envoy { namespace Extensions { namespace HttpFilters { From eda41c7a51720c5838c905dc05bb8d82070965f4 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 17 Sep 2019 11:55:53 -0700 Subject: [PATCH 027/109] Fix formatting, this time with the right version of clang-format. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/cache_filter.cc | 3 ++- test/extensions/filters/http/cache/simple_http_cache_test.cc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index a62e808f3e616..a44c2d60b82d4 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -80,7 +80,8 @@ Http::FilterHeadersStatus CacheFilter::encodeHeaders(Http::HeaderMap& headers, b Http::FilterDataStatus CacheFilter::encodeData(Buffer::Instance& data, bool end_stream) { if (insert_) { // TODO(toddmgreer) Wait for the cache if necessary. - insert_->insertBody(data, [](bool) {}, end_stream); + insert_->insertBody( + data, [](bool) {}, end_stream); } return Http::FilterDataStatus::Continue; } diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index 836fff401a97c..01f481157d655 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -194,7 +194,8 @@ TEST_F(SimpleHttpCacheTest, StreamingPut) { {"cache-control", "public, max-age=3600"}}; InsertContextPtr inserter = cache_.makeInsertContext(lookup("request_path")); inserter->insertHeaders(response_headers, false); - inserter->insertBody(Buffer::OwnedImpl("Hello, "), [](bool ready) { EXPECT_TRUE(ready); }, false); + inserter->insertBody( + Buffer::OwnedImpl("Hello, "), [](bool ready) { EXPECT_TRUE(ready); }, false); inserter->insertBody(Buffer::OwnedImpl("World!"), nullptr, true); LookupContextPtr name_lookup_context = lookup("request_path"); EXPECT_EQ(CacheEntryStatus::Ok, lookup_result_.cache_entry_status); From 56f6434370a347495eadbad98bfebb55dfb3c1d9 Mon Sep 17 00:00:00 2001 From: Josiah Kiehl Date: Mon, 23 Sep 2019 11:25:04 -0400 Subject: [PATCH 028/109] Add tests for RawByteRange Signed-off-by: Josiah Kiehl --- .../filters/http/cache/http_cache.h | 4 ++ .../filters/http/cache/http_cache_test.cc | 50 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index a48d1469c985d..457daa8f3b7d3 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -54,8 +54,12 @@ class RawByteRange { return last_byte_pos_; } uint64_t suffixLength() const { +<<<<<<< HEAD + return last_byte_pos_ - first_byte_pos_; +======= ASSERT(isSuffix()); return last_byte_pos_; +>>>>>>> bf972c452... fixupppp } private: diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index bc28920e5f035..618db84f03fa5 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -10,6 +10,56 @@ namespace Extensions { namespace HttpFilters { namespace Cache { +class RawByteRangeTest : public testing::Test {}; + + +using RawByteRangeDeathTest = RawByteRangeTest; + + +TEST_F(RawByteRangeTest, IsSuffix) { + auto r = RawByteRange(UINT64_MAX, 4); + ASSERT_TRUE(r.isSuffix()); +} + +TEST_F(RawByteRangeTest, IsNotSuffix) { + auto r = RawByteRange(3, 4); + ASSERT_FALSE(r.isSuffix()); +} + +TEST_F(RawByteRangeTest, IllegalByteRange) { + ASSERT_DEATH(RawByteRange(5, 4), "Illegal byte range"); +} + +TEST_F(RawByteRangeTest, FirstBytePos) { + auto r = RawByteRange(3, 4); + ASSERT_EQ(3, r.firstBytePos()); +} + +TEST_F(RawByteRangeDeathTest, FirstBytePosIfSuffix) { + auto r = RawByteRange(UINT64_MAX, 4); + ASSERT_DEATH(r.firstBytePos(), "!isSuffix()"); +} + +TEST_F(RawByteRangeTest, LastBytePos) { + auto r = RawByteRange(3, 4); + ASSERT_EQ(4, r.lastBytePos()); +} + +TEST_F(RawByteRangeTest, LastBytePosIfSuffix) { + auto r = RawByteRange(UINT64_MAX, UINT64_MAX); + ASSERT_DEATH(r.lastBytePos(), "!isSuffix()"); +} + +TEST_F(RawByteRangeDeathTest, SuffixLengthOfNotSuffix) { + auto r = RawByteRange(3, 4); + ASSERT_DEATH(r.suffixLength(), "isSuffix()"); +} + +TEST_F(RawByteRangeTest, suffixLength) { + auto r = RawByteRange(UINT64_MAX, 4); + ASSERT_EQ(4, r.suffixLength()); +} + class LookupRequestTest : public testing::Test { protected: Event::SimulatedTimeSystem time_source_; From 8cc9562ffc36e018867d0823dedc3639f4867e57 Mon Sep 17 00:00:00 2001 From: Josiah Kiehl Date: Mon, 23 Sep 2019 13:34:30 -0400 Subject: [PATCH 029/109] Add tests for AdjustedByteRange. Also add an additional test for RawByteRange ctor Signed-off-by: Josiah Kiehl --- .../filters/http/cache/http_cache.h | 4 +- .../filters/http/cache/http_cache_test.cc | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 457daa8f3b7d3..66400da30c378 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -74,13 +74,13 @@ class AdjustedByteRange { // response body. Prereq: first <= last Invariant: firstBytePos() <= // lastBytePos() AdjustedByteRange(uint64_t first, uint64_t last) : first_byte_pos_(first), last_byte_pos_(last) { - ASSERT(first <= last); + ASSERT(first <= last, "Illegal byte range."); } uint64_t firstBytePos() const { return first_byte_pos_; } uint64_t lastBytePos() const { return last_byte_pos_; } uint64_t length() const { return last_byte_pos_ - first_byte_pos_; } void trimFront(uint64_t n) { - RELEASE_ASSERT(n < length(), "Attempt to trim too much from range."); + RELEASE_ASSERT(n <= length(), "Attempt to trim too much from range."); first_byte_pos_ += n; } diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index 618db84f03fa5..09761bc06129b 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -30,6 +30,12 @@ TEST_F(RawByteRangeTest, IllegalByteRange) { ASSERT_DEATH(RawByteRange(5, 4), "Illegal byte range"); } +TEST_F(RawByteRangeTest, IllegalByteRangeButIsSuffix) { + // no death here + RawByteRange(UINT64_MAX, 3); + ASSERT_TRUE(true); +} + TEST_F(RawByteRangeTest, FirstBytePos) { auto r = RawByteRange(3, 4); ASSERT_EQ(3, r.firstBytePos()); @@ -60,6 +66,40 @@ TEST_F(RawByteRangeTest, suffixLength) { ASSERT_EQ(4, r.suffixLength()); } +class AdjustedByteRangeTest : public testing::Test {}; + +TEST_F(AdjustedByteRangeTest, IllegalByteRange) { + ASSERT_DEATH(AdjustedByteRange(5, 4), "Illegal byte range."); +} + +TEST_F(AdjustedByteRangeTest, Length) { + auto a = AdjustedByteRange(3, 6); + ASSERT_EQ(3, a.length()); +} + +TEST_F(AdjustedByteRangeTest, TrimFront) { + auto a = AdjustedByteRange(3, 6); + a.trimFront(2); + ASSERT_EQ(5, a.firstBytePos()); +} + +TEST_F(AdjustedByteRangeTest, TrimFrontTooMuch) { + auto a = AdjustedByteRange(3, 6); + ASSERT_DEATH(a.trimFront(5), "Attempt to trim too much from range."); +} + +TEST_F(AdjustedByteRangeTest, MaxLength) { + auto a = AdjustedByteRange(0, UINT64_MAX); + ASSERT_EQ(UINT64_MAX, a.length()); +} + +TEST_F(AdjustedByteRangeTest, MaxTrim) { + auto a = AdjustedByteRange(0, UINT64_MAX); + a.trimFront(UINT64_MAX); + ASSERT_EQ(UINT64_MAX, a.firstBytePos()); + ASSERT_EQ(0, a.length()); +} + class LookupRequestTest : public testing::Test { protected: Event::SimulatedTimeSystem time_source_; From 3e16e8dd2f7276250ab27c056bca74d43e62ad0b Mon Sep 17 00:00:00 2001 From: Josiah Kiehl Date: Mon, 23 Sep 2019 13:57:49 -0400 Subject: [PATCH 030/109] Label death tests appropriately Signed-off-by: Josiah Kiehl --- .../filters/http/cache/http_cache_test.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index 09761bc06129b..a0a73e6d0a946 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -16,7 +16,7 @@ class RawByteRangeTest : public testing::Test {}; using RawByteRangeDeathTest = RawByteRangeTest; -TEST_F(RawByteRangeTest, IsSuffix) { +TEST_F(RawByteRangeTest, isSuffix) { auto r = RawByteRange(UINT64_MAX, 4); ASSERT_TRUE(r.isSuffix()); } @@ -26,11 +26,11 @@ TEST_F(RawByteRangeTest, IsNotSuffix) { ASSERT_FALSE(r.isSuffix()); } -TEST_F(RawByteRangeTest, IllegalByteRange) { +TEST_F(RawByteRangeDeathTest, IllegalByteRange) { ASSERT_DEATH(RawByteRange(5, 4), "Illegal byte range"); } -TEST_F(RawByteRangeTest, IllegalByteRangeButIsSuffix) { +TEST_F(RawByteRangeDeathTest, IllegalByteRangeButIsSuffix) { // no death here RawByteRange(UINT64_MAX, 3); ASSERT_TRUE(true); @@ -51,7 +51,7 @@ TEST_F(RawByteRangeTest, LastBytePos) { ASSERT_EQ(4, r.lastBytePos()); } -TEST_F(RawByteRangeTest, LastBytePosIfSuffix) { +TEST_F(RawByteRangeDeathTest, LastBytePosIfSuffix) { auto r = RawByteRange(UINT64_MAX, UINT64_MAX); ASSERT_DEATH(r.lastBytePos(), "!isSuffix()"); } @@ -68,7 +68,9 @@ TEST_F(RawByteRangeTest, suffixLength) { class AdjustedByteRangeTest : public testing::Test {}; -TEST_F(AdjustedByteRangeTest, IllegalByteRange) { +using AdjustedByteRangeDeathTest = AdjustedByteRangeTest; + +TEST_F(AdjustedByteRangeDeathTest, IllegalByteRange) { ASSERT_DEATH(AdjustedByteRange(5, 4), "Illegal byte range."); } @@ -83,7 +85,7 @@ TEST_F(AdjustedByteRangeTest, TrimFront) { ASSERT_EQ(5, a.firstBytePos()); } -TEST_F(AdjustedByteRangeTest, TrimFrontTooMuch) { +TEST_F(AdjustedByteRangeDeathTest, TrimFrontTooMuch) { auto a = AdjustedByteRange(3, 6); ASSERT_DEATH(a.trimFront(5), "Attempt to trim too much from range."); } From 11e152094be88735eb42ceec86d420f2312a73c6 Mon Sep 17 00:00:00 2001 From: Josiah Kiehl Date: Mon, 23 Sep 2019 16:12:46 -0400 Subject: [PATCH 031/109] Add assert tests for request headers in LookupRequest Signed-off-by: Josiah Kiehl --- .../filters/http/cache/http_cache_test.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index a0a73e6d0a946..b38632ba87048 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -112,6 +112,23 @@ class LookupRequestTest : public testing::Test { const LookupRequest lookup_request_{request_headers_, current_time_}; }; +using LookupRequestDeathTest = LookupRequestTest; + +TEST_F(LookupRequestDeathTest, makeLookupRequestWithNoPath) { + auto headerMap = Http::TestHeaderMapImpl({{":scheme", "http"}, {":authority", "example.com"}}); + ASSERT_DEATH(LookupRequest(headerMap, current_time_), "malformed Http::HeaderMap with null Path"); +} + +TEST_F(LookupRequestDeathTest, makeLookupRequestWithNoScheme) { + auto headerMap = Http::TestHeaderMapImpl({{":path", "/"}, {":authority", "example.com"}}); + ASSERT_DEATH(LookupRequest(headerMap, current_time_), "malformed Http::HeaderMap with null Scheme"); +} + +TEST_F(LookupRequestDeathTest, makeLookupRequestWithNoHost) { + auto headerMap = Http::TestHeaderMapImpl({{":path", "/"}, {":scheme", "http"}}); + ASSERT_DEATH(LookupRequest(headerMap, current_time_), "malformed Http::HeaderMap with null Host"); +} + TEST_F(LookupRequestTest, makeLookupResult) { Http::HeaderMapPtr response_headers = Http::makeHeaderMap( {{"date", formatter_.fromTime(current_time_)}, {"cache-control", "public, max-age=3600"}}); From d0762834b1bc245ad9ff0c0bc747dd582a9b8288 Mon Sep 17 00:00:00 2001 From: Josiah Kiehl Date: Mon, 23 Sep 2019 16:48:57 -0400 Subject: [PATCH 032/109] Add tests for fallback header 'expired' Signed-off-by: Josiah Kiehl --- .../filters/http/cache/http_cache.h | 4 ---- .../filters/http/cache/http_cache_test.cc | 23 ++++++++++++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 66400da30c378..615d3b44805e9 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -54,12 +54,8 @@ class RawByteRange { return last_byte_pos_; } uint64_t suffixLength() const { -<<<<<<< HEAD - return last_byte_pos_ - first_byte_pos_; -======= ASSERT(isSuffix()); return last_byte_pos_; ->>>>>>> bf972c452... fixupppp } private: diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index b38632ba87048..722d33e5d7632 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -121,7 +121,8 @@ TEST_F(LookupRequestDeathTest, makeLookupRequestWithNoPath) { TEST_F(LookupRequestDeathTest, makeLookupRequestWithNoScheme) { auto headerMap = Http::TestHeaderMapImpl({{":path", "/"}, {":authority", "example.com"}}); - ASSERT_DEATH(LookupRequest(headerMap, current_time_), "malformed Http::HeaderMap with null Scheme"); + ASSERT_DEATH(LookupRequest(headerMap, current_time_), + "malformed Http::HeaderMap with null Scheme"); } TEST_F(LookupRequestDeathTest, makeLookupRequestWithNoHost) { @@ -171,6 +172,26 @@ TEST_F(LookupRequestTest, Expired) { EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status); } +TEST_F(LookupRequestTest, ExpiredViaFallbackheader) { + Http::HeaderMapPtr response_headers = Http::makeHeaderMap( + {{"expires", formatter_.fromTime(current_time_ - std::chrono::seconds(5))}, + {"date", formatter_.fromTime(current_time_)}}); + const LookupResult lookup_response = + lookup_request_.makeLookupResult(std::move(response_headers), 0); + + EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status); +} + +TEST_F(LookupRequestTest, NotExpiredViaFallbackheader) { + Http::HeaderMapPtr response_headers = Http::makeHeaderMap( + {{"expires", formatter_.fromTime(current_time_ + std::chrono::seconds(5))}, + {"date", formatter_.fromTime(current_time_)}}); + const LookupResult lookup_response = + lookup_request_.makeLookupResult(std::move(response_headers), 0); + + EXPECT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status); +} + } // namespace Cache } // namespace HttpFilters } // namespace Extensions From afc4523be9a3b8a40eae8ffb70198f60e77eb378 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 26 Sep 2019 14:35:49 -0700 Subject: [PATCH 033/109] Add CacheFilter test. Signed-off-by: Todd Greer --- test/extensions/filters/http/cache/BUILD | 13 ++++ .../filters/http/cache/cache_filter_test.cc | 70 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 test/extensions/filters/http/cache/cache_filter_test.cc diff --git a/test/extensions/filters/http/cache/BUILD b/test/extensions/filters/http/cache/BUILD index b8e83f8b6e751..4645bf6f175a8 100644 --- a/test/extensions/filters/http/cache/BUILD +++ b/test/extensions/filters/http/cache/BUILD @@ -55,3 +55,16 @@ envoy_extension_cc_test( "//test/test_common:utility_lib", ], ) + +envoy_extension_cc_test( + name = "cache_filter_test", + srcs = ["cache_filter_test.cc"], + extension_name = "envoy.filters.http.cache", + deps = [ + "//source/extensions/filters/http/cache:cache_filter_lib", + "//source/extensions/filters/http/cache:simple_http_cache_lib", + "//test/mocks/server:server_mocks", + "//test/test_common:simulated_time_system_lib", + "//test/test_common:utility_lib", + ], +) diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc new file mode 100644 index 0000000000000..dbd657e5285f9 --- /dev/null +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -0,0 +1,70 @@ +#include "extensions/filters/http/cache/cache_filter.h" + +#include "test/mocks/server/mocks.h" +#include "test/test_common/simulated_time_system.h" +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace Cache { +namespace { + +class CacheFilterTest : public ::testing::Test { +protected: + envoy::config::filter::http::cache::v2alpha::Cache config_; + NiceMock context_; + Event::SimulatedTimeSystem time_source_; + SystemTime current_time_ = time_source_.systemTime(); + DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; + Http::TestHeaderMapImpl request_headers_{{":authority", "host"}, + {":path", "/"}, + {":method", "GET"}, + {":scheme", "https"}, + {"Cache-Control", "max-age=3600"}}; + Http::TestHeaderMapImpl response_headers_{{"date", formatter_.fromTime(current_time_)}, + {"cache-control", "public,max-age=3600"}}; + NiceMock decoder_callbacks_; + NiceMock encoder_callbacks_; + + CacheFilterSharedPtr makeFilter() { + CacheFilterSharedPtr filter = + CacheFilter::make(config_, "", context_.scope(), context_.timeSource()); + if (filter) { + filter->setDecoderFilterCallbacks(decoder_callbacks_); + filter->setEncoderFilterCallbacks(encoder_callbacks_); + } + return filter; + } +}; + +TEST_F(CacheFilterTest, make) { + ON_CALL(decoder_callbacks_, dispatcher()).WillByDefault(ReturnRef(context_.dispatcher_)); + ON_CALL(context_.dispatcher_, post(_)).WillByDefault(::testing::InvokeArgument<0>()); + + config_.set_name("SimpleHttpCache"); + CacheFilterSharedPtr filter = makeFilter(); + ASSERT_TRUE(filter); + EXPECT_CALL(decoder_callbacks_, continueDecoding); + EXPECT_EQ(filter->decodeHeaders(request_headers_, true), + Http::FilterHeadersStatus::StopIteration); + ::testing::Mock::VerifyAndClearExpectations(&decoder_callbacks_); + + EXPECT_EQ(filter->encodeHeaders(response_headers_, true), Http::FilterHeadersStatus::Continue); + filter->onDestroy(); + + filter = makeFilter(); + ASSERT_TRUE(filter); + EXPECT_EQ(filter->decodeHeaders(request_headers_, true), + Http::FilterHeadersStatus::StopIteration); + ::testing::Mock::VerifyAndClearExpectations(&decoder_callbacks_); + filter->onDestroy(); +} + +} // namespace +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy From 969ee2fc8acf2bdf0d9f1aa74e76983a9fe97740 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 26 Sep 2019 15:04:58 -0700 Subject: [PATCH 034/109] Fix incorrect naming style. Signed-off-by: Todd Greer --- .../http/cache/http_cache_utils_test.cc | 4 +- .../http/cache/simple_http_cache_test.cc | 46 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/test/extensions/filters/http/cache/http_cache_utils_test.cc b/test/extensions/filters/http/cache/http_cache_utils_test.cc index 0c08c55ec4aa1..bbd9d2af404ea 100644 --- a/test/extensions/filters/http/cache/http_cache_utils_test.cc +++ b/test/extensions/filters/http/cache/http_cache_utils_test.cc @@ -15,12 +15,12 @@ namespace Cache { namespace Internal { namespace { -class HttpTimeTest : public testing::TestWithParam { +class HttpTimeTest : public testing::TestWithParam { protected: Http::TestHeaderMapImpl response_headers_{{"date", GetParam()}}; }; -const std::vector ok_times = { +const char*const ok_times[] = { "Sun, 06 Nov 1994 08:49:37 GMT", // IMF-fixdate "Sunday, 06-Nov-94 08:49:37 GMT", // obsolete RFC 850 format "Sun Nov 6 08:49:37 1994" // ANSI C's asctime() format diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index 01f481157d655..26f1f6ef4a9ef 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -16,7 +16,7 @@ namespace HttpFilters { namespace Cache { namespace { -const std::string kEpochDate = "Thu, 01 Jan 1970 00:00:00 GMT"; +const std::string EpochDate = "Thu, 01 Jan 1970 00:00:00 GMT"; class SimpleHttpCacheTest : public testing::Test { protected: @@ -63,9 +63,9 @@ class SimpleHttpCacheTest : public testing::Test { LookupRequest makeLookupRequest(absl::string_view request_path) { request_headers_.insertPath().value(request_path); return LookupRequest(request_headers_, current_time_); - }; + } - AssertionResult ExpectLookupSuccessWithBody(LookupContext* lookup_context, + AssertionResult expectLookupSuccessWithBody(LookupContext* lookup_context, absl::string_view body) { if (lookup_result_.cache_entry_status != CacheEntryStatus::Ok) { return AssertionFailure() << "Expected: lookup_result_.cache_entry_status == " @@ -95,25 +95,25 @@ class SimpleHttpCacheTest : public testing::Test { // Simple flow of putting in an item, getting it, deleting it. TEST_F(SimpleHttpCacheTest, PutGet) { - const std::string kRequestPath1("Name"); - LookupContextPtr name_lookup_context = lookup(kRequestPath1); + const std::string RequestPath1("Name"); + LookupContextPtr name_lookup_context = lookup(RequestPath1); EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); Http::TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, {"cache-control", "public,max-age=3600"}}; - const std::string kBody1("Value"); - insert(move(name_lookup_context), response_headers, kBody1); - name_lookup_context = lookup(kRequestPath1); - EXPECT_TRUE(ExpectLookupSuccessWithBody(name_lookup_context.get(), kBody1)); + const std::string Body1("Value"); + insert(move(name_lookup_context), response_headers, Body1); + name_lookup_context = lookup(RequestPath1); + EXPECT_TRUE(expectLookupSuccessWithBody(name_lookup_context.get(), Body1)); - const std::string& kRequestPath2("Another Name"); - LookupContextPtr another_name_lookup_context = lookup(kRequestPath2); + const std::string& RequestPath2("Another Name"); + LookupContextPtr another_name_lookup_context = lookup(RequestPath2); EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); - const std::string kNewBody1("NewValue"); - insert(move(name_lookup_context), response_headers, kNewBody1); - EXPECT_TRUE(ExpectLookupSuccessWithBody(lookup(kRequestPath1).get(), kNewBody1)); + const std::string NewBody1("NewValue"); + insert(move(name_lookup_context), response_headers, NewBody1); + EXPECT_TRUE(expectLookupSuccessWithBody(lookup(RequestPath1).get(), NewBody1)); } TEST_F(SimpleHttpCacheTest, PrivateResponse) { @@ -125,12 +125,12 @@ TEST_F(SimpleHttpCacheTest, PrivateResponse) { LookupContextPtr name_lookup_context = lookup(request_path); EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); - const std::string kBody("Value"); + const std::string Body("Value"); // We must make sure at cache insertion time, private responses must not be // inserted. However, if the insertion did happen, it would be served at the // time of lookup. - insert(move(name_lookup_context), response_headers, kBody); - EXPECT_TRUE(ExpectLookupSuccessWithBody(lookup(request_path).get(), kBody)); + insert(move(name_lookup_context), response_headers, Body); + EXPECT_TRUE(expectLookupSuccessWithBody(lookup(request_path).get(), Body)); } TEST_F(SimpleHttpCacheTest, Miss) { @@ -167,9 +167,9 @@ TEST_F(SimpleHttpCacheTest, RequestSmallMinFresh) { Http::TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, {"age", "6000"}, {"cache-control", "public, max-age=9000"}}; - const std::string kBody("Value"); - insert(move(name_lookup_context), response_headers, kBody); - EXPECT_TRUE(ExpectLookupSuccessWithBody(lookup(request_path).get(), kBody)); + const std::string Body("Value"); + insert(move(name_lookup_context), response_headers, Body); + EXPECT_TRUE(expectLookupSuccessWithBody(lookup(request_path).get(), Body)); } TEST_F(SimpleHttpCacheTest, ResponseStaleWithRequestLargeMaxStale) { @@ -183,9 +183,9 @@ TEST_F(SimpleHttpCacheTest, ResponseStaleWithRequestLargeMaxStale) { {"age", "7200"}, {"cache-control", "public, max-age=3600"}}; - const std::string kBody("Value"); - insert(move(name_lookup_context), response_headers, kBody); - EXPECT_TRUE(ExpectLookupSuccessWithBody(lookup(request_path).get(), kBody)); + const std::string Body("Value"); + insert(move(name_lookup_context), response_headers, Body); + EXPECT_TRUE(expectLookupSuccessWithBody(lookup(request_path).get(), Body)); } TEST_F(SimpleHttpCacheTest, StreamingPut) { From d5edfaef68d5acf0294af6e492ece5a4a73361b5 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 26 Sep 2019 15:45:30 -0700 Subject: [PATCH 035/109] Format fix. Signed-off-by: Todd Greer --- test/extensions/filters/http/cache/http_cache_utils_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/extensions/filters/http/cache/http_cache_utils_test.cc b/test/extensions/filters/http/cache/http_cache_utils_test.cc index bbd9d2af404ea..69f3d72f55f0d 100644 --- a/test/extensions/filters/http/cache/http_cache_utils_test.cc +++ b/test/extensions/filters/http/cache/http_cache_utils_test.cc @@ -20,7 +20,7 @@ class HttpTimeTest : public testing::TestWithParam { Http::TestHeaderMapImpl response_headers_{{"date", GetParam()}}; }; -const char*const ok_times[] = { +const char* const ok_times[] = { "Sun, 06 Nov 1994 08:49:37 GMT", // IMF-fixdate "Sunday, 06-Nov-94 08:49:37 GMT", // obsolete RFC 850 format "Sun Nov 6 08:49:37 1994" // ANSI C's asctime() format From 705c573da5dcef0cf960fce8a436d996601534c2 Mon Sep 17 00:00:00 2001 From: Josiah Kiehl Date: Wed, 2 Oct 2019 10:19:38 -0400 Subject: [PATCH 036/109] Remove death tests and unnecessary suite classes Signed-off-by: Josiah Kiehl --- .../filters/http/cache/http_cache_test.cc | 80 +++---------------- 1 file changed, 9 insertions(+), 71 deletions(-) diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index 722d33e5d7632..c1e3e837c8aef 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -10,92 +10,48 @@ namespace Extensions { namespace HttpFilters { namespace Cache { -class RawByteRangeTest : public testing::Test {}; - - -using RawByteRangeDeathTest = RawByteRangeTest; - - -TEST_F(RawByteRangeTest, isSuffix) { +TEST(RawByteRangeTest, isSuffix) { auto r = RawByteRange(UINT64_MAX, 4); ASSERT_TRUE(r.isSuffix()); } -TEST_F(RawByteRangeTest, IsNotSuffix) { +TEST(RawByteRangeTest, IsNotSuffix) { auto r = RawByteRange(3, 4); ASSERT_FALSE(r.isSuffix()); } -TEST_F(RawByteRangeDeathTest, IllegalByteRange) { - ASSERT_DEATH(RawByteRange(5, 4), "Illegal byte range"); -} - -TEST_F(RawByteRangeDeathTest, IllegalByteRangeButIsSuffix) { - // no death here - RawByteRange(UINT64_MAX, 3); - ASSERT_TRUE(true); -} - -TEST_F(RawByteRangeTest, FirstBytePos) { +TEST(RawByteRangeTest, FirstBytePos) { auto r = RawByteRange(3, 4); ASSERT_EQ(3, r.firstBytePos()); } -TEST_F(RawByteRangeDeathTest, FirstBytePosIfSuffix) { - auto r = RawByteRange(UINT64_MAX, 4); - ASSERT_DEATH(r.firstBytePos(), "!isSuffix()"); -} - -TEST_F(RawByteRangeTest, LastBytePos) { +TEST(RawByteRangeTest, LastBytePos) { auto r = RawByteRange(3, 4); ASSERT_EQ(4, r.lastBytePos()); } -TEST_F(RawByteRangeDeathTest, LastBytePosIfSuffix) { - auto r = RawByteRange(UINT64_MAX, UINT64_MAX); - ASSERT_DEATH(r.lastBytePos(), "!isSuffix()"); -} - -TEST_F(RawByteRangeDeathTest, SuffixLengthOfNotSuffix) { - auto r = RawByteRange(3, 4); - ASSERT_DEATH(r.suffixLength(), "isSuffix()"); -} - -TEST_F(RawByteRangeTest, suffixLength) { +TEST(RawByteRangeTest, suffixLength) { auto r = RawByteRange(UINT64_MAX, 4); ASSERT_EQ(4, r.suffixLength()); } -class AdjustedByteRangeTest : public testing::Test {}; - -using AdjustedByteRangeDeathTest = AdjustedByteRangeTest; - -TEST_F(AdjustedByteRangeDeathTest, IllegalByteRange) { - ASSERT_DEATH(AdjustedByteRange(5, 4), "Illegal byte range."); -} - -TEST_F(AdjustedByteRangeTest, Length) { +TEST(AdjustedByteRangeTest, Length) { auto a = AdjustedByteRange(3, 6); ASSERT_EQ(3, a.length()); } -TEST_F(AdjustedByteRangeTest, TrimFront) { +TEST(AdjustedByteRangeTest, TrimFront) { auto a = AdjustedByteRange(3, 6); a.trimFront(2); ASSERT_EQ(5, a.firstBytePos()); } -TEST_F(AdjustedByteRangeDeathTest, TrimFrontTooMuch) { - auto a = AdjustedByteRange(3, 6); - ASSERT_DEATH(a.trimFront(5), "Attempt to trim too much from range."); -} - -TEST_F(AdjustedByteRangeTest, MaxLength) { +TEST(AdjustedByteRangeTest, MaxLength) { auto a = AdjustedByteRange(0, UINT64_MAX); ASSERT_EQ(UINT64_MAX, a.length()); } -TEST_F(AdjustedByteRangeTest, MaxTrim) { +TEST(AdjustedByteRangeTest, MaxTrim) { auto a = AdjustedByteRange(0, UINT64_MAX); a.trimFront(UINT64_MAX); ASSERT_EQ(UINT64_MAX, a.firstBytePos()); @@ -112,24 +68,6 @@ class LookupRequestTest : public testing::Test { const LookupRequest lookup_request_{request_headers_, current_time_}; }; -using LookupRequestDeathTest = LookupRequestTest; - -TEST_F(LookupRequestDeathTest, makeLookupRequestWithNoPath) { - auto headerMap = Http::TestHeaderMapImpl({{":scheme", "http"}, {":authority", "example.com"}}); - ASSERT_DEATH(LookupRequest(headerMap, current_time_), "malformed Http::HeaderMap with null Path"); -} - -TEST_F(LookupRequestDeathTest, makeLookupRequestWithNoScheme) { - auto headerMap = Http::TestHeaderMapImpl({{":path", "/"}, {":authority", "example.com"}}); - ASSERT_DEATH(LookupRequest(headerMap, current_time_), - "malformed Http::HeaderMap with null Scheme"); -} - -TEST_F(LookupRequestDeathTest, makeLookupRequestWithNoHost) { - auto headerMap = Http::TestHeaderMapImpl({{":path", "/"}, {":scheme", "http"}}); - ASSERT_DEATH(LookupRequest(headerMap, current_time_), "malformed Http::HeaderMap with null Host"); -} - TEST_F(LookupRequestTest, makeLookupResult) { Http::HeaderMapPtr response_headers = Http::makeHeaderMap( {{"date", formatter_.fromTime(current_time_)}, {"cache-control", "public, max-age=3600"}}); From 7444101a2b37b01c330673736e79ba9ee12906d5 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 27 Nov 2019 17:13:35 -0800 Subject: [PATCH 037/109] Add cache filter to HttpFilterNameValues. Signed-off-by: Todd Greer --- source/extensions/filters/http/well_known_names.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/extensions/filters/http/well_known_names.h b/source/extensions/filters/http/well_known_names.h index 82341e13739fc..84ca1247abf7c 100644 --- a/source/extensions/filters/http/well_known_names.h +++ b/source/extensions/filters/http/well_known_names.h @@ -14,6 +14,8 @@ class HttpFilterNameValues { public: // Buffer filter const std::string Buffer = "envoy.buffer"; + // Cache filter + const std::string Cache = "envoy.cache"; // CORS filter const std::string Cors = "envoy.cors"; // CSRF filter From db1d5288fba55401304bbc10279b5309ba2766b8 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 27 Nov 2019 17:18:08 -0800 Subject: [PATCH 038/109] Use HttpFilterNameValues::Cache in CacheFilterFactory Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/config.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/extensions/filters/http/cache/config.h b/source/extensions/filters/http/cache/config.h index 5b19f64a0da14..f273671587fff 100644 --- a/source/extensions/filters/http/cache/config.h +++ b/source/extensions/filters/http/cache/config.h @@ -5,6 +5,7 @@ #include "extensions/filters/http/cache/cache_filter.h" #include "extensions/filters/http/common/factory_base.h" +#include "extensions/filters/http/well_known_names.h" namespace Envoy { namespace Extensions { @@ -14,7 +15,7 @@ namespace Cache { class CacheFilterFactory : public Common::FactoryBase { public: - CacheFilterFactory() : FactoryBase("envoy.filters.http.cache") {} + CacheFilterFactory() : FactoryBase(HttpFilterNames::get().Cache) {} private: Http::FilterFactoryCb createFilterFactoryFromProtoTyped( From b72afdf36ee2f54d3686932a24b90cb211d2c47a Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 27 Nov 2019 17:20:32 -0800 Subject: [PATCH 039/109] Make CacheFilter Loggable. Signed-off-by: Todd Greer --- source/common/common/logger.h | 1 + source/extensions/filters/http/cache/BUILD | 2 ++ source/extensions/filters/http/cache/cache_filter.h | 2 ++ 3 files changed, 5 insertions(+) diff --git a/source/common/common/logger.h b/source/common/common/logger.h index 58f06b447f0c3..8c609e1ff7978 100644 --- a/source/common/common/logger.h +++ b/source/common/common/logger.h @@ -26,6 +26,7 @@ namespace Logger { FUNCTION(aws) \ FUNCTION(assert) \ FUNCTION(backtrace) \ + FUNCTION(cache_filter) \ FUNCTION(client) \ FUNCTION(config) \ FUNCTION(connection) \ diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index 09da335d8de6a..b6ed027cc504c 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -18,6 +18,7 @@ envoy_cc_library( deps = [ ":http_cache_lib", "//include/envoy/registry", + "//source/common/common:logger_lib", "//source/common/common:macros", "//source/common/http:header_map_lib", "//source/common/http:headers_lib", @@ -37,6 +38,7 @@ envoy_cc_library( "//include/envoy/common:time_interface", "//include/envoy/registry", "//include/envoy/stats:stats_interface", + "//source/extensions/filters/http:well_known_names", "//source/extensions/filters/http/common:factory_base_lib", ], ) diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index 2fce9c185f83a..956449f90eb52 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -5,6 +5,7 @@ #include #include +#include "common/common/logger.h" #include "envoy/config/filter/http/cache/v2alpha/cache.pb.h" #include "extensions/filters/http/cache/http_cache.h" @@ -21,6 +22,7 @@ namespace Cache { class CacheFilter; using CacheFilterSharedPtr = std::shared_ptr; class CacheFilter : public Http::PassThroughFilter, + public Logger::Loggable, public std::enable_shared_from_this { public: // Throws ProtoValidationException if no registered HttpCacheFactory for config.name. From 2697008771fd8c6a4cefb1b60b14f455a09988de Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 27 Nov 2019 17:23:41 -0800 Subject: [PATCH 040/109] Use HeaderMap::ForwardedProto instead of Scheme in cache key, because that's what Envoy provides. Signed-off-by: Todd Greer --- .../filters/http/cache/cache_filter.cc | 15 +++++++++++---- .../filters/http/cache/http_cache.cc | 12 ++++++------ .../filters/http/cache/http_cache.h | 2 +- .../filters/http/cache/cache_filter_test.cc | 19 +++++++++++++------ 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index a44c2d60b82d4..15fe22403bea6 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -15,14 +15,14 @@ namespace { bool isCacheableRequest(Http::HeaderMap& headers) { const Http::HeaderEntry* method = headers.Method(); - const Http::HeaderEntry* scheme = headers.Scheme(); + const Http::HeaderEntry* forwarded_proto = headers.ForwardedProto(); const Http::HeaderValues& header_values = Http::Headers::get(); // TODO(toddmgreer) Also serve HEAD requests from cache. // TODO(toddmgreer) Check all the other cache-related headers. - return method && scheme && headers.Path() && headers.Host() && + return method && forwarded_proto && headers.Path() && headers.Host() && (method->value() == Http::Headers::get().MethodValues.Get) && - (scheme->value() == header_values.SchemeValues.Http || - scheme->value() == header_values.SchemeValues.Https); + (forwarded_proto->value() == header_values.SchemeValues.Http || + forwarded_proto->value() == header_values.SchemeValues.Https); } bool isCacheableResponse(Http::HeaderMap& headers) { @@ -57,7 +57,9 @@ void CacheFilter::onDestroy() { } Http::FilterHeadersStatus CacheFilter::decodeHeaders(Http::HeaderMap& headers, bool) { + ENVOY_STREAM_LOG(debug, "CacheFilter::decodeHeaders: {}", *decoder_callbacks_, headers); if (!isCacheableRequest(headers)) { + ENVOY_STREAM_LOG(debug, "CacheFilter::decodeHeaders ignoring uncacheable request: {}", *decoder_callbacks_, headers); return Http::FilterHeadersStatus::Continue; } ASSERT(decoder_callbacks_); @@ -65,12 +67,14 @@ Http::FilterHeadersStatus CacheFilter::decodeHeaders(Http::HeaderMap& headers, b ASSERT(lookup_); CacheFilterSharedPtr self = shared_from_this(); + ENVOY_STREAM_LOG(debug, "CacheFilter::decodeHeaders starting lookup", *decoder_callbacks_); lookup_->getHeaders([self](LookupResult&& result) { onHeadersAsync(self, std::move(result)); }); return Http::FilterHeadersStatus::StopIteration; } Http::FilterHeadersStatus CacheFilter::encodeHeaders(Http::HeaderMap& headers, bool end_stream) { if (lookup_ && isCacheableResponse(headers)) { + ENVOY_STREAM_LOG(debug, "CacheFilter::encodeHeaders inserting headers", *encoder_callbacks_); insert_ = cache_.makeInsertContext(std::move(lookup_)); insert_->insertHeaders(headers, end_stream); } @@ -79,6 +83,7 @@ Http::FilterHeadersStatus CacheFilter::encodeHeaders(Http::HeaderMap& headers, b Http::FilterDataStatus CacheFilter::encodeData(Buffer::Instance& data, bool end_stream) { if (insert_) { + ENVOY_STREAM_LOG(debug, "CacheFilter::encodeHeaders inserting body", *encoder_callbacks_); // TODO(toddmgreer) Wait for the cache if necessary. insert_->insertBody( data, [](bool) {}, end_stream); @@ -94,6 +99,8 @@ void CacheFilter::onOkHeaders(Http::HeaderMapPtr&& headers, } response_has_trailers_ = has_trailers; const bool end_stream = (content_length == 0 && !response_has_trailers_); + // TODO(toddmgreer) Calculate age per https://httpwg.org/specs/rfc7234.html#age.calculations + headers->addReferenceKey(Http::Headers::get().Age, 0); decoder_callbacks_->encodeHeaders(std::move(headers), end_stream); if (end_stream) { return; diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index 0fbab656d57de..bd89e1abb3503 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -21,21 +21,21 @@ LookupRequest::LookupRequest(const Http::HeaderMap& request_headers, SystemTime : request_headers.CacheControl()->value().getStringView()) { ASSERT(request_headers.Path(), "Can't form cache lookup key for malformed Http::HeaderMap " "with null Path."); - ASSERT(request_headers.Scheme(), "Can't form cache lookup key for malformed Http::HeaderMap " - "with null Scheme."); + ASSERT(request_headers.ForwardedProto(), "Can't form cache lookup key for malformed Http::HeaderMap" + " with null ForwardedProto."); ASSERT(request_headers.Host(), "Can't form cache lookup key for malformed Http::HeaderMap " "with null Host."); - const Http::HeaderString& scheme = request_headers.Scheme()->value(); + const Http::HeaderString& forwarded_proto = request_headers.ForwardedProto()->value(); const auto& scheme_values = Http::Headers::get().SchemeValues; - ASSERT(scheme == scheme_values.Http || scheme == scheme_values.Https); - // TODO(toddmgreer) Let config determine whether to include scheme, host, and + ASSERT(forwarded_proto == scheme_values.Http || forwarded_proto == scheme_values.Https); + // TODO(toddmgreer) Let config determine whether to include forwarded_proto, host, and // query params. // TODO(toddmgreer) get cluster name. // TODO(toddmgreer) Parse Range header into request_range_spec_. key_.set_cluster_name("cluster_name_goes_here"); key_.set_host(std::string(request_headers.Host()->value().getStringView())); key_.set_path(std::string(request_headers.Path()->value().getStringView())); - key_.set_clear_http(scheme == scheme_values.Http); + key_.set_clear_http(forwarded_proto == scheme_values.Http); } // Unless this API is still alpha, calls to stableHashKey() must always return diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index a48d1469c985d..b767185d9d6d1 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -74,7 +74,7 @@ class AdjustedByteRange { } uint64_t firstBytePos() const { return first_byte_pos_; } uint64_t lastBytePos() const { return last_byte_pos_; } - uint64_t length() const { return last_byte_pos_ - first_byte_pos_; } + uint64_t length() const { return last_byte_pos_ - first_byte_pos_ + 1; } void trimFront(uint64_t n) { RELEASE_ASSERT(n < length(), "Attempt to trim too much from range."); first_byte_pos_ += n; diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index dbd657e5285f9..302583b7a0e51 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -17,21 +17,19 @@ class CacheFilterTest : public ::testing::Test { envoy::config::filter::http::cache::v2alpha::Cache config_; NiceMock context_; Event::SimulatedTimeSystem time_source_; - SystemTime current_time_ = time_source_.systemTime(); DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; Http::TestHeaderMapImpl request_headers_{{":authority", "host"}, {":path", "/"}, {":method", "GET"}, - {":scheme", "https"}, - {"Cache-Control", "max-age=3600"}}; - Http::TestHeaderMapImpl response_headers_{{"date", formatter_.fromTime(current_time_)}, + {"x-forwarded-proto", "https"}}; + Http::TestHeaderMapImpl response_headers_{{"date", formatter_.now(time_source_)}, {"cache-control", "public,max-age=3600"}}; NiceMock decoder_callbacks_; NiceMock encoder_callbacks_; CacheFilterSharedPtr makeFilter() { CacheFilterSharedPtr filter = - CacheFilter::make(config_, "", context_.scope(), context_.timeSource()); + CacheFilter::make(config_, /*stats_prefix=*/"", context_.scope(), context_.timeSource()); if (filter) { filter->setDecoderFilterCallbacks(decoder_callbacks_); filter->setEncoderFilterCallbacks(encoder_callbacks_); @@ -40,23 +38,32 @@ class CacheFilterTest : public ::testing::Test { } }; -TEST_F(CacheFilterTest, make) { +TEST_F(CacheFilterTest, ImmediateHit) { ON_CALL(decoder_callbacks_, dispatcher()).WillByDefault(ReturnRef(context_.dispatcher_)); ON_CALL(context_.dispatcher_, post(_)).WillByDefault(::testing::InvokeArgument<0>()); + // Create filter for request 1 config_.set_name("SimpleHttpCache"); CacheFilterSharedPtr filter = makeFilter(); ASSERT_TRUE(filter); + + // Decode request 1 header EXPECT_CALL(decoder_callbacks_, continueDecoding); EXPECT_EQ(filter->decodeHeaders(request_headers_, true), Http::FilterHeadersStatus::StopIteration); ::testing::Mock::VerifyAndClearExpectations(&decoder_callbacks_); + // Encode response header EXPECT_EQ(filter->encodeHeaders(response_headers_, true), Http::FilterHeadersStatus::Continue); filter->onDestroy(); + // Create filter for request 2 filter = makeFilter(); ASSERT_TRUE(filter); + + // Decode request 2 header + EXPECT_CALL(decoder_callbacks_, encodeHeaders_(testing::AllOf(IsSupersetOfHeaders(response_headers_), + HeaderHasValueRef("age", "0")), true)); EXPECT_EQ(filter->decodeHeaders(request_headers_, true), Http::FilterHeadersStatus::StopIteration); ::testing::Mock::VerifyAndClearExpectations(&decoder_callbacks_); From a30a7841a9fdf680f4275a6e6f67602451b62c3a Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 27 Nov 2019 17:27:58 -0800 Subject: [PATCH 041/109] Add CacheFilter integration test. Signed-off-by: Todd Greer --- test/extensions/filters/http/cache/BUILD | 15 ++++ .../cache/cache_filter_integration_test.cc | 74 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 test/extensions/filters/http/cache/cache_filter_integration_test.cc diff --git a/test/extensions/filters/http/cache/BUILD b/test/extensions/filters/http/cache/BUILD index 4645bf6f175a8..39fd7caff69bc 100644 --- a/test/extensions/filters/http/cache/BUILD +++ b/test/extensions/filters/http/cache/BUILD @@ -68,3 +68,18 @@ envoy_extension_cc_test( "//test/test_common:utility_lib", ], ) + +envoy_extension_cc_test( + name = "cache_filter_integration_test", + srcs = [ + "cache_filter_integration_test.cc", + ], + extension_name = "envoy.filters.http.cache", + deps = [ + "//source/extensions/filters/http/cache:config", + "//source/extensions/filters/http/cache:http_cache_lib", + "//source/extensions/filters/http/cache:simple_http_cache_lib", + "//test/integration:http_protocol_integration_lib", + "//test/test_common:simulated_time_system_lib", + ], +) diff --git a/test/extensions/filters/http/cache/cache_filter_integration_test.cc b/test/extensions/filters/http/cache/cache_filter_integration_test.cc new file mode 100644 index 0000000000000..ddf967f248764 --- /dev/null +++ b/test/extensions/filters/http/cache/cache_filter_integration_test.cc @@ -0,0 +1,74 @@ +#include "test/integration/http_protocol_integration.h" +#include "test/test_common/simulated_time_system.h" + +namespace Envoy { +namespace Extensions{ +namespace HttpFilters{ +namespace Cache { + +class CacheIntegrationTest : public Event::TestUsingSimulatedTime, public HttpProtocolIntegrationTest { + public: + void TearDown() override { + cleanupUpstreamAndDownstream(); + HttpProtocolIntegrationTest::TearDown(); + } + + void initializeFilter(const std::string& config) { + config_helper_.addFilter(config); + initialize(); + codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http")))); + } + + const std::string default_config{R"EOF( + name: envoy.cache + typed_config: + "@type": type.googleapis.com/envoy.config.filter.http.cache.v2alpha.Cache + name: SimpleHttpCache + )EOF"}; + DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; +}; + +INSTANTIATE_TEST_SUITE_P(Protocols, CacheIntegrationTest, + testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams()), + HttpProtocolIntegrationTest::protocolTestParamsToString); + +TEST_P(CacheIntegrationTest, MissInsertHit) { + // Set system time to cause Envoy's cached formatted time to match time on this thread. + simTime().setSystemTime(std::chrono::hours(1)); + initializeFilter(default_config); + + // Include test name and params in URL to make each test's requests unique. + const Http::TestHeaderMapImpl request_headers = {{":method", "GET"}, + {":path", absl::StrCat("/",protocolTestParamsToString({GetParam(),0}))}, + {":scheme", "http"}, + {":authority", "MissInsertHit"}}; + Http::TestHeaderMapImpl response_headers = {{":status", "200"}, + {"date", formatter_.now(simTime())}, + {"cache-control", "public,max-age=3600"}, + {"content-length", "42"}}; + + // Send first request, and get response from upstream. + { + IntegrationStreamDecoderPtr request = codec_client_->makeHeaderOnlyRequest(request_headers); + waitForNextUpstreamRequest(); + upstream_request_->encodeHeaders(response_headers, /*end_stream=*/ false); + // send 42 'a's + upstream_request_->encodeData(42, true); + // Wait for the response to be read by the codec client. + request->waitForEndStream(); + EXPECT_TRUE(request->complete()); + EXPECT_THAT(request->headers(), IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(request->headers().Age(), nullptr); + EXPECT_EQ(request->body(), std::string(42, 'a')); + } + + // Send second resquest, and get response from cache. + IntegrationStreamDecoderPtr request = codec_client_->makeHeaderOnlyRequest(request_headers); + request->waitForEndStream(); + EXPECT_TRUE(request->complete()); + EXPECT_THAT(request->headers(), IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(request->body(), std::string(42, 'a')); + EXPECT_NE(request->headers().Age(), nullptr); +} +} // namespace Envoy +}}} From c8dafa04caeb2575901810e664e1471f8f6f610d Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 27 Nov 2019 17:34:05 -0800 Subject: [PATCH 042/109] Fix formatting Signed-off-by: Todd Greer --- .../filters/http/cache/cache_filter.cc | 3 ++- .../filters/http/cache/cache_filter.h | 3 ++- .../filters/http/cache/http_cache.cc | 4 ++-- .../cache/cache_filter_integration_test.cc | 24 +++++++++++-------- .../filters/http/cache/cache_filter_test.cc | 12 +++++----- 5 files changed, 26 insertions(+), 20 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 15fe22403bea6..b462b4bf8d800 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -59,7 +59,8 @@ void CacheFilter::onDestroy() { Http::FilterHeadersStatus CacheFilter::decodeHeaders(Http::HeaderMap& headers, bool) { ENVOY_STREAM_LOG(debug, "CacheFilter::decodeHeaders: {}", *decoder_callbacks_, headers); if (!isCacheableRequest(headers)) { - ENVOY_STREAM_LOG(debug, "CacheFilter::decodeHeaders ignoring uncacheable request: {}", *decoder_callbacks_, headers); + ENVOY_STREAM_LOG(debug, "CacheFilter::decodeHeaders ignoring uncacheable request: {}", + *decoder_callbacks_, headers); return Http::FilterHeadersStatus::Continue; } ASSERT(decoder_callbacks_); diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index 956449f90eb52..3417fb3cba73e 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -5,9 +5,10 @@ #include #include -#include "common/common/logger.h" #include "envoy/config/filter/http/cache/v2alpha/cache.pb.h" +#include "common/common/logger.h" + #include "extensions/filters/http/cache/http_cache.h" #include "extensions/filters/http/common/pass_through_filter.h" diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index bd89e1abb3503..aae79cb4327de 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -21,8 +21,8 @@ LookupRequest::LookupRequest(const Http::HeaderMap& request_headers, SystemTime : request_headers.CacheControl()->value().getStringView()) { ASSERT(request_headers.Path(), "Can't form cache lookup key for malformed Http::HeaderMap " "with null Path."); - ASSERT(request_headers.ForwardedProto(), "Can't form cache lookup key for malformed Http::HeaderMap" - " with null ForwardedProto."); + ASSERT(request_headers.ForwardedProto(), + "Can't form cache lookup key for malformed Http::HeaderMap with null ForwardedProto."); ASSERT(request_headers.Host(), "Can't form cache lookup key for malformed Http::HeaderMap " "with null Host."); const Http::HeaderString& forwarded_proto = request_headers.ForwardedProto()->value(); diff --git a/test/extensions/filters/http/cache/cache_filter_integration_test.cc b/test/extensions/filters/http/cache/cache_filter_integration_test.cc index ddf967f248764..0be22af1ead6b 100644 --- a/test/extensions/filters/http/cache/cache_filter_integration_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_integration_test.cc @@ -2,12 +2,13 @@ #include "test/test_common/simulated_time_system.h" namespace Envoy { -namespace Extensions{ -namespace HttpFilters{ +namespace Extensions { +namespace HttpFilters { namespace Cache { -class CacheIntegrationTest : public Event::TestUsingSimulatedTime, public HttpProtocolIntegrationTest { - public: +class CacheIntegrationTest : public Event::TestUsingSimulatedTime, + public HttpProtocolIntegrationTest { +public: void TearDown() override { cleanupUpstreamAndDownstream(); HttpProtocolIntegrationTest::TearDown(); @@ -38,10 +39,11 @@ TEST_P(CacheIntegrationTest, MissInsertHit) { initializeFilter(default_config); // Include test name and params in URL to make each test's requests unique. - const Http::TestHeaderMapImpl request_headers = {{":method", "GET"}, - {":path", absl::StrCat("/",protocolTestParamsToString({GetParam(),0}))}, - {":scheme", "http"}, - {":authority", "MissInsertHit"}}; + const Http::TestHeaderMapImpl request_headers = { + {":method", "GET"}, + {":path", absl::StrCat("/", protocolTestParamsToString({GetParam(), 0}))}, + {":scheme", "http"}, + {":authority", "MissInsertHit"}}; Http::TestHeaderMapImpl response_headers = {{":status", "200"}, {"date", formatter_.now(simTime())}, {"cache-control", "public,max-age=3600"}, @@ -51,7 +53,7 @@ TEST_P(CacheIntegrationTest, MissInsertHit) { { IntegrationStreamDecoderPtr request = codec_client_->makeHeaderOnlyRequest(request_headers); waitForNextUpstreamRequest(); - upstream_request_->encodeHeaders(response_headers, /*end_stream=*/ false); + upstream_request_->encodeHeaders(response_headers, /*end_stream=*/false); // send 42 'a's upstream_request_->encodeData(42, true); // Wait for the response to be read by the codec client. @@ -70,5 +72,7 @@ TEST_P(CacheIntegrationTest, MissInsertHit) { EXPECT_EQ(request->body(), std::string(42, 'a')); EXPECT_NE(request->headers().Age(), nullptr); } +} // namespace Cache +} // namespace HttpFilters +} // namespace Extensions } // namespace Envoy -}}} diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index 302583b7a0e51..4f14e4b48333e 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -18,10 +18,8 @@ class CacheFilterTest : public ::testing::Test { NiceMock context_; Event::SimulatedTimeSystem time_source_; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; - Http::TestHeaderMapImpl request_headers_{{":authority", "host"}, - {":path", "/"}, - {":method", "GET"}, - {"x-forwarded-proto", "https"}}; + Http::TestHeaderMapImpl request_headers_{ + {":authority", "host"}, {":path", "/"}, {":method", "GET"}, {"x-forwarded-proto", "https"}}; Http::TestHeaderMapImpl response_headers_{{"date", formatter_.now(time_source_)}, {"cache-control", "public,max-age=3600"}}; NiceMock decoder_callbacks_; @@ -62,8 +60,10 @@ TEST_F(CacheFilterTest, ImmediateHit) { ASSERT_TRUE(filter); // Decode request 2 header - EXPECT_CALL(decoder_callbacks_, encodeHeaders_(testing::AllOf(IsSupersetOfHeaders(response_headers_), - HeaderHasValueRef("age", "0")), true)); + EXPECT_CALL(decoder_callbacks_, + encodeHeaders_(testing::AllOf(IsSupersetOfHeaders(response_headers_), + HeaderHasValueRef("age", "0")), + true)); EXPECT_EQ(filter->decodeHeaders(request_headers_, true), Http::FilterHeadersStatus::StopIteration); ::testing::Mock::VerifyAndClearExpectations(&decoder_callbacks_); From 03e1908018929abfa4474655afb23317615de7fd Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 27 Nov 2019 18:18:29 -0800 Subject: [PATCH 043/109] buildozer and spelling fixes Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/BUILD | 2 ++ source/extensions/filters/http/cache/http_cache.h | 2 +- source/extensions/filters/http/cache/http_cache_utils.cc | 2 +- .../filters/http/cache/cache_filter_integration_test.cc | 2 +- tools/spelling_dictionary.txt | 5 +++++ 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index b6ed027cc504c..9e685ed3d1c5f 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -25,6 +25,7 @@ envoy_cc_library( "//source/common/protobuf", "//source/extensions/filters/http/common:pass_through_filter_lib", "@envoy_api//envoy/config/filter/http/cache/v2alpha:cache_cc", + "@envoy_api//envoy/config/filter/http/cache/v2alpha:pkg_cc_proto", ], ) @@ -40,6 +41,7 @@ envoy_cc_library( "//include/envoy/stats:stats_interface", "//source/extensions/filters/http:well_known_names", "//source/extensions/filters/http/common:factory_base_lib", + "@envoy_api//envoy/config/filter/http/cache/v2alpha:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index ee4917a4f86d5..737f27764b7ff 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -137,7 +137,7 @@ class LookupRequest { public: using HeaderVector = std::vector; - // Prereq: request_headers's Path(), Scheme(), and Host() are nonnull. + // Prereq: request_headers's Path(), Scheme(), and Host() are non-null. LookupRequest(const Http::HeaderMap& request_headers, SystemTime timestamp); // Caches may modify the key according to local needs, though care must be diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index 243b41b57abde..be4072823331f 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -82,7 +82,7 @@ void eatDirectiveArgument(absl::string_view& s) { } } // namespace -// If s is nonnull and begins with a decimal number ([0-9]+), removes it from +// If s is non-null and begins with a decimal number ([0-9]+), removes it from // the input and returns a SystemTime::duration representing that many seconds. // If s is null or doesn't begin with digits, returns // SystemTime::duration::zero(). If parsing overflows, returns diff --git a/test/extensions/filters/http/cache/cache_filter_integration_test.cc b/test/extensions/filters/http/cache/cache_filter_integration_test.cc index 0be22af1ead6b..e3c9c522ef2cf 100644 --- a/test/extensions/filters/http/cache/cache_filter_integration_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_integration_test.cc @@ -64,7 +64,7 @@ TEST_P(CacheIntegrationTest, MissInsertHit) { EXPECT_EQ(request->body(), std::string(42, 'a')); } - // Send second resquest, and get response from cache. + // Send second request, and get response from cache. IntegrationStreamDecoderPtr request = codec_client_->makeHeaderOnlyRequest(request_headers); request->waitForEndStream(); EXPECT_TRUE(request->complete()); diff --git a/tools/spelling_dictionary.txt b/tools/spelling_dictionary.txt index 9848889a39f22..85665be025cb7 100644 --- a/tools/spelling_dictionary.txt +++ b/tools/spelling_dictionary.txt @@ -82,6 +82,7 @@ EWOULDBLOCK EXPECTing EXPECTs EXPR +FALLTHROUGH FAQ FASTOPEN FCDS @@ -200,6 +201,7 @@ PostCBs PREBIND PRNG PROT +Prereq QUIC QoS RAII @@ -304,8 +306,11 @@ WS Welford's XDS asctime +cacheability dechunk dechunked +fixdate +maxage qdtext satisfiable tchar From 8d4496280342159c6a8a4f8a5c5d885f4eb51494 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 27 Nov 2019 19:22:00 -0800 Subject: [PATCH 044/109] Adapted to upstream changes: v2alpha -> v3alpha new magic for proto deps factories now require category() Signed-off-by: Todd Greer --- api/envoy/config/filter/http/cache/v2alpha/BUILD | 10 ---------- api/envoy/config/filter/http/cache/v3alpha/BUILD | 7 +++++++ .../filter/http/cache/{v2alpha => v3alpha}/cache.proto | 2 +- source/extensions/filters/http/cache/BUILD | 9 ++++----- source/extensions/filters/http/cache/cache_filter.cc | 4 ++-- source/extensions/filters/http/cache/cache_filter.h | 6 +++--- source/extensions/filters/http/cache/config.cc | 4 ++-- source/extensions/filters/http/cache/config.h | 8 ++++---- source/extensions/filters/http/cache/http_cache.h | 1 + .../http/cache/cache_filter_integration_test.cc | 2 +- .../extensions/filters/http/cache/cache_filter_test.cc | 2 +- test/extensions/filters/http/cache/config_test.cc | 2 +- 12 files changed, 27 insertions(+), 30 deletions(-) delete mode 100644 api/envoy/config/filter/http/cache/v2alpha/BUILD create mode 100644 api/envoy/config/filter/http/cache/v3alpha/BUILD rename api/envoy/config/filter/http/cache/{v2alpha => v3alpha}/cache.proto (97%) diff --git a/api/envoy/config/filter/http/cache/v2alpha/BUILD b/api/envoy/config/filter/http/cache/v2alpha/BUILD deleted file mode 100644 index ba476d88ebae6..0000000000000 --- a/api/envoy/config/filter/http/cache/v2alpha/BUILD +++ /dev/null @@ -1,10 +0,0 @@ -load("@envoy_api//bazel:api_build_system.bzl", "api_proto_library_internal", "api_proto_package") - -licenses(["notice"]) # Apache 2 - -api_proto_package() - -api_proto_library_internal( - name = "cache", - srcs = ["cache.proto"], -) diff --git a/api/envoy/config/filter/http/cache/v3alpha/BUILD b/api/envoy/config/filter/http/cache/v3alpha/BUILD new file mode 100644 index 0000000000000..5dc095ade27a9 --- /dev/null +++ b/api/envoy/config/filter/http/cache/v3alpha/BUILD @@ -0,0 +1,7 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package() diff --git a/api/envoy/config/filter/http/cache/v2alpha/cache.proto b/api/envoy/config/filter/http/cache/v3alpha/cache.proto similarity index 97% rename from api/envoy/config/filter/http/cache/v2alpha/cache.proto rename to api/envoy/config/filter/http/cache/v3alpha/cache.proto index 85d3d2374d97e..bc41ad04f6e98 100644 --- a/api/envoy/config/filter/http/cache/v2alpha/cache.proto +++ b/api/envoy/config/filter/http/cache/v3alpha/cache.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package envoy.config.filter.http.cache.v2alpha; +package envoy.config.filter.http.cache.v3alpha; option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2alpha"; option java_outer_classname = "CacheProto"; diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index 9e685ed3d1c5f..16acc3b4697b6 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -24,8 +24,7 @@ envoy_cc_library( "//source/common/http:headers_lib", "//source/common/protobuf", "//source/extensions/filters/http/common:pass_through_filter_lib", - "@envoy_api//envoy/config/filter/http/cache/v2alpha:cache_cc", - "@envoy_api//envoy/config/filter/http/cache/v2alpha:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/cache/v3alpha:pkg_cc_proto", ], ) @@ -35,13 +34,13 @@ envoy_cc_library( hdrs = ["config.h"], deps = [ ":cache_filter_lib", - ":key_cc", + ":key_cc_proto", "//include/envoy/common:time_interface", "//include/envoy/registry", "//include/envoy/stats:stats_interface", "//source/extensions/filters/http:well_known_names", "//source/extensions/filters/http/common:factory_base_lib", - "@envoy_api//envoy/config/filter/http/cache/v2alpha:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/cache/v3alpha:pkg_cc_proto", ], ) @@ -56,7 +55,7 @@ envoy_cc_library( hdrs = ["http_cache.h"], deps = [ ":http_cache_utils_lib", - ":key_cc", + ":key_cc_proto", "//include/envoy/buffer:buffer_interface", "//include/envoy/common:time_interface", "//include/envoy/http:header_map_interface", diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index b462b4bf8d800..99055ed0c72bf 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -36,7 +36,7 @@ bool isCacheableResponse(Http::HeaderMap& headers) { return false; } -HttpCache& getCache(const envoy::config::filter::http::cache::v2alpha::Cache& config) { +HttpCache& getCache(const envoy::config::filter::http::cache::v3alpha::Cache& config) { HttpCacheFactory* factory = Registry::FactoryRegistry::getFactory(config.name()); if (!factory) { @@ -47,7 +47,7 @@ HttpCache& getCache(const envoy::config::filter::http::cache::v2alpha::Cache& co } } // namespace -CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v2alpha::Cache& config, +CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v3alpha::Cache& config, const std::string&, Stats::Scope&, TimeSource& time_source) : time_source_(time_source), cache_(getCache(config)) {} diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index 3417fb3cba73e..1f2febd060a15 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -5,7 +5,7 @@ #include #include -#include "envoy/config/filter/http/cache/v2alpha/cache.pb.h" +#include "envoy/config/filter/http/cache/v3alpha/cache.pb.h" #include "common/common/logger.h" @@ -27,7 +27,7 @@ class CacheFilter : public Http::PassThroughFilter, public std::enable_shared_from_this { public: // Throws ProtoValidationException if no registered HttpCacheFactory for config.name. - static CacheFilterSharedPtr make(const envoy::config::filter::http::cache::v2alpha::Cache& config, + static CacheFilterSharedPtr make(const envoy::config::filter::http::cache::v3alpha::Cache& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source) { return std::shared_ptr(new CacheFilter(config, stats_prefix, scope, time_source)); @@ -44,7 +44,7 @@ class CacheFilter : public Http::PassThroughFilter, // Throws EnvoyException if no registered HttpCacheFactory for config.name. // Constructor is private to enforce enable_shared_from_this's requirement that this must be owned // by a shared_ptr. - CacheFilter(const envoy::config::filter::http::cache::v2alpha::Cache& config, + CacheFilter(const envoy::config::filter::http::cache::v3alpha::Cache& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source); void getBody(); diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index c4189f06735ff..9715ed2804f6c 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -3,7 +3,7 @@ #include #include "envoy/common/time.h" -#include "envoy/config/filter/http/cache/v2alpha/cache.pb.validate.h" +#include "envoy/config/filter/http/cache/v3alpha/cache.pb.validate.h" #include "envoy/registry/registry.h" #include "envoy/stats/scope.h" @@ -15,7 +15,7 @@ namespace HttpFilters { namespace Cache { Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v2alpha::Cache& config, + const envoy::config::filter::http::cache::v3alpha::Cache& config, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { return [config, stats_prefix, &context](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter( diff --git a/source/extensions/filters/http/cache/config.h b/source/extensions/filters/http/cache/config.h index f273671587fff..8b644af1cd5e5 100644 --- a/source/extensions/filters/http/cache/config.h +++ b/source/extensions/filters/http/cache/config.h @@ -1,7 +1,7 @@ #pragma once -#include "envoy/config/filter/http/cache/v2alpha/cache.pb.h" -#include "envoy/config/filter/http/cache/v2alpha/cache.pb.validate.h" +#include "envoy/config/filter/http/cache/v3alpha/cache.pb.h" +#include "envoy/config/filter/http/cache/v3alpha/cache.pb.validate.h" #include "extensions/filters/http/cache/cache_filter.h" #include "extensions/filters/http/common/factory_base.h" @@ -13,13 +13,13 @@ namespace HttpFilters { namespace Cache { class CacheFilterFactory - : public Common::FactoryBase { + : public Common::FactoryBase { public: CacheFilterFactory() : FactoryBase(HttpFilterNames::get().Cache) {} private: Http::FilterFactoryCb createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v2alpha::Cache& config, + const envoy::config::filter::http::cache::v3alpha::Cache& config, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; }; diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 737f27764b7ff..8867e8ebbb734 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -283,6 +283,7 @@ class HttpCacheFactory { public: explicit HttpCacheFactory(std::string name) : name_(std::move(name)) {} const std::string& name() const { return name_; } + static std::string category() { return "http_cache_factory"; } // Returns an HttpCache that will remain valid indefinitely (at least as long // as the calling CacheFilter). diff --git a/test/extensions/filters/http/cache/cache_filter_integration_test.cc b/test/extensions/filters/http/cache/cache_filter_integration_test.cc index e3c9c522ef2cf..b36e1c7cbe79c 100644 --- a/test/extensions/filters/http/cache/cache_filter_integration_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_integration_test.cc @@ -23,7 +23,7 @@ class CacheIntegrationTest : public Event::TestUsingSimulatedTime, const std::string default_config{R"EOF( name: envoy.cache typed_config: - "@type": type.googleapis.com/envoy.config.filter.http.cache.v2alpha.Cache + "@type": type.googleapis.com/envoy.config.filter.http.cache.v3alpha.Cache name: SimpleHttpCache )EOF"}; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index 4f14e4b48333e..8459964698f99 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -14,7 +14,7 @@ namespace { class CacheFilterTest : public ::testing::Test { protected: - envoy::config::filter::http::cache::v2alpha::Cache config_; + envoy::config::filter::http::cache::v3alpha::Cache config_; NiceMock context_; Event::SimulatedTimeSystem time_source_; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; diff --git a/test/extensions/filters/http/cache/config_test.cc b/test/extensions/filters/http/cache/config_test.cc index f8f6f685fd02d..1b9c16b5fbfa9 100644 --- a/test/extensions/filters/http/cache/config_test.cc +++ b/test/extensions/filters/http/cache/config_test.cc @@ -14,7 +14,7 @@ namespace { class CacheFilterFactoryTest : public ::testing::Test { protected: - envoy::config::filter::http::cache::v2alpha::Cache config_; + envoy::config::filter::http::cache::v3alpha::Cache config_; NiceMock context_; CacheFilterFactory factory_; Http::MockFilterChainFactoryCallbacks filter_callback_; From 4d3d57e06612a5dfe861b7b7172e5e7b8e2c4ee0 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 27 Nov 2019 19:40:22 -0800 Subject: [PATCH 045/109] Minor cleanups Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/cache_filter.cc | 2 +- source/extensions/filters/http/cache/http_cache.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 99055ed0c72bf..a254e9b835211 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -20,7 +20,7 @@ bool isCacheableRequest(Http::HeaderMap& headers) { // TODO(toddmgreer) Also serve HEAD requests from cache. // TODO(toddmgreer) Check all the other cache-related headers. return method && forwarded_proto && headers.Path() && headers.Host() && - (method->value() == Http::Headers::get().MethodValues.Get) && + (method->value() == header_values.MethodValues.Get) && (forwarded_proto->value() == header_values.SchemeValues.Http || forwarded_proto->value() == header_values.SchemeValues.Https); } diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index aae79cb4327de..c59a4b77f9361 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -54,7 +54,7 @@ bool LookupRequest::isFresh(const Http::HeaderMap& response_headers) const { } // We didn't find a cache-control header with enough info to determine // freshness, so fall back to the expires header. - return timestamp_ <= Internal::httpTime(response_headers.get(Http::LowerCaseString("expires"))); + return timestamp_ <= Internal::httpTime(response_headers.Expires()); } LookupResult LookupRequest::makeLookupResult(Http::HeaderMapPtr&& response_headers, From 84a8532ad344bf7d25fb594fa24a4ebf3ac93ea6 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 10 Dec 2019 18:44:55 -0800 Subject: [PATCH 046/109] Change AdjustByteRange to use C++-style half-open intervals instead of HTTP-style closed intervals, plus misc cleanups/fixes. Signed-off-by: Todd Greer --- .../filters/http/cache/cache_filter.cc | 2 +- .../filters/http/cache/http_cache.cc | 39 +++--- .../filters/http/cache/http_cache.h | 49 ++++--- .../filters/http/cache/simple_http_cache.cc | 5 +- .../network/common/redis/codec_impl.cc | 4 +- test/extensions/filters/http/cache/BUILD | 1 + .../filters/http/cache/cache_filter_test.cc | 47 ++++++- .../filters/http/cache/http_cache_test.cc | 126 +++++++++++++----- .../http/cache/simple_http_cache_test.cc | 6 +- 9 files changed, 194 insertions(+), 85 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index a254e9b835211..ce4b516e278d1 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -111,7 +111,7 @@ void CacheFilter::onOkHeaders(Http::HeaderMapPtr&& headers, // TODO(toddmgreer) handle multi-range requests. ASSERT(remaining_body_.size() <= 1); if (remaining_body_.empty()) { - remaining_body_.emplace_back(0, content_length - 1); + remaining_body_.emplace_back(0, content_length); } getBody(); } else { diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index c59a4b77f9361..a0abc6bb57e94 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -46,6 +46,9 @@ size_t localHashKey(const Key& key) { return stableHashKey(key); } // Returns true if response_headers is fresh. bool LookupRequest::isFresh(const Http::HeaderMap& response_headers) const { + if (!response_headers.Date()) { + return false; + } const Http::HeaderEntry* cache_control_header = response_headers.CacheControl(); if (cache_control_header) { const SystemTime::duration effective_max_age = @@ -62,28 +65,21 @@ LookupResult LookupRequest::makeLookupResult(Http::HeaderMapPtr&& response_heade // TODO(toddmgreer) Implement all HTTP caching semantics. ASSERT(response_headers); LookupResult result; - if (!isFresh(*response_headers)) { - result.cache_entry_status = CacheEntryStatus::RequiresValidation; - return result; - } - + result.cache_entry_status = + isFresh(*response_headers) ? CacheEntryStatus::Ok : CacheEntryStatus::RequiresValidation; result.headers = std::move(response_headers); result.content_length = content_length; - if (adjustByteRangeSet(result.response_ranges, content_length)) { - result.cache_entry_status = CacheEntryStatus::Ok; - } else { - result.cache_entry_status = CacheEntryStatus::UnsatisfiableRange; + if (!adjustByteRangeSet(result.response_ranges, request_range_spec_, content_length)) { + result.headers->insertStatus().value(416); // Range Not Satisfiable } + result.has_trailers = false; return result; } -// Adjusts response_ranges to fit a cached response of size content_length. -// Returns true if response_ranges is satisfiable (empty is considered -// satisfiable, as it denotes the entire body). -// TODO(toddmgreer) Merge/reorder ranges where appropriate. -bool LookupRequest::adjustByteRangeSet(std::vector& response_ranges, - uint64_t content_length) const { - if (request_range_spec_.empty()) { +bool adjustByteRangeSet(std::vector& response_ranges, + const std::vector& request_range_spec, + uint64_t content_length) { + if (request_range_spec.empty()) { // No range header, so the request can proceed. return true; } @@ -93,7 +89,7 @@ bool LookupRequest::adjustByteRangeSet(std::vector& response_ return false; } - for (const RawByteRange& spec : request_range_spec_) { + for (const RawByteRange& spec : request_range_spec) { if (spec.isSuffix()) { // spec is a suffix-byte-range-spec if (spec.suffixLength() >= content_length) { @@ -102,15 +98,18 @@ bool LookupRequest::adjustByteRangeSet(std::vector& response_ response_ranges.clear(); return true; } - response_ranges.emplace_back(content_length - spec.suffixLength(), content_length - 1); + response_ranges.emplace_back(content_length - spec.suffixLength(), content_length); } else { // spec is a byte-range-spec if (spec.firstBytePos() >= content_length) { // This range is unsatisfiable, so skip it. continue; } - response_ranges.emplace_back(spec.firstBytePos(), - std::min(spec.lastBytePos(), content_length - 1)); + if (spec.lastBytePos() >= content_length - 1) { + response_ranges.emplace_back(spec.firstBytePos(), content_length); + } else { + response_ranges.emplace_back(spec.firstBytePos(), spec.lastBytePos()); + } } } if (response_ranges.empty()) { diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 8867e8ebbb734..68e9e0669eebd 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -36,11 +36,12 @@ enum CacheEntryStatus { // Byte range from an HTTP request. class RawByteRange { public: - // - If first==UINT64_MAX, construct a RawByteRange requesting the final last - // body bytes. - // - Otherwise, construct a RawByteRange requesting the [first,last] body - // bytes. Prereq: first == UINT64_MAX || first <= last Invariant: isSuffix() || - // firstBytePos() <= lastBytePos + // - If first==UINT64_MAX, construct a RawByteRange requesting the final last body bytes. + // - Otherwise, construct a RawByteRange requesting the [first,last] body bytes. + // Prereq: first == UINT64_MAX || first <= last + // Invariant: isSuffix() || firstBytePos() <= lastBytePos + // Examples: RawByteRange(0,4) requests the first 5 bytes. + // RawByteRange(UINT64_MAX,4) requests the last 4 bytes. RawByteRange(uint64_t first, uint64_t last) : first_byte_pos_(first), last_byte_pos_(last) { RELEASE_ASSERT(isSuffix() || first <= last, "Illegal byte range."); } @@ -63,28 +64,38 @@ class RawByteRange { uint64_t last_byte_pos_; }; -// Byte range from an HTTP request, adjusted for a known response body size. +// Byte range from an HTTP request, adjusted for a known response body size, and converted from an +// HTTP-style closed interval to a C++ style half-open interval. class AdjustedByteRange { public: - // Construct an AdjustedByteRange representing the [first,last] bytes in the - // response body. Prereq: first <= last Invariant: firstBytePos() <= - // lastBytePos() - AdjustedByteRange(uint64_t first, uint64_t last) : first_byte_pos_(first), last_byte_pos_(last) { - ASSERT(first <= last, "Illegal byte range."); + // Construct an AdjustedByteRange representing the [first,last) bytes in the + // response body. Prereq: first <= last Invariant: begin() <= end() + // Example: AdjustedByteRange(0,4) represents the first 4 bytes. + AdjustedByteRange(uint64_t first, uint64_t last) : first_(first), last_(last) { + ASSERT(first < last, "Illegal byte range."); } - uint64_t firstBytePos() const { return first_byte_pos_; } - uint64_t lastBytePos() const { return last_byte_pos_; } - uint64_t length() const { return last_byte_pos_ - first_byte_pos_ + 1; } + uint64_t begin() const { return first_; } + // Unlike RawByteRange, end() is one past the index of the last offset. + uint64_t end() const { return last_; } + uint64_t length() const { return last_ - first_; } void trimFront(uint64_t n) { RELEASE_ASSERT(n <= length(), "Attempt to trim too much from range."); - first_byte_pos_ += n; + first_ += n; } private: - uint64_t first_byte_pos_; - uint64_t last_byte_pos_; + uint64_t first_; + uint64_t last_; }; +// Adjusts request_range_spec to fit a cached response of size content_length, putting the results +// in response_ranges. Returns true if response_ranges is satisfiable (empty is considered +// satisfiable, as it denotes the entire body). +// TODO(toddmgreer) Merge/reorder ranges where appropriate. +bool adjustByteRangeSet(std::vector& response_ranges, + const std::vector& request_range_spec, + uint64_t content_length); + // Result of a lookup operation, including cached headers and information needed // to serve a response based on it, or to attempt to validate. struct LookupResult { @@ -168,8 +179,6 @@ class LookupRequest { private: bool isFresh(const Http::HeaderMap& response_headers) const; - bool adjustByteRangeSet(std::vector& response_ranges, - uint64_t content_length) const; Key key_; std::vector request_range_spec_; @@ -234,7 +243,7 @@ class LookupContext { // // If a cache happens to load data in chunks of a set size, it may be // efficient to respond with fewer than the requested number of bytes. For - // example, assuming a 24 byte full-bodied response from a cache that reads in + // example, assuming a 23 byte full-bodied response from a cache that reads in // absurdly small 10 byte chunks: // // getBody requests bytes 0-23 .......... callback with bytes 0-9 diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index 9d71079e86a74..fec49afbcea98 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -26,9 +26,8 @@ class SimpleLookupContext : public LookupContext { } void getBody(const AdjustedByteRange& range, LookupBodyCallback&& cb) override { - RELEASE_ASSERT(range.lastBytePos() < body_.length(), "Attempt to read past end of body."); - cb(std::make_unique(&body_[range.firstBytePos()], - range.lastBytePos() - range.firstBytePos() + 1)); + RELEASE_ASSERT(range.end() <= body_.length(), "Attempt to read past end of body."); + cb(std::make_unique(&body_[range.begin()], range.length())); } void getTrailers(LookupTrailersCallback&& cb) override { diff --git a/source/extensions/filters/network/common/redis/codec_impl.cc b/source/extensions/filters/network/common/redis/codec_impl.cc index 6b62e228bfa60..c7ebc51efa4b2 100644 --- a/source/extensions/filters/network/common/redis/codec_impl.cc +++ b/source/extensions/filters/network/common/redis/codec_impl.cc @@ -315,8 +315,8 @@ RespValue::CompositeArray::CompositeArrayConstIterator::operator++() { return *this; } -bool RespValue::CompositeArray::CompositeArrayConstIterator::operator!=( - const CompositeArrayConstIterator& rhs) const { +bool RespValue::CompositeArray::CompositeArrayConstIterator:: +operator!=(const CompositeArrayConstIterator& rhs) const { return command_ != (rhs.command_) || &array_ != &(rhs.array_) || index_ != rhs.index_ || first_ != rhs.first_; } diff --git a/test/extensions/filters/http/cache/BUILD b/test/extensions/filters/http/cache/BUILD index 39fd7caff69bc..a411835ace77e 100644 --- a/test/extensions/filters/http/cache/BUILD +++ b/test/extensions/filters/http/cache/BUILD @@ -14,6 +14,7 @@ envoy_extension_cc_test( extension_name = "envoy.filters.http.cache", deps = [ "//source/extensions/filters/http/cache:http_cache_lib", + "//test/mocks/http:http_mocks", "//test/test_common:simulated_time_system_lib", "//test/test_common:utility_lib", ], diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index 8459964698f99..6d15c798ad5df 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -19,7 +19,7 @@ class CacheFilterTest : public ::testing::Test { Event::SimulatedTimeSystem time_source_; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; Http::TestHeaderMapImpl request_headers_{ - {":authority", "host"}, {":path", "/"}, {":method", "GET"}, {"x-forwarded-proto", "https"}}; + {":path", "/"}, {":method", "GET"}, {"x-forwarded-proto", "https"}}; Http::TestHeaderMapImpl response_headers_{{"date", formatter_.now(time_source_)}, {"cache-control", "public,max-age=3600"}}; NiceMock decoder_callbacks_; @@ -36,7 +36,8 @@ class CacheFilterTest : public ::testing::Test { } }; -TEST_F(CacheFilterTest, ImmediateHit) { +TEST_F(CacheFilterTest, ImmediateHitNoBody) { + request_headers_.insertHost().value(absl::string_view("ImmediateHitNoBody")); ON_CALL(decoder_callbacks_, dispatcher()).WillByDefault(ReturnRef(context_.dispatcher_)); ON_CALL(context_.dispatcher_, post(_)).WillByDefault(::testing::InvokeArgument<0>()); @@ -70,6 +71,48 @@ TEST_F(CacheFilterTest, ImmediateHit) { filter->onDestroy(); } +TEST_F(CacheFilterTest, ImmediateHitBody) { + request_headers_.insertHost().value(absl::string_view("ImmediateHitBody")); + ON_CALL(decoder_callbacks_, dispatcher()).WillByDefault(ReturnRef(context_.dispatcher_)); + ON_CALL(context_.dispatcher_, post(_)).WillByDefault(::testing::InvokeArgument<0>()); + + // Create filter for request 1 + config_.set_name("SimpleHttpCache"); + CacheFilterSharedPtr filter = makeFilter(); + ASSERT_TRUE(filter); + + // Decode request 1 header + EXPECT_CALL(decoder_callbacks_, continueDecoding); + EXPECT_EQ(filter->decodeHeaders(request_headers_, true), + Http::FilterHeadersStatus::StopIteration); + ::testing::Mock::VerifyAndClearExpectations(&decoder_callbacks_); + + // Encode response header + const std::string body = "abc"; + const uint64_t content_length = body.size(); + Buffer::OwnedImpl buffer(body); + response_headers_.insertContentLength().value(content_length); + EXPECT_EQ(filter->encodeHeaders(response_headers_, false), Http::FilterHeadersStatus::Continue); + EXPECT_EQ(filter->encodeData(buffer, true), Http::FilterDataStatus::Continue); + filter->onDestroy(); + + // Create filter for request 2 + filter = makeFilter(); + ASSERT_TRUE(filter); + + // Decode request 2 header + EXPECT_CALL(decoder_callbacks_, + encodeHeaders_(testing::AllOf(IsSupersetOfHeaders(response_headers_), + HeaderHasValueRef("age", "0")), + false)); + EXPECT_CALL(decoder_callbacks_, + encodeData(testing::Property(&Buffer::Instance::toString, testing::Eq(body)), true)); + EXPECT_EQ(filter->decodeHeaders(request_headers_, true), + Http::FilterHeadersStatus::StopIteration); + ::testing::Mock::VerifyAndClearExpectations(&decoder_callbacks_); + filter->onDestroy(); +} + } // namespace } // namespace Cache } // namespace HttpFilters diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index c1e3e837c8aef..0c61ba03b682a 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -1,5 +1,6 @@ #include "extensions/filters/http/cache/http_cache.h" +#include "test/mocks/http/mocks.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" @@ -9,7 +10,6 @@ namespace Envoy { namespace Extensions { namespace HttpFilters { namespace Cache { - TEST(RawByteRangeTest, isSuffix) { auto r = RawByteRange(UINT64_MAX, 4); ASSERT_TRUE(r.isSuffix()); @@ -43,7 +43,7 @@ TEST(AdjustedByteRangeTest, Length) { TEST(AdjustedByteRangeTest, TrimFront) { auto a = AdjustedByteRange(3, 6); a.trimFront(2); - ASSERT_EQ(5, a.firstBytePos()); + ASSERT_EQ(5, a.begin()); } TEST(AdjustedByteRangeTest, MaxLength) { @@ -54,7 +54,6 @@ TEST(AdjustedByteRangeTest, MaxLength) { TEST(AdjustedByteRangeTest, MaxTrim) { auto a = AdjustedByteRange(0, UINT64_MAX); a.trimFront(UINT64_MAX); - ASSERT_EQ(UINT64_MAX, a.firstBytePos()); ASSERT_EQ(0, a.length()); } @@ -63,71 +62,130 @@ class LookupRequestTest : public testing::Test { Event::SimulatedTimeSystem time_source_; SystemTime current_time_ = time_source_.systemTime(); DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; - const Http::TestHeaderMapImpl request_headers_{ - {":path", "/"}, {":scheme", "http"}, {":authority", "example.com"}}; - const LookupRequest lookup_request_{request_headers_, current_time_}; + Http::TestHeaderMapImpl request_headers_{ + {":path", "/"}, {"x-forwarded-proto", "https"}, {":authority", "example.com"}}; }; -TEST_F(LookupRequestTest, makeLookupResult) { - Http::HeaderMapPtr response_headers = Http::makeHeaderMap( +LookupResult makeLookupResult(const LookupRequest& lookup_request, + const Http::TestHeaderMapImpl& response_headers, + uint64_t content_length = 0) { + return lookup_request.makeLookupResult( + std::make_unique(response_headers), content_length); +} + +TEST_F(LookupRequestTest, makeLookupResultNoBody) { + const LookupRequest lookup_request(request_headers_, current_time_); + const Http::TestHeaderMapImpl response_headers( + {{"date", formatter_.fromTime(current_time_)}, {"cache-control", "public, max-age=3600"}}); + const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers); + ASSERT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status); + ASSERT_TRUE(lookup_response.headers); + EXPECT_THAT(*lookup_response.headers, Http::IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(lookup_response.content_length, 0); + EXPECT_TRUE(lookup_response.response_ranges.empty()); + EXPECT_FALSE(lookup_response.has_trailers); +} + +TEST_F(LookupRequestTest, makeLookupResultBody) { + const LookupRequest lookup_request(request_headers_, current_time_); + const Http::TestHeaderMapImpl response_headers( {{"date", formatter_.fromTime(current_time_)}, {"cache-control", "public, max-age=3600"}}); + const uint64_t content_length = 5; const LookupResult lookup_response = - lookup_request_.makeLookupResult(std::move(response_headers), 0); - EXPECT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status) - << formatter_.fromTime(current_time_); + makeLookupResult(lookup_request, response_headers, content_length); + ASSERT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status); + ASSERT_TRUE(lookup_response.headers); + EXPECT_THAT(*lookup_response.headers, Http::IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(lookup_response.content_length, content_length); + EXPECT_TRUE(lookup_response.response_ranges.empty()); + EXPECT_FALSE(lookup_response.has_trailers); } TEST_F(LookupRequestTest, makeLookupResultNoDate) { - Http::HeaderMapPtr response_headers = - Http::makeHeaderMap({{"cache-control", "public, max-age=3600"}}); - const LookupResult lookup_response = - lookup_request_.makeLookupResult(std::move(response_headers), 0); - EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status) - << formatter_.fromTime(current_time_); + const LookupRequest lookup_request(request_headers_, current_time_); + const Http::TestHeaderMapImpl response_headers({{"cache-control", "public, max-age=3600"}}); + const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers); + EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status); + ASSERT_TRUE(lookup_response.headers); + EXPECT_THAT(*lookup_response.headers, Http::IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(lookup_response.content_length, 0); + EXPECT_TRUE(lookup_response.response_ranges.empty()); + EXPECT_FALSE(lookup_response.has_trailers); } TEST_F(LookupRequestTest, PrivateResponse) { - Http::HeaderMapPtr response_headers = - Http::makeHeaderMap({{"age", "2"}, - {"cache-control", "private, max-age=3600"}, - {"date", formatter_.fromTime(current_time_)}}); - const LookupResult lookup_response = - lookup_request_.makeLookupResult(std::move(response_headers), 0); + const LookupRequest lookup_request(request_headers_, current_time_); + const Http::TestHeaderMapImpl response_headers({{"age", "2"}, + {"cache-control", "private, max-age=3600"}, + {"date", formatter_.fromTime(current_time_)}}); + const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers); // We must make sure at cache insertion time, private responses must not be // inserted. However, if the insertion did happen, it would be served at the // time of lookup. (Nothing should rely on this.) - EXPECT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status) - << formatter_.fromTime(current_time_); + ASSERT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status); + ASSERT_TRUE(lookup_response.headers); + EXPECT_THAT(*lookup_response.headers, Http::IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(lookup_response.content_length, 0); + EXPECT_TRUE(lookup_response.response_ranges.empty()); + EXPECT_FALSE(lookup_response.has_trailers); } TEST_F(LookupRequestTest, Expired) { - Http::HeaderMapPtr response_headers = Http::makeHeaderMap( + const LookupRequest lookup_request(request_headers_, current_time_); + const Http::TestHeaderMapImpl response_headers( {{"cache-control", "public, max-age=3600"}, {"date", "Thu, 01 Jan 2019 00:00:00 GMT"}}); - const LookupResult lookup_response = - lookup_request_.makeLookupResult(std::move(response_headers), 0); + const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers); EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status); + ASSERT_TRUE(lookup_response.headers); + EXPECT_THAT(*lookup_response.headers, Http::IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(lookup_response.content_length, 0); + EXPECT_TRUE(lookup_response.response_ranges.empty()); + EXPECT_FALSE(lookup_response.has_trailers); } TEST_F(LookupRequestTest, ExpiredViaFallbackheader) { - Http::HeaderMapPtr response_headers = Http::makeHeaderMap( + const LookupRequest lookup_request(request_headers_, current_time_); + const Http::TestHeaderMapImpl response_headers( {{"expires", formatter_.fromTime(current_time_ - std::chrono::seconds(5))}, {"date", formatter_.fromTime(current_time_)}}); - const LookupResult lookup_response = - lookup_request_.makeLookupResult(std::move(response_headers), 0); + const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers); EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status); } TEST_F(LookupRequestTest, NotExpiredViaFallbackheader) { - Http::HeaderMapPtr response_headers = Http::makeHeaderMap( + const LookupRequest lookup_request(request_headers_, current_time_); + const Http::TestHeaderMapImpl response_headers( {{"expires", formatter_.fromTime(current_time_ + std::chrono::seconds(5))}, {"date", formatter_.fromTime(current_time_)}}); + const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers); + EXPECT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status); +} + +TEST_F(LookupRequestTest, FullRange) { + request_headers_.addCopy("Range", "0-99"); + const LookupRequest lookup_request(request_headers_, current_time_); + const Http::TestHeaderMapImpl response_headers({{"date", formatter_.fromTime(current_time_)}, + {"cache-control", "public, max-age=3600"}, + {"content-length", "4"}}); + const uint64_t content_length = 4; const LookupResult lookup_response = - lookup_request_.makeLookupResult(std::move(response_headers), 0); + makeLookupResult(lookup_request, response_headers, content_length); + ASSERT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status); + ASSERT_TRUE(lookup_response.headers); + EXPECT_THAT(*lookup_response.headers, Http::IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(lookup_response.content_length, 4); + EXPECT_TRUE(lookup_response.response_ranges.empty()); + EXPECT_FALSE(lookup_response.has_trailers); +} - EXPECT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status); +TEST(AdjustByteRangeSet, FullRange) { + std::vector result; + EXPECT_TRUE(adjustByteRangeSet(result, {{0, 99}}, 4)); + ASSERT_EQ(result.size(), 1); + EXPECT_EQ(result[0].length(), 4); } } // namespace Cache diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index 26f1f6ef4a9ef..e68b28c2114c3 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -23,7 +23,7 @@ class SimpleHttpCacheTest : public testing::Test { SimpleHttpCacheTest() { request_headers_.insertMethod().value(absl::string_view(("GET"))); request_headers_.insertHost().value(absl::string_view(("example.com"))); - request_headers_.insertScheme().value(absl::string_view(("https"))); + request_headers_.insertForwardedProto().value(absl::string_view(("https"))); request_headers_.insertCacheControl().value(absl::string_view(("max-age=3600"))); } @@ -78,7 +78,7 @@ class SimpleHttpCacheTest : public testing::Test { if (!lookup_context) { return AssertionFailure() << "Expected nonnull lookup_context"; } - const std::string actual_body = getBody(*lookup_context, 0, body.size() - 1u); + const std::string actual_body = getBody(*lookup_context, 0, body.size()); if (body != actual_body) { return AssertionFailure() << "Expected body == " << body << "\n Actual: " << actual_body; } @@ -201,7 +201,7 @@ TEST_F(SimpleHttpCacheTest, StreamingPut) { EXPECT_EQ(CacheEntryStatus::Ok, lookup_result_.cache_entry_status); EXPECT_NE(nullptr, lookup_result_.headers); ASSERT_EQ(13, lookup_result_.content_length); - EXPECT_EQ("Hello, World!", getBody(*name_lookup_context, 0, 12)); + EXPECT_EQ("Hello, World!", getBody(*name_lookup_context, 0, 13)); } TEST(Registration, getFactory) { From 463994a7800e0e3a89da347123bd91234c69317e Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 11 Dec 2019 15:08:41 -0800 Subject: [PATCH 047/109] Explain reason to use std::enable_shared_from_this. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/cache_filter.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index 1f2febd060a15..29633afb23cda 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -19,6 +19,8 @@ namespace Cache { /** * A filter that caches responses and attempts to satisfy requests from cache. + * It also inherits from std::enable_shared_from_this so it can pass shared_ptrs to asynch methods, + * to ensure that it doesn't get destroyed before they complete. */ class CacheFilter; using CacheFilterSharedPtr = std::shared_ptr; From 48f046b74f0e93ddc554b98261ef36c09e2a097c Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 11 Dec 2019 18:48:50 -0800 Subject: [PATCH 048/109] Clarify and comment out range handling code that can't yet be reached. Signed-off-by: Todd Greer --- .../filters/http/cache/cache_filter.cc | 22 +++++++++++++------ .../filters/http/cache/cache_filter.h | 4 ++-- .../filters/http/cache/cache_filter_test.cc | 2 +- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index ce4b516e278d1..03016f6040f38 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -93,7 +93,7 @@ Http::FilterDataStatus CacheFilter::encodeData(Buffer::Instance& data, bool end_ } void CacheFilter::onOkHeaders(Http::HeaderMapPtr&& headers, - std::vector&& response_ranges, + std::vector&& /*response_ranges*/, uint64_t content_length, bool has_trailers) { if (!lookup_) { return; @@ -107,12 +107,20 @@ void CacheFilter::onOkHeaders(Http::HeaderMapPtr&& headers, return; } if (content_length > 0) { - remaining_body_ = std::move(response_ranges); - // TODO(toddmgreer) handle multi-range requests. - ASSERT(remaining_body_.size() <= 1); - if (remaining_body_.empty()) { - remaining_body_.emplace_back(0, content_length); - } + // TODO(toddmgreer) Once we actually parse range headers, + // uncomment the following to handle their parsed form. + // + // Http::HeaderEntry& status = *headers->Status(); + // if (response_ranges.empty() || status.value() != std::to_string(Envoy::enumToInt( + // Envoy::Http::Code::OK))) { + // // Get the full body from cache. + remaining_body_.emplace_back(0, content_length); + // } else { + // // TODO(toddmgreer) handle multi-range requests. + // ASSERT(response_ranges.size() == 1); + // remaining_body_ = std::move(response_ranges); + // status.value((std::to_string(Envoy::enumToInt(Envoy::Http::Code::PartialContent)))); + // } getBody(); } else { lookup_->getTrailers([self = shared_from_this()](Http::HeaderMapPtr&& trailers) { diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index 29633afb23cda..0e9a88eafa868 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -19,7 +19,7 @@ namespace Cache { /** * A filter that caches responses and attempts to satisfy requests from cache. - * It also inherits from std::enable_shared_from_this so it can pass shared_ptrs to asynch methods, + * It also inherits from std::enable_shared_from_this so it can pass shared_ptrs to async methods, * to ensure that it doesn't get destroyed before they complete. */ class CacheFilter; @@ -66,7 +66,7 @@ class CacheFilter : public Http::PassThroughFilter, InsertContextPtr insert_; // Tracks what body bytes still need to be read from the cache. This is - // currently only one Range, but will expand when full range support is added. + // currently only one Range, but will expand when full range support is added. Initialized by onOkHeaders. std::vector remaining_body_; // True if the response has trailers. diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index 6d15c798ad5df..ea252553bf42f 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -20,7 +20,7 @@ class CacheFilterTest : public ::testing::Test { DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; Http::TestHeaderMapImpl request_headers_{ {":path", "/"}, {":method", "GET"}, {"x-forwarded-proto", "https"}}; - Http::TestHeaderMapImpl response_headers_{{"date", formatter_.now(time_source_)}, + Http::TestHeaderMapImpl response_headers_{{":status", "200"},{"date", formatter_.now(time_source_)}, {"cache-control", "public,max-age=3600"}}; NiceMock decoder_callbacks_; NiceMock encoder_callbacks_; From 03d11566c3dd5c4bc6b84cabbbc01a7210d1085e Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 12 Dec 2019 18:52:26 -0800 Subject: [PATCH 049/109] Style fixes. Signed-off-by: Todd Greer --- .../filter/http/cache/v3alpha/cache.proto | 2 +- .../filters/http/cache/cache_filter.h | 4 +++- .../filters/http/cache/http_cache.cc | 18 ++++++++++++++++++ .../extensions/filters/http/cache/http_cache.h | 5 ++++- .../extensions/filters/http/well_known_names.h | 2 +- .../cache/cache_filter_integration_test.cc | 2 +- .../filters/http/cache/cache_filter_test.cc | 3 ++- .../filters/http/cache/http_cache_test.cc | 10 +++++----- .../http/cache/simple_http_cache_test.cc | 2 +- 9 files changed, 36 insertions(+), 12 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v3alpha/cache.proto b/api/envoy/config/filter/http/cache/v3alpha/cache.proto index bc41ad04f6e98..23f54b3be185c 100644 --- a/api/envoy/config/filter/http/cache/v3alpha/cache.proto +++ b/api/envoy/config/filter/http/cache/v3alpha/cache.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package envoy.config.filter.http.cache.v3alpha; -option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2alpha"; +option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v3alpha"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index 0e9a88eafa868..9deedfdac536a 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -32,6 +32,7 @@ class CacheFilter : public Http::PassThroughFilter, static CacheFilterSharedPtr make(const envoy::config::filter::http::cache::v3alpha::Cache& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source) { + // Can't use make_shared due to private constructor. return std::shared_ptr(new CacheFilter(config, stats_prefix, scope, time_source)); } // Http::StreamFilterBase @@ -66,7 +67,8 @@ class CacheFilter : public Http::PassThroughFilter, InsertContextPtr insert_; // Tracks what body bytes still need to be read from the cache. This is - // currently only one Range, but will expand when full range support is added. Initialized by onOkHeaders. + // currently only one Range, but will expand when full range support is added. Initialized by + // onOkHeaders. std::vector remaining_body_; // True if the response has trailers. diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index a0abc6bb57e94..d21f575d0f8f5 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -1,6 +1,7 @@ #include "extensions/filters/http/cache/http_cache.h" #include +#include #include "common/http/headers.h" #include "common/protobuf/utility.h" @@ -14,6 +15,23 @@ namespace Extensions { namespace HttpFilters { namespace Cache { +std::ostream& operator<<(std::ostream& os, CacheEntryStatus status) { + switch (status) { + case CacheEntryStatus::Ok: + return os << "Ok"; + case CacheEntryStatus::Unusable: + return os << "Unusable"; + case CacheEntryStatus::RequiresValidation: + return os << "RequiresValidation"; + case CacheEntryStatus::FoundNotModified: + return os << "FoundNotModified"; + case CacheEntryStatus::UnsatisfiableRange: + return os << "UnsatisfiableRange"; + } + ASSERT(false, "Unreachable"); + return os; +} + LookupRequest::LookupRequest(const Http::HeaderMap& request_headers, SystemTime timestamp) : timestamp_(timestamp), request_cache_control_(request_headers.CacheControl() == nullptr diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 68e9e0669eebd..63a39a3a724b9 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -18,7 +19,7 @@ namespace Extensions { namespace HttpFilters { namespace Cache { // Whether a given cache entry is good for the current request. -enum CacheEntryStatus { +enum class CacheEntryStatus { // This entry is fresh, and an appropriate response to the request. Ok, // No usable entry was found. If this was generated for a cache entry, the @@ -33,6 +34,8 @@ enum CacheEntryStatus { UnsatisfiableRange, }; +std::ostream& operator<<(std::ostream& os, CacheEntryStatus status); + // Byte range from an HTTP request. class RawByteRange { public: diff --git a/source/extensions/filters/http/well_known_names.h b/source/extensions/filters/http/well_known_names.h index c7e53fc2f68a2..df1a64849f4fd 100644 --- a/source/extensions/filters/http/well_known_names.h +++ b/source/extensions/filters/http/well_known_names.h @@ -15,7 +15,7 @@ class HttpFilterNameValues { // Buffer filter const std::string Buffer = "envoy.buffer"; // Cache filter - const std::string Cache = "envoy.cache"; + const std::string Cache = "envoy.filters.http.cache"; // CORS filter const std::string Cors = "envoy.cors"; // CSRF filter diff --git a/test/extensions/filters/http/cache/cache_filter_integration_test.cc b/test/extensions/filters/http/cache/cache_filter_integration_test.cc index b36e1c7cbe79c..1770d8005ae57 100644 --- a/test/extensions/filters/http/cache/cache_filter_integration_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_integration_test.cc @@ -21,7 +21,7 @@ class CacheIntegrationTest : public Event::TestUsingSimulatedTime, } const std::string default_config{R"EOF( - name: envoy.cache + name: envoy.filters.http.cache typed_config: "@type": type.googleapis.com/envoy.config.filter.http.cache.v3alpha.Cache name: SimpleHttpCache diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index ea252553bf42f..092420c959273 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -20,7 +20,8 @@ class CacheFilterTest : public ::testing::Test { DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; Http::TestHeaderMapImpl request_headers_{ {":path", "/"}, {":method", "GET"}, {"x-forwarded-proto", "https"}}; - Http::TestHeaderMapImpl response_headers_{{":status", "200"},{"date", formatter_.now(time_source_)}, + Http::TestHeaderMapImpl response_headers_{{":status", "200"}, + {"date", formatter_.now(time_source_)}, {"cache-control", "public,max-age=3600"}}; NiceMock decoder_callbacks_; NiceMock encoder_callbacks_; diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index 0c61ba03b682a..b245bf32bb503 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -10,7 +10,7 @@ namespace Envoy { namespace Extensions { namespace HttpFilters { namespace Cache { -TEST(RawByteRangeTest, isSuffix) { +TEST(RawByteRangeTest, IsSuffix) { auto r = RawByteRange(UINT64_MAX, 4); ASSERT_TRUE(r.isSuffix()); } @@ -30,7 +30,7 @@ TEST(RawByteRangeTest, LastBytePos) { ASSERT_EQ(4, r.lastBytePos()); } -TEST(RawByteRangeTest, suffixLength) { +TEST(RawByteRangeTest, SuffixLength) { auto r = RawByteRange(UINT64_MAX, 4); ASSERT_EQ(4, r.suffixLength()); } @@ -73,7 +73,7 @@ LookupResult makeLookupResult(const LookupRequest& lookup_request, std::make_unique(response_headers), content_length); } -TEST_F(LookupRequestTest, makeLookupResultNoBody) { +TEST_F(LookupRequestTest, MakeLookupResultNoBody) { const LookupRequest lookup_request(request_headers_, current_time_); const Http::TestHeaderMapImpl response_headers( {{"date", formatter_.fromTime(current_time_)}, {"cache-control", "public, max-age=3600"}}); @@ -86,7 +86,7 @@ TEST_F(LookupRequestTest, makeLookupResultNoBody) { EXPECT_FALSE(lookup_response.has_trailers); } -TEST_F(LookupRequestTest, makeLookupResultBody) { +TEST_F(LookupRequestTest, MakeLookupResultBody) { const LookupRequest lookup_request(request_headers_, current_time_); const Http::TestHeaderMapImpl response_headers( {{"date", formatter_.fromTime(current_time_)}, {"cache-control", "public, max-age=3600"}}); @@ -101,7 +101,7 @@ TEST_F(LookupRequestTest, makeLookupResultBody) { EXPECT_FALSE(lookup_response.has_trailers); } -TEST_F(LookupRequestTest, makeLookupResultNoDate) { +TEST_F(LookupRequestTest, MakeLookupResultNoDate) { const LookupRequest lookup_request(request_headers_, current_time_); const Http::TestHeaderMapImpl response_headers({{"cache-control", "public, max-age=3600"}}); const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers); diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index e68b28c2114c3..3b8be484bdc80 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -204,7 +204,7 @@ TEST_F(SimpleHttpCacheTest, StreamingPut) { EXPECT_EQ("Hello, World!", getBody(*name_lookup_context, 0, 13)); } -TEST(Registration, getFactory) { +TEST(Registration, GetFactory) { HttpCacheFactory* factory = Registry::FactoryRegistry::getFactory("SimpleHttpCache"); ASSERT_NE(factory, nullptr); From 473e8fe5d3a52747aa60f6c5d88467b8c78b770f Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 13 Dec 2019 14:39:43 -0800 Subject: [PATCH 050/109] Make Cache.name required. Signed-off-by: Todd Greer --- api/envoy/config/filter/http/cache/v3alpha/cache.proto | 6 ++++-- test/extensions/filters/http/cache/config_test.cc | 4 +--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v3alpha/cache.proto b/api/envoy/config/filter/http/cache/v3alpha/cache.proto index 23f54b3be185c..20ea69170c48c 100644 --- a/api/envoy/config/filter/http/cache/v3alpha/cache.proto +++ b/api/envoy/config/filter/http/cache/v3alpha/cache.proto @@ -5,14 +5,16 @@ package envoy.config.filter.http.cache.v3alpha; option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v3alpha"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; + +import "validate/validate.proto"; + // [#protodoc-title: HTTP Cache Filter] // `Design`_ // [#proto-status: experimental] message Cache { - // Name of cache implementation to use. - string name = 1; + string name = 1 [(validate.rules).string = {min_bytes: 1}]; // List of allowed Vary headers. These headers will be provided to caches in lookups, and the // cache filter will only insert responses whose Vary headers (if any) are all in this list. For diff --git a/test/extensions/filters/http/cache/config_test.cc b/test/extensions/filters/http/cache/config_test.cc index 1b9c16b5fbfa9..e80783b94a782 100644 --- a/test/extensions/filters/http/cache/config_test.cc +++ b/test/extensions/filters/http/cache/config_test.cc @@ -31,9 +31,7 @@ TEST_F(CacheFilterFactoryTest, Basic) { } TEST_F(CacheFilterFactoryTest, NoName) { - Http::FilterFactoryCb cb = factory_.createFilterFactoryFromProto(config_, "stats", context_); - EXPECT_THROW(cb(filter_callback_), ProtoValidationException); - ; + EXPECT_THROW(factory_.createFilterFactoryFromProto(config_, "stats", context_), EnvoyException); } TEST_F(CacheFilterFactoryTest, UnregisteredName) { From 259b755677a6f0dd694c59690ce10cc14a81f588 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 13 Dec 2019 15:02:09 -0800 Subject: [PATCH 051/109] Handle unexpected onBody callbacks. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/cache_filter.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 03016f6040f38..24e7d80e29752 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -158,7 +158,7 @@ void CacheFilter::onHeadersAsync(const CacheFilterSharedPtr& self, LookupResult& } void CacheFilter::getBody() { - ASSERT(!remaining_body_.empty()); + ASSERT(!remaining_body_.empty(), "No reason to call getBody when there's no body to get."); CacheFilterSharedPtr self = shared_from_this(); lookup_->getBody(remaining_body_[0], [self](Buffer::InstancePtr&& body) { self->onBody(std::move(body)); }); @@ -172,8 +172,15 @@ void CacheFilter::onBody(Buffer::InstancePtr&& body) { if (!lookup_) { return; } - ASSERT(!remaining_body_.empty()); + if (remaining_body_.empty()) { + ASSERT(false, "CacheFilter doesn't call getBody unless there's more body to get, so this is a " + "bogus callback."); + decoder_callbacks_->resetStream(); + return; + } + if (!body) { + ASSERT(false, "Cache said it had a body, but isn't giving it to us."); decoder_callbacks_->resetStream(); return; } From 0097e24cefd8e8ceb1a32c69f5ca0caa85161b1d Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Mon, 30 Dec 2019 20:06:33 -0800 Subject: [PATCH 052/109] Make adjustByteRangeSet public, with unit tests. Signed-off-by: Todd Greer --- .../filters/http/cache/http_cache.cc | 18 ++++- .../filters/http/cache/http_cache.h | 6 ++ .../filters/http/cache/http_cache_test.cc | 70 +++++++++++++++++-- 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index d21f575d0f8f5..9d00ddaa248a0 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -32,6 +32,10 @@ std::ostream& operator<<(std::ostream& os, CacheEntryStatus status) { return os; } +std::ostream& operator<<(std::ostream& os, const AdjustedByteRange& range) { + return os << "[" << range.begin() << "," << range.end() << ")"; +} + LookupRequest::LookupRequest(const Http::HeaderMap& request_headers, SystemTime timestamp) : timestamp_(timestamp), request_cache_control_(request_headers.CacheControl() == nullptr @@ -110,8 +114,12 @@ bool adjustByteRangeSet(std::vector& response_ranges, for (const RawByteRange& spec : request_range_spec) { if (spec.isSuffix()) { // spec is a suffix-byte-range-spec + if (spec.suffixLength() == 0) { + // This range is unsatisfiable, so skip it. + continue; + } if (spec.suffixLength() >= content_length) { - // All bytes are being requested, so we may as well send a normal '200 + // All bytes are being requested, so we may as well send a '200 // OK' response. response_ranges.clear(); return true; @@ -124,9 +132,15 @@ bool adjustByteRangeSet(std::vector& response_ranges, continue; } if (spec.lastBytePos() >= content_length - 1) { + if (spec.firstBytePos() == 0) { + // All bytes are being requested, so we may as well send a '200 + // OK' response. + response_ranges.clear(); + return true; + } response_ranges.emplace_back(spec.firstBytePos(), content_length); } else { - response_ranges.emplace_back(spec.firstBytePos(), spec.lastBytePos()); + response_ranges.emplace_back(spec.firstBytePos(), spec.lastBytePos() + 1); } } } diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 63a39a3a724b9..274259fc39597 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -91,6 +91,12 @@ class AdjustedByteRange { uint64_t last_; }; +inline bool operator==(const AdjustedByteRange& lhs, const AdjustedByteRange& rhs) { + return lhs.begin() == rhs.begin() && lhs.end() == rhs.end(); +} + +std::ostream& operator<<(std::ostream& os, const AdjustedByteRange& range); + // Adjusts request_range_spec to fit a cached response of size content_length, putting the results // in response_ranges. Returns true if response_ranges is satisfiable (empty is considered // satisfiable, as it denotes the entire body). diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index b245bf32bb503..1944e38b2fbc2 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -6,6 +6,10 @@ #include "gtest/gtest.h" +using testing::ContainerEq; +using testing::TestWithParam; +using testing::ValuesIn; + namespace Envoy { namespace Extensions { namespace HttpFilters { @@ -181,11 +185,69 @@ TEST_F(LookupRequestTest, FullRange) { EXPECT_FALSE(lookup_response.has_trailers); } -TEST(AdjustByteRangeSet, FullRange) { +struct AdjustByteRangeParams { + std::vector request; + std::vector result; + uint64_t content_length; +}; + +AdjustByteRangeParams satisfiable_ranges[] = + // request, result, content_length + { + // Various ways to request the full body. Full responses are signaled by empty result + // vectors. + {{{0, 3}}, {}, 4}, // byte-range-spec, exact + {{{UINT64_MAX, 4}}, {}, 4}, // suffix-byte-range-spec, exact + {{{0, 99}}, {}, 4}, // byte-range-spec, overlong + {{{0, UINT64_MAX}}, {}, 4}, // byte-range-spec, overlong + {{{UINT64_MAX, 5}}, {}, 4}, // suffix-byte-range-spec, overlong + {{{UINT64_MAX, UINT64_MAX - 1}}, {}, 4}, // suffix-byte-range-spec, overlong + {{{UINT64_MAX, UINT64_MAX}}, {}, 4}, // suffix-byte-range-spec, overlong + + // Single bytes + {{{0, 0}}, {{0, 1}}, 4}, + {{{1, 1}}, {{1, 2}}, 4}, + {{{3, 3}}, {{3, 4}}, 4}, + {{{UINT64_MAX, 1}}, {{3, 4}}, 4}, + + // Multiple bytes, starting in the middle + {{{1, 2}}, {{1, 3}}, 4}, // fully in the middle + {{{1, 3}}, {{1, 4}}, 4}, // to the end + {{{2, 21}}, {{2, 4}}, 4}, // overlong + {{{1, UINT64_MAX}}, {{1, 4}}, 4}}; // overlong +// TODO(toddmgreer) Before enabling support for multi-range requests, test it. + +class AdjustByteRangeTest : public TestWithParam {}; + +TEST_P(AdjustByteRangeTest, All) { + std::vector result; + ASSERT_TRUE(adjustByteRangeSet(result, GetParam().request, GetParam().content_length)); + EXPECT_THAT(result, ContainerEq(GetParam().result)); +} + +INSTANTIATE_TEST_SUITE_P(AdjustByteRangeTest, AdjustByteRangeTest, ValuesIn(satisfiable_ranges)); + +class AdjustByteRangeUnsatisfiableTest : public TestWithParam> {}; + +std::vector unsatisfiable_ranges[] = { + {{4, 5}}, + {{4, 9}}, + {{7, UINT64_MAX}}, + {{UINT64_MAX, 0}}, +}; + +TEST_P(AdjustByteRangeUnsatisfiableTest, All) { + std::vector result; + ASSERT_FALSE(adjustByteRangeSet(result, GetParam(), 3)); +} + +INSTANTIATE_TEST_SUITE_P(AdjustByteRangeUnsatisfiableTest, AdjustByteRangeUnsatisfiableTest, + ValuesIn(unsatisfiable_ranges)); + +TEST(AdjustByteRange, NoRangeRequest) { std::vector result; - EXPECT_TRUE(adjustByteRangeSet(result, {{0, 99}}, 4)); - ASSERT_EQ(result.size(), 1); - EXPECT_EQ(result[0].length(), 4); + ASSERT_TRUE(adjustByteRangeSet(result, {}, 8)); + EXPECT_THAT(result, ContainerEq(std::vector{})); } } // namespace Cache From 296a4203511ed0dd2ecae57905d678f5bb9df6bd Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 2 Jan 2020 16:37:06 -0800 Subject: [PATCH 053/109] Use class instead of namespace in http_cache_utils, per reviewer request. Signed-off-by: Todd Greer --- .../filters/http/cache/http_cache.cc | 6 ++--- .../filters/http/cache/http_cache_utils.cc | 17 +++++------- .../filters/http/cache/http_cache_utils.h | 27 ++++++++++++------- .../http/cache/http_cache_utils_test.cc | 13 ++++----- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index 9d00ddaa248a0..3fa31aa8b5e34 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -74,12 +74,12 @@ bool LookupRequest::isFresh(const Http::HeaderMap& response_headers) const { const Http::HeaderEntry* cache_control_header = response_headers.CacheControl(); if (cache_control_header) { const SystemTime::duration effective_max_age = - Internal::effectiveMaxAge(cache_control_header->value().getStringView()); - return timestamp_ - Internal::httpTime(response_headers.Date()) < effective_max_age; + Utils::effectiveMaxAge(cache_control_header->value().getStringView()); + return timestamp_ - Utils::httpTime(response_headers.Date()) < effective_max_age; } // We didn't find a cache-control header with enough info to determine // freshness, so fall back to the expires header. - return timestamp_ <= Internal::httpTime(response_headers.Expires()); + return timestamp_ <= Utils::httpTime(response_headers.Expires()); } LookupResult LookupRequest::makeLookupResult(Http::HeaderMapPtr&& response_headers, diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index be4072823331f..92eb9cc20a1b4 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -12,16 +12,13 @@ namespace Envoy { namespace Extensions { namespace HttpFilters { namespace Cache { -namespace Internal { - -namespace { // True for characters defined as tchars by // https://tools.ietf.org/html/rfc7230#section-3.2.6 // // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" // / "-" / "." / "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA -bool tchar(char c) { +bool Utils::tchar(char c) { switch (c) { case '!': case '#': @@ -47,7 +44,7 @@ bool tchar(char c) { // token was present. // // token = 1*tchar -bool eatToken(absl::string_view& s) { +bool Utils::eatToken(absl::string_view& s) { const absl::string_view::iterator token_end = c_find_if_not(s, &tchar); if (token_end == s.begin()) { return false; @@ -68,7 +65,7 @@ bool eatToken(absl::string_view& s) { // // For example, the directive "my-extension=42" has an argument of "42", so an // input of "public, my-extension=42, max-age=999" -void eatDirectiveArgument(absl::string_view& s) { +void Utils::eatDirectiveArgument(absl::string_view& s) { if (s.empty()) { return; } @@ -80,14 +77,13 @@ void eatDirectiveArgument(absl::string_view& s) { eatToken(s); } } -} // namespace // If s is non-null and begins with a decimal number ([0-9]+), removes it from // the input and returns a SystemTime::duration representing that many seconds. // If s is null or doesn't begin with digits, returns // SystemTime::duration::zero(). If parsing overflows, returns // SystemTime::duration::max(). -SystemTime::duration eatLeadingDuration(absl::string_view& s) { +SystemTime::duration Utils::eatLeadingDuration(absl::string_view& s) { const absl::string_view::iterator digits_end = c_find_if_not(s, &absl::ascii_isdigit); const size_t digits_length = digits_end - s.begin(); if (digits_length == 0) { @@ -105,7 +101,7 @@ SystemTime::duration eatLeadingDuration(absl::string_view& s) { // // TODO(toddmgreer) Write a CacheControl class to fully parse the cache-control // header value. Consider sharing with the gzip filter. -SystemTime::duration effectiveMaxAge(absl::string_view cache_control) { +SystemTime::duration Utils::effectiveMaxAge(absl::string_view cache_control) { // The grammar for This Cache-Control header value should be: // Cache-Control = 1#cache-directive // cache-directive = token [ "=" ( token / quoted-string ) ] @@ -164,7 +160,7 @@ SystemTime::duration effectiveMaxAge(absl::string_view cache_control) { return max_age; } -SystemTime httpTime(const Http::HeaderEntry* header_entry) { +SystemTime Utils::httpTime(const Http::HeaderEntry* header_entry) { if (!header_entry) { return {}; } @@ -186,7 +182,6 @@ SystemTime httpTime(const Http::HeaderEntry* header_entry) { } return {}; } -} // namespace Internal } // namespace Cache } // namespace HttpFilters } // namespace Extensions diff --git a/source/extensions/filters/http/cache/http_cache_utils.h b/source/extensions/filters/http/cache/http_cache_utils.h index 1b956dfd813e7..d62599b8f5bb1 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.h +++ b/source/extensions/filters/http/cache/http_cache_utils.h @@ -10,17 +10,24 @@ namespace Envoy { namespace Extensions { namespace HttpFilters { namespace Cache { -namespace Internal { -// Parses and returns max-age or s-maxage (with s-maxage taking precedence), -// parsed into a SystemTime::Duration. Returns SystemTime::Duration::zero if -// neither is present, or there is a no-cache directive, or if max-age or -// s-maxage is malformed. -SystemTime::duration effectiveMaxAge(absl::string_view cache_control); +class Utils { +public: + // Parses and returns max-age or s-maxage (with s-maxage taking precedence), + // parsed into a SystemTime::Duration. Returns SystemTime::Duration::zero if + // neither is present, or there is a no-cache directive, or if max-age or + // s-maxage is malformed. + static SystemTime::duration effectiveMaxAge(absl::string_view cache_control); -// Parses header_entry as an HTTP time. Returns SystemTime() if -// header_entry is null or malformed. -SystemTime httpTime(const Http::HeaderEntry* header_entry); -} // namespace Internal + // Parses header_entry as an HTTP time. Returns SystemTime() if + // header_entry is null or malformed. + static SystemTime httpTime(const Http::HeaderEntry* header_entry); + +private: + static bool tchar(char c); + static bool eatToken(absl::string_view& s); + static void eatDirectiveArgument(absl::string_view& s); + static SystemTime::duration eatLeadingDuration(absl::string_view& s); +}; } // namespace Cache } // namespace HttpFilters } // namespace Extensions diff --git a/test/extensions/filters/http/cache/http_cache_utils_test.cc b/test/extensions/filters/http/cache/http_cache_utils_test.cc index 69f3d72f55f0d..ee9a041880784 100644 --- a/test/extensions/filters/http/cache/http_cache_utils_test.cc +++ b/test/extensions/filters/http/cache/http_cache_utils_test.cc @@ -12,9 +12,11 @@ namespace Envoy { namespace Extensions { namespace HttpFilters { namespace Cache { -namespace Internal { namespace { +// TODO(toddmgreer) Add tests for eat* functions +// TODO(toddmgreer) More tests for httpTime, effectiveMaxAge + class HttpTimeTest : public testing::TestWithParam { protected: Http::TestHeaderMapImpl response_headers_{{"date", GetParam()}}; @@ -29,22 +31,21 @@ const char* const ok_times[] = { INSTANTIATE_TEST_SUITE_P(Ok, HttpTimeTest, testing::ValuesIn(ok_times)); TEST_P(HttpTimeTest, Ok) { - const std::time_t time = SystemTime::clock::to_time_t(httpTime(response_headers_.Date())); + const std::time_t time = SystemTime::clock::to_time_t(Utils::httpTime(response_headers_.Date())); EXPECT_STREQ(ctime(&time), "Sun Nov 6 08:49:37 1994\n"); } -TEST(HttpTime, Null) { EXPECT_EQ(httpTime(nullptr), SystemTime()); } +TEST(HttpTime, Null) { EXPECT_EQ(Utils::httpTime(nullptr), SystemTime()); } TEST(EffectiveMaxAge, Ok) { - EXPECT_EQ(std::chrono::seconds(3600), effectiveMaxAge("public, max-age=3600")); + EXPECT_EQ(std::chrono::seconds(3600), Utils::effectiveMaxAge("public, max-age=3600")); } TEST(EffectiveMaxAge, NegativeMaxAge) { - EXPECT_EQ(SystemTime::duration::zero(), effectiveMaxAge("public, max-age=-1")); + EXPECT_EQ(SystemTime::duration::zero(), Utils::effectiveMaxAge("public, max-age=-1")); } } // namespace -} // namespace Internal } // namespace Cache } // namespace HttpFilters } // namespace Extensions From e83d95aa2ba6e7c9d5624b0e2ed18dde936f1920 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 2 Jan 2020 16:52:14 -0800 Subject: [PATCH 054/109] Make CacheFilter's internal helper functions static members, per envoy conventions. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/cache_filter.cc | 8 +++----- source/extensions/filters/http/cache/cache_filter.h | 5 +++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 24e7d80e29752..a51fce0433d08 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -11,9 +11,8 @@ namespace Envoy { namespace Extensions { namespace HttpFilters { namespace Cache { -namespace { -bool isCacheableRequest(Http::HeaderMap& headers) { +bool CacheFilter::isCacheableRequest(Http::HeaderMap& headers) { const Http::HeaderEntry* method = headers.Method(); const Http::HeaderEntry* forwarded_proto = headers.ForwardedProto(); const Http::HeaderValues& header_values = Http::Headers::get(); @@ -25,7 +24,7 @@ bool isCacheableRequest(Http::HeaderMap& headers) { forwarded_proto->value() == header_values.SchemeValues.Https); } -bool isCacheableResponse(Http::HeaderMap& headers) { +bool CacheFilter::isCacheableResponse(Http::HeaderMap& headers) { const Http::HeaderEntry* cache_control = headers.CacheControl(); // TODO(toddmgreer) fully check for cacheability. See for example // https://github.com/apache/incubator-pagespeed-mod/blob/master/pagespeed/kernel/http/caching_headers.h. @@ -36,7 +35,7 @@ bool isCacheableResponse(Http::HeaderMap& headers) { return false; } -HttpCache& getCache(const envoy::config::filter::http::cache::v3alpha::Cache& config) { +HttpCache& CacheFilter::getCache(const envoy::config::filter::http::cache::v3alpha::Cache& config) { HttpCacheFactory* factory = Registry::FactoryRegistry::getFactory(config.name()); if (!factory) { @@ -45,7 +44,6 @@ HttpCache& getCache(const envoy::config::filter::http::cache::v3alpha::Cache& co } return factory->getCache(); } -} // namespace CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v3alpha::Cache& config, const std::string&, Stats::Scope&, TimeSource& time_source) diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index 9deedfdac536a..fe3a76d588010 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -61,6 +61,11 @@ class CacheFilter : public Http::PassThroughFilter, static void onTrailersAsync(const CacheFilterSharedPtr& self, Http::HeaderMapPtr&& trailers); void post(std::function f) const; + // These don't require private access, but are members per envoy convention. + static bool isCacheableRequest(Http::HeaderMap& headers); + static bool isCacheableResponse(Http::HeaderMap& headers); + static HttpCache& getCache(const envoy::config::filter::http::cache::v3alpha::Cache& config); + TimeSource& time_source_; HttpCache& cache_; LookupContextPtr lookup_; From a0af25b9503ee0f0a40e0def94e2cc91a0fe9ef6 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 2 Jan 2020 18:43:51 -0800 Subject: [PATCH 055/109] Add effectiveMaxAge tests. Signed-off-by: Todd Greer --- .../http/cache/http_cache_utils_test.cc | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/test/extensions/filters/http/cache/http_cache_utils_test.cc b/test/extensions/filters/http/cache/http_cache_utils_test.cc index ee9a041880784..b06034b8eaf4b 100644 --- a/test/extensions/filters/http/cache/http_cache_utils_test.cc +++ b/test/extensions/filters/http/cache/http_cache_utils_test.cc @@ -37,12 +37,43 @@ TEST_P(HttpTimeTest, Ok) { TEST(HttpTime, Null) { EXPECT_EQ(Utils::httpTime(nullptr), SystemTime()); } -TEST(EffectiveMaxAge, Ok) { - EXPECT_EQ(std::chrono::seconds(3600), Utils::effectiveMaxAge("public, max-age=3600")); -} +struct EffectiveMaxAgeParams { + absl::string_view cache_control; + int effective_max_age_secs; +}; + +EffectiveMaxAgeParams params[] = { + {"public, max-age=3600", 3600}, + {"public, max-age=-1", 0}, + {"max-age=20", 20}, + {"max-age=86400, public", 86400}, + {"public,max-age=\"0\"", 0}, + {"public,max-age=8", 8}, + {"public,max-age=3,no-cache", 0}, + {"s-maxage=0", 0}, + {"max-age=10,s-maxage=0", 0}, + {"s-maxage=10", 10}, + {"no-cache", 0}, + {"max-age=0", 0}, + {"no-cache", 0}, + {"public", 0}, + // TODO(toddmgreer) parse quoted forms + // {"max-age=20, s-maxage=\"25\"",25}, + // {"public,max-age=\"8\",foo=11",8}, + // {"public,max-age=\"8\",bar=\"11\"",8}, + // TODO(toddmgreer) parse public/private + // {"private,max-age=10",0} + // {"private",0}, + // {"private,s-maxage=8",0}, +}; + +class EffectiveMaxAgeTest : public testing::TestWithParam {}; + +INSTANTIATE_TEST_SUITE_P(EffectiveMaxAgeTest, EffectiveMaxAgeTest, testing::ValuesIn(params)); -TEST(EffectiveMaxAge, NegativeMaxAge) { - EXPECT_EQ(SystemTime::duration::zero(), Utils::effectiveMaxAge("public, max-age=-1")); +TEST_P(EffectiveMaxAgeTest, EffectiveMaxAgeTest) { + EXPECT_EQ(Utils::effectiveMaxAge(GetParam().cache_control), + std::chrono::seconds(GetParam().effective_max_age_secs)); } } // namespace From 3a9406a6a6cd734877e506dce048cd2d070e39da Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Mon, 6 Jan 2020 15:05:43 -0800 Subject: [PATCH 056/109] Adapt to HeaderMap changes. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/http_cache.cc | 2 +- .../extensions/filters/http/cache/cache_filter_test.cc | 7 +++---- .../filters/http/cache/simple_http_cache_test.cc | 10 +++++----- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index 3fa31aa8b5e34..1d3828ffa4923 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -92,7 +92,7 @@ LookupResult LookupRequest::makeLookupResult(Http::HeaderMapPtr&& response_heade result.headers = std::move(response_headers); result.content_length = content_length; if (!adjustByteRangeSet(result.response_ranges, request_range_spec_, content_length)) { - result.headers->insertStatus().value(416); // Range Not Satisfiable + result.headers->setStatus(416); // Range Not Satisfiable } result.has_trailers = false; return result; diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index 092420c959273..ec51aaaad84e8 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -38,7 +38,7 @@ class CacheFilterTest : public ::testing::Test { }; TEST_F(CacheFilterTest, ImmediateHitNoBody) { - request_headers_.insertHost().value(absl::string_view("ImmediateHitNoBody")); + request_headers_.setHost("ImmediateHitNoBody"); ON_CALL(decoder_callbacks_, dispatcher()).WillByDefault(ReturnRef(context_.dispatcher_)); ON_CALL(context_.dispatcher_, post(_)).WillByDefault(::testing::InvokeArgument<0>()); @@ -73,7 +73,7 @@ TEST_F(CacheFilterTest, ImmediateHitNoBody) { } TEST_F(CacheFilterTest, ImmediateHitBody) { - request_headers_.insertHost().value(absl::string_view("ImmediateHitBody")); + request_headers_.setHost("ImmediateHitBody"); ON_CALL(decoder_callbacks_, dispatcher()).WillByDefault(ReturnRef(context_.dispatcher_)); ON_CALL(context_.dispatcher_, post(_)).WillByDefault(::testing::InvokeArgument<0>()); @@ -90,9 +90,8 @@ TEST_F(CacheFilterTest, ImmediateHitBody) { // Encode response header const std::string body = "abc"; - const uint64_t content_length = body.size(); Buffer::OwnedImpl buffer(body); - response_headers_.insertContentLength().value(content_length); + response_headers_.setContentLength(body.size()); EXPECT_EQ(filter->encodeHeaders(response_headers_, false), Http::FilterHeadersStatus::Continue); EXPECT_EQ(filter->encodeData(buffer, true), Http::FilterDataStatus::Continue); filter->onDestroy(); diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index 3b8be484bdc80..63eb3068d93f6 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -21,10 +21,10 @@ const std::string EpochDate = "Thu, 01 Jan 1970 00:00:00 GMT"; class SimpleHttpCacheTest : public testing::Test { protected: SimpleHttpCacheTest() { - request_headers_.insertMethod().value(absl::string_view(("GET"))); - request_headers_.insertHost().value(absl::string_view(("example.com"))); - request_headers_.insertForwardedProto().value(absl::string_view(("https"))); - request_headers_.insertCacheControl().value(absl::string_view(("max-age=3600"))); + request_headers_.setMethod("GET"); + request_headers_.setHost("example.com"); + request_headers_.setForwardedProto("https"); + request_headers_.setCacheControl("max-age=3600"); } // Performs a cache lookup. @@ -61,7 +61,7 @@ class SimpleHttpCacheTest : public testing::Test { } LookupRequest makeLookupRequest(absl::string_view request_path) { - request_headers_.insertPath().value(request_path); + request_headers_.setPath(request_path); return LookupRequest(request_headers_, current_time_); } From 6f91e3697efee72ddfead92ef9954f779982137d Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Mon, 6 Jan 2020 15:42:13 -0800 Subject: [PATCH 057/109] Adapt to proto rules changes. Signed-off-by: Todd Greer --- api/BUILD | 1 + api/docs/BUILD | 1 + .../filter/http/cache/v3alpha/cache.proto | 24 +++++++++---------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/api/BUILD b/api/BUILD index b9d24fc7dbbfc..ffc87639ad9fb 100644 --- a/api/BUILD +++ b/api/BUILD @@ -45,6 +45,7 @@ proto_library( "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/adaptive_concurrency/v3alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", + "//envoy/config/filter/http/cache/v3alpha:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/csrf/v3alpha:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", diff --git a/api/docs/BUILD b/api/docs/BUILD index f92d12d133bd3..97b98fb78005a 100644 --- a/api/docs/BUILD +++ b/api/docs/BUILD @@ -35,6 +35,7 @@ proto_library( "//envoy/config/filter/fault/v2:pkg", "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", + "//envoy/config/filter/http/cache/v3alpha:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", "//envoy/config/filter/http/ext_authz/v2:pkg", diff --git a/api/envoy/config/filter/http/cache/v3alpha/cache.proto b/api/envoy/config/filter/http/cache/v3alpha/cache.proto index 20ea69170c48c..860b543a5f25a 100644 --- a/api/envoy/config/filter/http/cache/v3alpha/cache.proto +++ b/api/envoy/config/filter/http/cache/v3alpha/cache.proto @@ -2,26 +2,16 @@ syntax = "proto3"; package envoy.config.filter.http.cache.v3alpha; +import "validate/validate.proto"; + option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v3alpha"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; -import "validate/validate.proto"; - // [#protodoc-title: HTTP Cache Filter] // `Design`_ -// [#proto-status: experimental] message Cache { - // Name of cache implementation to use. - string name = 1 [(validate.rules).string = {min_bytes: 1}]; - - // List of allowed Vary headers. These headers will be provided to caches in lookups, and the - // cache filter will only insert responses whose Vary headers (if any) are all in this list. For - // example, if an origin supplies "vary:user-agent" in a response, the response will only be - // inserted if "user-agent" is in this list. - repeated string allowed_vary_headers = 2; - message KeyCreatorParams { // If true, exclude the URL scheme from the cache key. bool exclude_scheme = 1; @@ -45,5 +35,15 @@ message Cache { // leave both query_string fields at their false/empty defaults. repeated string query_string_keys = 4; } + + // Name of cache implementation to use. + string name = 1 [(validate.rules).string = {min_bytes: 1}]; + + // List of allowed Vary headers. These headers will be provided to caches in lookups, and the + // cache filter will only insert responses whose Vary headers (if any) are all in this list. For + // example, if an origin supplies "vary:user-agent" in a response, the response will only be + // inserted if "user-agent" is in this list. + repeated string allowed_vary_headers = 2; + KeyCreatorParams key_creator_params = 3; } From c1bb3a310bb906254e3a3c579907233eeebf98ef Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Mon, 6 Jan 2020 16:40:22 -0800 Subject: [PATCH 058/109] Add security_posture and status. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/BUILD | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index 16acc3b4697b6..05edf3c4b5b63 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -4,6 +4,7 @@ licenses(["notice"]) # Apache 2 load( "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", "envoy_cc_library", "envoy_package", "envoy_proto_library", @@ -28,10 +29,12 @@ envoy_cc_library( ], ) -envoy_cc_library( +envoy_cc_extension( name = "config", srcs = ["config.cc"], hdrs = ["config.h"], + security_posture = "robust_to_untrusted_downstream_and_upstream", + status = "wip", deps = [ ":cache_filter_lib", ":key_cc_proto", From aaf9ad95c2a45acd325ef3ad4ff805e095df26ed Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 7 Jan 2020 15:06:38 -0800 Subject: [PATCH 059/109] Add placeholder .rst Signed-off-by: Todd Greer --- docs/root/configuration/http/http_filters/cache_filter.rst | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 docs/root/configuration/http/http_filters/cache_filter.rst diff --git a/docs/root/configuration/http/http_filters/cache_filter.rst b/docs/root/configuration/http/http_filters/cache_filter.rst new file mode 100644 index 0000000000000..730bf9dc209be --- /dev/null +++ b/docs/root/configuration/http/http_filters/cache_filter.rst @@ -0,0 +1,7 @@ +.. _config_http_filters_cache: + +Pluggable HTTP Cache +==================== +Pluggable HTTP cache filter is not yet ready for deployment. See `Design doc`_ for more. + +.. TODO(toddmgreer) Write user docs From 2b33348a39732e892253014e7fb5e6e8c71889a0 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 7 Jan 2020 15:12:09 -0800 Subject: [PATCH 060/109] Move from v3alpha to v2, per htuch. Signed-off-by: Todd Greer --- api/BUILD | 2 +- api/docs/BUILD | 2 +- api/envoy/config/filter/http/cache/{v3alpha => v2}/BUILD | 0 .../config/filter/http/cache/{v3alpha => v2}/cache.proto | 8 +++++--- source/extensions/filters/http/cache/BUILD | 4 ++-- source/extensions/filters/http/cache/cache_filter.cc | 4 ++-- source/extensions/filters/http/cache/cache_filter.h | 8 ++++---- source/extensions/filters/http/cache/config.cc | 4 ++-- source/extensions/filters/http/cache/config.h | 8 ++++---- .../filters/http/cache/cache_filter_integration_test.cc | 2 +- test/extensions/filters/http/cache/cache_filter_test.cc | 2 +- test/extensions/filters/http/cache/config_test.cc | 2 +- 12 files changed, 24 insertions(+), 22 deletions(-) rename api/envoy/config/filter/http/cache/{v3alpha => v2}/BUILD (100%) rename api/envoy/config/filter/http/cache/{v3alpha => v2}/cache.proto (86%) diff --git a/api/BUILD b/api/BUILD index ffc87639ad9fb..4be9d20b8c051 100644 --- a/api/BUILD +++ b/api/BUILD @@ -45,7 +45,7 @@ proto_library( "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/adaptive_concurrency/v3alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", - "//envoy/config/filter/http/cache/v3alpha:pkg", + "//envoy/config/filter/http/cache/v2:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/csrf/v3alpha:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", diff --git a/api/docs/BUILD b/api/docs/BUILD index 97b98fb78005a..38b31be6be360 100644 --- a/api/docs/BUILD +++ b/api/docs/BUILD @@ -35,7 +35,7 @@ proto_library( "//envoy/config/filter/fault/v2:pkg", "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", - "//envoy/config/filter/http/cache/v3alpha:pkg", + "//envoy/config/filter/http/cache/v2:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", "//envoy/config/filter/http/ext_authz/v2:pkg", diff --git a/api/envoy/config/filter/http/cache/v3alpha/BUILD b/api/envoy/config/filter/http/cache/v2/BUILD similarity index 100% rename from api/envoy/config/filter/http/cache/v3alpha/BUILD rename to api/envoy/config/filter/http/cache/v2/BUILD diff --git a/api/envoy/config/filter/http/cache/v3alpha/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto similarity index 86% rename from api/envoy/config/filter/http/cache/v3alpha/cache.proto rename to api/envoy/config/filter/http/cache/v2/cache.proto index 860b543a5f25a..0c808817327f2 100644 --- a/api/envoy/config/filter/http/cache/v3alpha/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -1,16 +1,18 @@ syntax = "proto3"; -package envoy.config.filter.http.cache.v3alpha; +package envoy.config.filter.http.cache.v2; import "validate/validate.proto"; -option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v3alpha"; +option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] -// `Design`_ +// Pluggable HTTP Cache :ref:`configuration overview `. +// [#extension: envoy.filters.http.cache] +// bqDesignbq_ message Cache { message KeyCreatorParams { // If true, exclude the URL scheme from the cache key. diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index 05edf3c4b5b63..a82a40ec9faf7 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -25,7 +25,7 @@ envoy_cc_library( "//source/common/http:headers_lib", "//source/common/protobuf", "//source/extensions/filters/http/common:pass_through_filter_lib", - "@envoy_api//envoy/config/filter/http/cache/v3alpha:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/cache/v2:pkg_cc_proto", ], ) @@ -43,7 +43,7 @@ envoy_cc_extension( "//include/envoy/stats:stats_interface", "//source/extensions/filters/http:well_known_names", "//source/extensions/filters/http/common:factory_base_lib", - "@envoy_api//envoy/config/filter/http/cache/v3alpha:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/cache/v2:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index a51fce0433d08..e8009f4e24176 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -35,7 +35,7 @@ bool CacheFilter::isCacheableResponse(Http::HeaderMap& headers) { return false; } -HttpCache& CacheFilter::getCache(const envoy::config::filter::http::cache::v3alpha::Cache& config) { +HttpCache& CacheFilter::getCache(const envoy::config::filter::http::cache::v2::Cache& config) { HttpCacheFactory* factory = Registry::FactoryRegistry::getFactory(config.name()); if (!factory) { @@ -45,7 +45,7 @@ HttpCache& CacheFilter::getCache(const envoy::config::filter::http::cache::v3alp return factory->getCache(); } -CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v3alpha::Cache& config, +CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v2::Cache& config, const std::string&, Stats::Scope&, TimeSource& time_source) : time_source_(time_source), cache_(getCache(config)) {} diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index fe3a76d588010..c12f9bfb6d78b 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -5,7 +5,7 @@ #include #include -#include "envoy/config/filter/http/cache/v3alpha/cache.pb.h" +#include "envoy/config/filter/http/cache/v2/cache.pb.h" #include "common/common/logger.h" @@ -29,7 +29,7 @@ class CacheFilter : public Http::PassThroughFilter, public std::enable_shared_from_this { public: // Throws ProtoValidationException if no registered HttpCacheFactory for config.name. - static CacheFilterSharedPtr make(const envoy::config::filter::http::cache::v3alpha::Cache& config, + static CacheFilterSharedPtr make(const envoy::config::filter::http::cache::v2::Cache& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source) { // Can't use make_shared due to private constructor. @@ -47,7 +47,7 @@ class CacheFilter : public Http::PassThroughFilter, // Throws EnvoyException if no registered HttpCacheFactory for config.name. // Constructor is private to enforce enable_shared_from_this's requirement that this must be owned // by a shared_ptr. - CacheFilter(const envoy::config::filter::http::cache::v3alpha::Cache& config, + CacheFilter(const envoy::config::filter::http::cache::v2::Cache& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source); void getBody(); @@ -64,7 +64,7 @@ class CacheFilter : public Http::PassThroughFilter, // These don't require private access, but are members per envoy convention. static bool isCacheableRequest(Http::HeaderMap& headers); static bool isCacheableResponse(Http::HeaderMap& headers); - static HttpCache& getCache(const envoy::config::filter::http::cache::v3alpha::Cache& config); + static HttpCache& getCache(const envoy::config::filter::http::cache::v2::Cache& config); TimeSource& time_source_; HttpCache& cache_; diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index 9715ed2804f6c..1a517d50068d3 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -3,7 +3,7 @@ #include #include "envoy/common/time.h" -#include "envoy/config/filter/http/cache/v3alpha/cache.pb.validate.h" +#include "envoy/config/filter/http/cache/v2/cache.pb.validate.h" #include "envoy/registry/registry.h" #include "envoy/stats/scope.h" @@ -15,7 +15,7 @@ namespace HttpFilters { namespace Cache { Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v3alpha::Cache& config, + const envoy::config::filter::http::cache::v2::Cache& config, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { return [config, stats_prefix, &context](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter( diff --git a/source/extensions/filters/http/cache/config.h b/source/extensions/filters/http/cache/config.h index 8b644af1cd5e5..47b70e402e71a 100644 --- a/source/extensions/filters/http/cache/config.h +++ b/source/extensions/filters/http/cache/config.h @@ -1,7 +1,7 @@ #pragma once -#include "envoy/config/filter/http/cache/v3alpha/cache.pb.h" -#include "envoy/config/filter/http/cache/v3alpha/cache.pb.validate.h" +#include "envoy/config/filter/http/cache/v2/cache.pb.h" +#include "envoy/config/filter/http/cache/v2/cache.pb.validate.h" #include "extensions/filters/http/cache/cache_filter.h" #include "extensions/filters/http/common/factory_base.h" @@ -13,13 +13,13 @@ namespace HttpFilters { namespace Cache { class CacheFilterFactory - : public Common::FactoryBase { + : public Common::FactoryBase { public: CacheFilterFactory() : FactoryBase(HttpFilterNames::get().Cache) {} private: Http::FilterFactoryCb createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v3alpha::Cache& config, + const envoy::config::filter::http::cache::v2::Cache& config, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; }; diff --git a/test/extensions/filters/http/cache/cache_filter_integration_test.cc b/test/extensions/filters/http/cache/cache_filter_integration_test.cc index 1770d8005ae57..ffa9cddd40793 100644 --- a/test/extensions/filters/http/cache/cache_filter_integration_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_integration_test.cc @@ -23,7 +23,7 @@ class CacheIntegrationTest : public Event::TestUsingSimulatedTime, const std::string default_config{R"EOF( name: envoy.filters.http.cache typed_config: - "@type": type.googleapis.com/envoy.config.filter.http.cache.v3alpha.Cache + "@type": type.googleapis.com/envoy.config.filter.http.cache.v2.Cache name: SimpleHttpCache )EOF"}; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index ec51aaaad84e8..68624761123f1 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -14,7 +14,7 @@ namespace { class CacheFilterTest : public ::testing::Test { protected: - envoy::config::filter::http::cache::v3alpha::Cache config_; + envoy::config::filter::http::cache::v2::Cache config_; NiceMock context_; Event::SimulatedTimeSystem time_source_; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; diff --git a/test/extensions/filters/http/cache/config_test.cc b/test/extensions/filters/http/cache/config_test.cc index e80783b94a782..8cd7cfae4bc84 100644 --- a/test/extensions/filters/http/cache/config_test.cc +++ b/test/extensions/filters/http/cache/config_test.cc @@ -14,7 +14,7 @@ namespace { class CacheFilterFactoryTest : public ::testing::Test { protected: - envoy::config::filter::http::cache::v3alpha::Cache config_; + envoy::config::filter::http::cache::v2::Cache config_; NiceMock context_; CacheFilterFactory factory_; Http::MockFilterChainFactoryCallbacks filter_callback_; From 39064e8bf54065aa8bc2d5faff70aed633186765 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 7 Jan 2020 15:18:41 -0800 Subject: [PATCH 061/109] Fix rst file Signed-off-by: Todd Greer --- docs/root/configuration/http/http_filters/cache_filter.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/root/configuration/http/http_filters/cache_filter.rst b/docs/root/configuration/http/http_filters/cache_filter.rst index 730bf9dc209be..1f39dbb1fa4e0 100644 --- a/docs/root/configuration/http/http_filters/cache_filter.rst +++ b/docs/root/configuration/http/http_filters/cache_filter.rst @@ -2,6 +2,6 @@ Pluggable HTTP Cache ==================== -Pluggable HTTP cache filter is not yet ready for deployment. See `Design doc`_ for more. +Pluggable HTTP cache filter is not yet ready for deployment. See `Design doc `_ for more. .. TODO(toddmgreer) Write user docs From aebd8ca4fc9230b1cfa85fede8022eed55834f58 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 7 Jan 2020 15:20:41 -0800 Subject: [PATCH 062/109] Add to http_filter.rst Signed-off-by: Todd Greer --- docs/root/configuration/http/http_filters/http_filters.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/root/configuration/http/http_filters/http_filters.rst b/docs/root/configuration/http/http_filters/http_filters.rst index 3c5a1eef39cdf..e896c37738a8c 100644 --- a/docs/root/configuration/http/http_filters/http_filters.rst +++ b/docs/root/configuration/http/http_filters/http_filters.rst @@ -8,6 +8,7 @@ HTTP filters adaptive_concurrency_filter buffer_filter + cache_filter cors_filter csrf_filter dynamic_forward_proxy_filter From d24a316080a7c33d733b4085f0918d4a42f7c72e Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 7 Jan 2020 15:48:19 -0800 Subject: [PATCH 063/109] Fix formatting Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/config.cc | 4 ++-- source/extensions/filters/http/cache/config.h | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index 1a517d50068d3..cf0f01babb2cb 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -15,8 +15,8 @@ namespace HttpFilters { namespace Cache { Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v2::Cache& config, - const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { + const envoy::config::filter::http::cache::v2::Cache& config, const std::string& stats_prefix, + Server::Configuration::FactoryContext& context) { return [config, stats_prefix, &context](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter( CacheFilter::make(config, stats_prefix, context.scope(), context.timeSource())); diff --git a/source/extensions/filters/http/cache/config.h b/source/extensions/filters/http/cache/config.h index 47b70e402e71a..26dce73b251e8 100644 --- a/source/extensions/filters/http/cache/config.h +++ b/source/extensions/filters/http/cache/config.h @@ -18,9 +18,10 @@ class CacheFilterFactory CacheFilterFactory() : FactoryBase(HttpFilterNames::get().Cache) {} private: - Http::FilterFactoryCb createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v2::Cache& config, - const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; + Http::FilterFactoryCb + createFilterFactoryFromProtoTyped(const envoy::config::filter::http::cache::v2::Cache& config, + const std::string& stats_prefix, + Server::Configuration::FactoryContext& context) override; }; } // namespace Cache From 7a05205c0ee1e401d1adaed4af410879926f9170 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 8 Jan 2020 16:37:36 -0800 Subject: [PATCH 064/109] Revert accidentally reformatted codec_impl.cc (partial revert of 84a8532ad344bf7d25fb594fa24a4ebf3ac93ea6). Signed-off-by: Todd Greer --- source/extensions/filters/network/common/redis/codec_impl.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/extensions/filters/network/common/redis/codec_impl.cc b/source/extensions/filters/network/common/redis/codec_impl.cc index c7ebc51efa4b2..6b62e228bfa60 100644 --- a/source/extensions/filters/network/common/redis/codec_impl.cc +++ b/source/extensions/filters/network/common/redis/codec_impl.cc @@ -315,8 +315,8 @@ RespValue::CompositeArray::CompositeArrayConstIterator::operator++() { return *this; } -bool RespValue::CompositeArray::CompositeArrayConstIterator:: -operator!=(const CompositeArrayConstIterator& rhs) const { +bool RespValue::CompositeArray::CompositeArrayConstIterator::operator!=( + const CompositeArrayConstIterator& rhs) const { return command_ != (rhs.command_) || &array_ != &(rhs.array_) || index_ != rhs.index_ || first_ != rhs.first_; } From d50765c98b930b2f79058eb9237b393c18b229ad Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 9 Jan 2020 14:39:59 -0800 Subject: [PATCH 065/109] Change query_strings whitelist/blacklist to use oneof, and rename for clarity. Signed-off-by: Todd Greer --- .../config/filter/http/cache/v2/cache.proto | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index 0c808817327f2..fc415decaa8d5 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -14,6 +14,10 @@ option java_multiple_files = true; // bqDesignbq_ message Cache { + message QueryParams { + repeated string query_string_keys = 1; + } + message KeyCreatorParams { // If true, exclude the URL scheme from the cache key. bool exclude_scheme = 1; @@ -21,21 +25,17 @@ message Cache { // If true, exclude the host from the cache key. bool exclude_host = 2; - // If true, treat query_string_keys as a whitelist. - // If false, treat query_string_keys as a blacklist. - bool query_string_keys_whitelist = 3; - - // The list of query string keys, to be treated as a whitelist/blacklist - // depending on the value of query_string_keys_whitelist. - // If your origins ignore all but a few query params, list them here and set - // query_string_keys_whitelist to true. - // If your origins only pay attention to a few params, list them here and - // leave query_string_keys_whitelist as false. - // If your origins ignore the query string entirely, leave this empty and - // set query_string_keys_whitelist to true. - // If you don't know what query params your origins may pay attention to, - // leave both query_string fields at their false/empty defaults. - repeated string query_string_keys = 4; + oneof query_string_keys { + // If set, only query params in this list are included in the cache + // key. Any other query params will not affect cache lookup. If set to an + // empty list, the query string will be entirely ignored in cache lookups. + QueryParams query_string_keys_included = 3; + + // If set, query params in this list are excluded from the cache key, and + // will not affect cache lookup. Setting this to an empty list is + // equivalent to not setting it (but is less efficient). + QueryParams query_string_keys_excluded = 4; + } } // Name of cache implementation to use. @@ -45,7 +45,12 @@ message Cache { // cache filter will only insert responses whose Vary headers (if any) are all in this list. For // example, if an origin supplies "vary:user-agent" in a response, the response will only be // inserted if "user-agent" is in this list. - repeated string allowed_vary_headers = 2; + // + // TODO(toddmgreer) implement vary headers + // repeated string allowed_vary_headers = 2; + + // TODO(toddmgreer) implement key customization + // KeyCreatorParams key_creator_params = 3; - KeyCreatorParams key_creator_params = 3; + // TODO(toddmgreer) add size limit } From a82a214b065791ce0a46f06d19f5c1f176cb0ec0 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 10 Jan 2020 15:42:41 -0800 Subject: [PATCH 066/109] Improve config docs. Signed-off-by: Todd Greer --- .../config/filter/http/cache/v2/cache.proto | 16 ++++++++++------ .../http/http_filters/cache_filter.rst | 5 ++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index fc415decaa8d5..71a1b498a6f5c 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -9,20 +9,22 @@ option java_outer_classname = "CacheProto"; option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] -// Pluggable HTTP Cache :ref:`configuration overview `. +// Pluggable HTTP Cache :ref:`overview `. // [#extension: envoy.filters.http.cache] - -// bqDesignbq_ message Cache { + // List of query params. message QueryParams { repeated string query_string_keys = 1; } + // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { - // If true, exclude the URL scheme from the cache key. + // If true, exclude the URL scheme from the cache key. Set to true if your origins always + // produce the same response for http and https requests. bool exclude_scheme = 1; - // If true, exclude the host from the cache key. + // If true, exclude the host from the cache key. Set to true if your origins' responses don't + // ever depend on host. bool exclude_host = 2; oneof query_string_keys { @@ -50,7 +52,9 @@ message Cache { // repeated string allowed_vary_headers = 2; // TODO(toddmgreer) implement key customization - // KeyCreatorParams key_creator_params = 3; + // + // Modifies cache key creation by restricting which parts of the URL are included. + KeyCreatorParams key_creator_params = 3; // TODO(toddmgreer) add size limit } diff --git a/docs/root/configuration/http/http_filters/cache_filter.rst b/docs/root/configuration/http/http_filters/cache_filter.rst index 1f39dbb1fa4e0..e9d8e31b1d919 100644 --- a/docs/root/configuration/http/http_filters/cache_filter.rst +++ b/docs/root/configuration/http/http_filters/cache_filter.rst @@ -2,6 +2,9 @@ Pluggable HTTP Cache ==================== -Pluggable HTTP cache filter is not yet ready for deployment. See `Design doc `_ for more. +Pluggable HTTP cache filter is not yet ready for deployment. + +* `Design doc `_ +* :ref:`Configuration API ` .. TODO(toddmgreer) Write user docs From 34b14c28eda459c0af930fc720af11461b6ad348 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 10 Jan 2020 17:33:01 -0800 Subject: [PATCH 067/109] Uncomment unimplemented fields in cache.proto, since the formatting tools don't seem to like them. Signed-off-by: Todd Greer --- api/envoy/config/filter/http/cache/v2/cache.proto | 11 +++++++---- generated_api_shadow/BUILD | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index 71a1b498a6f5c..9878c433a7ff6 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -43,18 +43,21 @@ message Cache { // Name of cache implementation to use. string name = 1 [(validate.rules).string = {min_bytes: 1}]; + // TODO(toddmgreer) implement vary headers + // // List of allowed Vary headers. These headers will be provided to caches in lookups, and the // cache filter will only insert responses whose Vary headers (if any) are all in this list. For // example, if an origin supplies "vary:user-agent" in a response, the response will only be // inserted if "user-agent" is in this list. - // - // TODO(toddmgreer) implement vary headers - // repeated string allowed_vary_headers = 2; + repeated string allowed_vary_headers = 2; // TODO(toddmgreer) implement key customization // // Modifies cache key creation by restricting which parts of the URL are included. KeyCreatorParams key_creator_params = 3; - // TODO(toddmgreer) add size limit + // TODO(toddmgreer) implement size limit + // + // Max body size the cache filter will insert into a cache. + uint32 max_body_bytes = 4; } diff --git a/generated_api_shadow/BUILD b/generated_api_shadow/BUILD index caff7f0860a64..c2485ab3f7786 100644 --- a/generated_api_shadow/BUILD +++ b/generated_api_shadow/BUILD @@ -35,6 +35,7 @@ proto_library( "//envoy/config/filter/fault/v2:pkg", "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", + "//envoy/config/filter/http/cache/v2:pkg", "//envoy/config/filter/http/cors/v2:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", From 29d50d118d13d4351190802e439732227a6a701b Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 10 Jan 2020 17:57:01 -0800 Subject: [PATCH 068/109] Fix wrong BUILD deps incantation Signed-off-by: Todd Greer --- .../envoy/config/filter/http/cache/v2/BUILD | 7 +++ .../config/filter/http/cache/v2/cache.proto | 63 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD create mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD b/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD new file mode 100644 index 0000000000000..5dc095ade27a9 --- /dev/null +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD @@ -0,0 +1,7 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package() diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto new file mode 100644 index 0000000000000..9878c433a7ff6 --- /dev/null +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto @@ -0,0 +1,63 @@ +syntax = "proto3"; + +package envoy.config.filter.http.cache.v2; + +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2"; +option java_outer_classname = "CacheProto"; +option java_multiple_files = true; + +// [#protodoc-title: HTTP Cache Filter] +// Pluggable HTTP Cache :ref:`overview `. +// [#extension: envoy.filters.http.cache] +message Cache { + // List of query params. + message QueryParams { + repeated string query_string_keys = 1; + } + + // Modifies cache key creation by restricting which parts of the URL are included. + message KeyCreatorParams { + // If true, exclude the URL scheme from the cache key. Set to true if your origins always + // produce the same response for http and https requests. + bool exclude_scheme = 1; + + // If true, exclude the host from the cache key. Set to true if your origins' responses don't + // ever depend on host. + bool exclude_host = 2; + + oneof query_string_keys { + // If set, only query params in this list are included in the cache + // key. Any other query params will not affect cache lookup. If set to an + // empty list, the query string will be entirely ignored in cache lookups. + QueryParams query_string_keys_included = 3; + + // If set, query params in this list are excluded from the cache key, and + // will not affect cache lookup. Setting this to an empty list is + // equivalent to not setting it (but is less efficient). + QueryParams query_string_keys_excluded = 4; + } + } + + // Name of cache implementation to use. + string name = 1 [(validate.rules).string = {min_bytes: 1}]; + + // TODO(toddmgreer) implement vary headers + // + // List of allowed Vary headers. These headers will be provided to caches in lookups, and the + // cache filter will only insert responses whose Vary headers (if any) are all in this list. For + // example, if an origin supplies "vary:user-agent" in a response, the response will only be + // inserted if "user-agent" is in this list. + repeated string allowed_vary_headers = 2; + + // TODO(toddmgreer) implement key customization + // + // Modifies cache key creation by restricting which parts of the URL are included. + KeyCreatorParams key_creator_params = 3; + + // TODO(toddmgreer) implement size limit + // + // Max body size the cache filter will insert into a cache. + uint32 max_body_bytes = 4; +} From 02c012ff7a84b8c2d744205ac7a8515b4fe1d24d Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 14 Jan 2020 16:20:26 -0800 Subject: [PATCH 069/109] Add proto migration annotations Signed-off-by: Todd Greer --- api/BUILD | 1 + api/envoy/config/filter/http/cache/v2/BUILD | 4 +++- api/envoy/config/filter/http/cache/v2/cache.proto | 3 +++ generated_api_shadow/BUILD | 1 + generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD | 4 +++- .../envoy/config/filter/http/cache/v2/cache.proto | 3 +++ 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/api/BUILD b/api/BUILD index c2485ab3f7786..bdcf1d02b743e 100644 --- a/api/BUILD +++ b/api/BUILD @@ -127,6 +127,7 @@ proto_library( "//envoy/extensions/filters/common/fault/v3alpha:pkg", "//envoy/extensions/filters/http/adaptive_concurrency/v3alpha:pkg", "//envoy/extensions/filters/http/buffer/v3alpha:pkg", + "//envoy/extensions/filters/http/cache/v3alpha:pkg", "//envoy/extensions/filters/http/cors/v3alpha:pkg", "//envoy/extensions/filters/http/csrf/v3alpha:pkg", "//envoy/extensions/filters/http/dynamic_forward_proxy/v3alpha:pkg", diff --git a/api/envoy/config/filter/http/cache/v2/BUILD b/api/envoy/config/filter/http/cache/v2/BUILD index 5dc095ade27a9..ef3541ebcb1df 100644 --- a/api/envoy/config/filter/http/cache/v2/BUILD +++ b/api/envoy/config/filter/http/cache/v2/BUILD @@ -4,4 +4,6 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 -api_proto_package() +api_proto_package( + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index 9878c433a7ff6..7fc6b2a422140 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -2,11 +2,14 @@ syntax = "proto3"; package envoy.config.filter.http.cache.v2; +import "udpa/annotations/migrate.proto"; import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; +option (udpa.annotations.file_migrate).move_to_package = + "envoy.extensions.filters.http.cache.v3alpha"; // [#protodoc-title: HTTP Cache Filter] // Pluggable HTTP Cache :ref:`overview `. diff --git a/generated_api_shadow/BUILD b/generated_api_shadow/BUILD index c2485ab3f7786..bdcf1d02b743e 100644 --- a/generated_api_shadow/BUILD +++ b/generated_api_shadow/BUILD @@ -127,6 +127,7 @@ proto_library( "//envoy/extensions/filters/common/fault/v3alpha:pkg", "//envoy/extensions/filters/http/adaptive_concurrency/v3alpha:pkg", "//envoy/extensions/filters/http/buffer/v3alpha:pkg", + "//envoy/extensions/filters/http/cache/v3alpha:pkg", "//envoy/extensions/filters/http/cors/v3alpha:pkg", "//envoy/extensions/filters/http/csrf/v3alpha:pkg", "//envoy/extensions/filters/http/dynamic_forward_proxy/v3alpha:pkg", diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD b/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD index 5dc095ade27a9..ef3541ebcb1df 100644 --- a/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD @@ -4,4 +4,6 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 -api_proto_package() +api_proto_package( + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto index 9878c433a7ff6..7fc6b2a422140 100644 --- a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto @@ -2,11 +2,14 @@ syntax = "proto3"; package envoy.config.filter.http.cache.v2; +import "udpa/annotations/migrate.proto"; import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; +option (udpa.annotations.file_migrate).move_to_package = + "envoy.extensions.filters.http.cache.v3alpha"; // [#protodoc-title: HTTP Cache Filter] // Pluggable HTTP Cache :ref:`overview `. From 8925c4f7acc7871d6e834ec15cff3b8a6ed54c68 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 14 Jan 2020 16:20:54 -0800 Subject: [PATCH 070/109] Unify range TODO Signed-off-by: Todd Greer --- .../extensions/filters/http/cache/cache_filter.cc | 13 ------------- source/extensions/filters/http/cache/http_cache.cc | 3 ++- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index e8009f4e24176..5d453539eec8b 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -105,20 +105,7 @@ void CacheFilter::onOkHeaders(Http::HeaderMapPtr&& headers, return; } if (content_length > 0) { - // TODO(toddmgreer) Once we actually parse range headers, - // uncomment the following to handle their parsed form. - // - // Http::HeaderEntry& status = *headers->Status(); - // if (response_ranges.empty() || status.value() != std::to_string(Envoy::enumToInt( - // Envoy::Http::Code::OK))) { - // // Get the full body from cache. remaining_body_.emplace_back(0, content_length); - // } else { - // // TODO(toddmgreer) handle multi-range requests. - // ASSERT(response_ranges.size() == 1); - // remaining_body_ = std::move(response_ranges); - // status.value((std::to_string(Envoy::enumToInt(Envoy::Http::Code::PartialContent)))); - // } getBody(); } else { lookup_->getTrailers([self = shared_from_this()](Http::HeaderMapPtr&& trailers) { diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index 1d3828ffa4923..05425198defdc 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -53,7 +53,8 @@ LookupRequest::LookupRequest(const Http::HeaderMap& request_headers, SystemTime // TODO(toddmgreer) Let config determine whether to include forwarded_proto, host, and // query params. // TODO(toddmgreer) get cluster name. - // TODO(toddmgreer) Parse Range header into request_range_spec_. + // TODO(toddmgreer) Parse Range header into request_range_spec_, and handle the resultant + // vector in CacheFilter::onOkHeaders. key_.set_cluster_name("cluster_name_goes_here"); key_.set_host(std::string(request_headers.Host()->value().getStringView())); key_.set_path(std::string(request_headers.Path()->value().getStringView())); From eec76835b90b367e65741583b0a0bad6a7bf1047 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 14 Jan 2020 16:22:08 -0800 Subject: [PATCH 071/109] Add generated BUILD/proto files. Signed-off-by: Todd Greer --- .../filters/http/cache/v3alpha/BUILD | 12 +++ .../filters/http/cache/v3alpha/cache.proto | 74 +++++++++++++++++++ .../filters/http/cache/v3alpha/BUILD | 12 +++ .../filters/http/cache/v3alpha/cache.proto | 74 +++++++++++++++++++ 4 files changed, 172 insertions(+) create mode 100644 api/envoy/extensions/filters/http/cache/v3alpha/BUILD create mode 100644 api/envoy/extensions/filters/http/cache/v3alpha/cache.proto create mode 100644 generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/BUILD create mode 100644 generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto diff --git a/api/envoy/extensions/filters/http/cache/v3alpha/BUILD b/api/envoy/extensions/filters/http/cache/v3alpha/BUILD new file mode 100644 index 0000000000000..45b9b5301b8d9 --- /dev/null +++ b/api/envoy/extensions/filters/http/cache/v3alpha/BUILD @@ -0,0 +1,12 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/filter/http/cache/v2:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/filters/http/cache/v3alpha/cache.proto b/api/envoy/extensions/filters/http/cache/v3alpha/cache.proto new file mode 100644 index 0000000000000..1eddc48bd6de1 --- /dev/null +++ b/api/envoy/extensions/filters/http/cache/v3alpha/cache.proto @@ -0,0 +1,74 @@ +syntax = "proto3"; + +package envoy.extensions.filters.http.cache.v3alpha; + +import "udpa/annotations/versioning.proto"; + +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.http.cache.v3alpha"; +option java_outer_classname = "CacheProto"; +option java_multiple_files = true; + +// [#protodoc-title: HTTP Cache Filter] +// Pluggable HTTP Cache :ref:`overview `. +// [#extension: envoy.filters.http.cache] +message Cache { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.filter.http.cache.v2.Cache"; + + // List of query params. + message QueryParams { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.filter.http.cache.v2.Cache.QueryParams"; + + repeated string query_string_keys = 1; + } + + // Modifies cache key creation by restricting which parts of the URL are included. + message KeyCreatorParams { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.filter.http.cache.v2.Cache.KeyCreatorParams"; + + // If true, exclude the URL scheme from the cache key. Set to true if your origins always + // produce the same response for http and https requests. + bool exclude_scheme = 1; + + // If true, exclude the host from the cache key. Set to true if your origins' responses don't + // ever depend on host. + bool exclude_host = 2; + + oneof query_string_keys { + // If set, only query params in this list are included in the cache + // key. Any other query params will not affect cache lookup. If set to an + // empty list, the query string will be entirely ignored in cache lookups. + QueryParams query_string_keys_included = 3; + + // If set, query params in this list are excluded from the cache key, and + // will not affect cache lookup. Setting this to an empty list is + // equivalent to not setting it (but is less efficient). + QueryParams query_string_keys_excluded = 4; + } + } + + // Name of cache implementation to use. + string name = 1 [(validate.rules).string = {min_bytes: 1}]; + + // TODO(toddmgreer) implement vary headers + // + // List of allowed Vary headers. These headers will be provided to caches in lookups, and the + // cache filter will only insert responses whose Vary headers (if any) are all in this list. For + // example, if an origin supplies "vary:user-agent" in a response, the response will only be + // inserted if "user-agent" is in this list. + repeated string allowed_vary_headers = 2; + + // TODO(toddmgreer) implement key customization + // + // Modifies cache key creation by restricting which parts of the URL are included. + KeyCreatorParams key_creator_params = 3; + + // TODO(toddmgreer) implement size limit + // + // Max body size the cache filter will insert into a cache. + uint32 max_body_bytes = 4; +} diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/BUILD b/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/BUILD new file mode 100644 index 0000000000000..45b9b5301b8d9 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/BUILD @@ -0,0 +1,12 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/filter/http/cache/v2:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto b/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto new file mode 100644 index 0000000000000..1eddc48bd6de1 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto @@ -0,0 +1,74 @@ +syntax = "proto3"; + +package envoy.extensions.filters.http.cache.v3alpha; + +import "udpa/annotations/versioning.proto"; + +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.http.cache.v3alpha"; +option java_outer_classname = "CacheProto"; +option java_multiple_files = true; + +// [#protodoc-title: HTTP Cache Filter] +// Pluggable HTTP Cache :ref:`overview `. +// [#extension: envoy.filters.http.cache] +message Cache { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.filter.http.cache.v2.Cache"; + + // List of query params. + message QueryParams { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.filter.http.cache.v2.Cache.QueryParams"; + + repeated string query_string_keys = 1; + } + + // Modifies cache key creation by restricting which parts of the URL are included. + message KeyCreatorParams { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.filter.http.cache.v2.Cache.KeyCreatorParams"; + + // If true, exclude the URL scheme from the cache key. Set to true if your origins always + // produce the same response for http and https requests. + bool exclude_scheme = 1; + + // If true, exclude the host from the cache key. Set to true if your origins' responses don't + // ever depend on host. + bool exclude_host = 2; + + oneof query_string_keys { + // If set, only query params in this list are included in the cache + // key. Any other query params will not affect cache lookup. If set to an + // empty list, the query string will be entirely ignored in cache lookups. + QueryParams query_string_keys_included = 3; + + // If set, query params in this list are excluded from the cache key, and + // will not affect cache lookup. Setting this to an empty list is + // equivalent to not setting it (but is less efficient). + QueryParams query_string_keys_excluded = 4; + } + } + + // Name of cache implementation to use. + string name = 1 [(validate.rules).string = {min_bytes: 1}]; + + // TODO(toddmgreer) implement vary headers + // + // List of allowed Vary headers. These headers will be provided to caches in lookups, and the + // cache filter will only insert responses whose Vary headers (if any) are all in this list. For + // example, if an origin supplies "vary:user-agent" in a response, the response will only be + // inserted if "user-agent" is in this list. + repeated string allowed_vary_headers = 2; + + // TODO(toddmgreer) implement key customization + // + // Modifies cache key creation by restricting which parts of the URL are included. + KeyCreatorParams key_creator_params = 3; + + // TODO(toddmgreer) implement size limit + // + // Max body size the cache filter will insert into a cache. + uint32 max_body_bytes = 4; +} From 3bfe116ba796a0167573d98c9ebc26dcde4aea0f Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 14 Jan 2020 16:44:43 -0800 Subject: [PATCH 072/109] Fix TODO formatting Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/cache_filter.cc | 10 +++++----- source/extensions/filters/http/cache/cache_filter.h | 2 +- source/extensions/filters/http/cache/http_cache.cc | 8 ++++---- source/extensions/filters/http/cache/http_cache.h | 6 +++--- .../extensions/filters/http/cache/http_cache_utils.cc | 4 ++-- .../extensions/filters/http/cache/simple_http_cache.cc | 6 +++--- test/extensions/filters/http/cache/http_cache_test.cc | 2 +- .../filters/http/cache/http_cache_utils_test.cc | 8 ++++---- .../filters/http/cache/simple_http_cache_test.cc | 4 ++-- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 5d453539eec8b..23cc93f684a53 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -16,8 +16,8 @@ bool CacheFilter::isCacheableRequest(Http::HeaderMap& headers) { const Http::HeaderEntry* method = headers.Method(); const Http::HeaderEntry* forwarded_proto = headers.ForwardedProto(); const Http::HeaderValues& header_values = Http::Headers::get(); - // TODO(toddmgreer) Also serve HEAD requests from cache. - // TODO(toddmgreer) Check all the other cache-related headers. + // TODO(toddmgreer): Also serve HEAD requests from cache. + // TODO(toddmgreer): Check all the other cache-related headers. return method && forwarded_proto && headers.Path() && headers.Host() && (method->value() == header_values.MethodValues.Get) && (forwarded_proto->value() == header_values.SchemeValues.Http || @@ -26,7 +26,7 @@ bool CacheFilter::isCacheableRequest(Http::HeaderMap& headers) { bool CacheFilter::isCacheableResponse(Http::HeaderMap& headers) { const Http::HeaderEntry* cache_control = headers.CacheControl(); - // TODO(toddmgreer) fully check for cacheability. See for example + // TODO(toddmgreer): fully check for cacheability. See for example // https://github.com/apache/incubator-pagespeed-mod/blob/master/pagespeed/kernel/http/caching_headers.h. if (cache_control) { return !StringUtil::caseFindToken(cache_control->value().getStringView(), ",", @@ -83,7 +83,7 @@ Http::FilterHeadersStatus CacheFilter::encodeHeaders(Http::HeaderMap& headers, b Http::FilterDataStatus CacheFilter::encodeData(Buffer::Instance& data, bool end_stream) { if (insert_) { ENVOY_STREAM_LOG(debug, "CacheFilter::encodeHeaders inserting body", *encoder_callbacks_); - // TODO(toddmgreer) Wait for the cache if necessary. + // TODO(toddmgreer): Wait for the cache if necessary. insert_->insertBody( data, [](bool) {}, end_stream); } @@ -98,7 +98,7 @@ void CacheFilter::onOkHeaders(Http::HeaderMapPtr&& headers, } response_has_trailers_ = has_trailers; const bool end_stream = (content_length == 0 && !response_has_trailers_); - // TODO(toddmgreer) Calculate age per https://httpwg.org/specs/rfc7234.html#age.calculations + // TODO(toddmgreer): Calculate age per https://httpwg.org/specs/rfc7234.html#age.calculations headers->addReferenceKey(Http::Headers::get().Age, 0); decoder_callbacks_->encodeHeaders(std::move(headers), end_stream); if (end_stream) { diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index c12f9bfb6d78b..5c02b7665c32f 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -77,7 +77,7 @@ class CacheFilter : public Http::PassThroughFilter, std::vector remaining_body_; // True if the response has trailers. - // TODO(toddmgreer) cache trailers. + // TODO(toddmgreer): cache trailers. bool response_has_trailers_; }; diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index 05425198defdc..cea8d4af1f85f 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -50,10 +50,10 @@ LookupRequest::LookupRequest(const Http::HeaderMap& request_headers, SystemTime const Http::HeaderString& forwarded_proto = request_headers.ForwardedProto()->value(); const auto& scheme_values = Http::Headers::get().SchemeValues; ASSERT(forwarded_proto == scheme_values.Http || forwarded_proto == scheme_values.Https); - // TODO(toddmgreer) Let config determine whether to include forwarded_proto, host, and + // TODO(toddmgreer): Let config determine whether to include forwarded_proto, host, and // query params. - // TODO(toddmgreer) get cluster name. - // TODO(toddmgreer) Parse Range header into request_range_spec_, and handle the resultant + // TODO(toddmgreer): get cluster name. + // TODO(toddmgreer): Parse Range header into request_range_spec_, and handle the resultant // vector in CacheFilter::onOkHeaders. key_.set_cluster_name("cluster_name_goes_here"); key_.set_host(std::string(request_headers.Host()->value().getStringView())); @@ -85,7 +85,7 @@ bool LookupRequest::isFresh(const Http::HeaderMap& response_headers) const { LookupResult LookupRequest::makeLookupResult(Http::HeaderMapPtr&& response_headers, uint64_t content_length) const { - // TODO(toddmgreer) Implement all HTTP caching semantics. + // TODO(toddmgreer): Implement all HTTP caching semantics. ASSERT(response_headers); LookupResult result; result.cache_entry_status = diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 274259fc39597..71df32f45ae57 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -100,7 +100,7 @@ std::ostream& operator<<(std::ostream& os, const AdjustedByteRange& range); // Adjusts request_range_spec to fit a cached response of size content_length, putting the results // in response_ranges. Returns true if response_ranges is satisfiable (empty is considered // satisfiable, as it denotes the entire body). -// TODO(toddmgreer) Merge/reorder ranges where appropriate. +// TODO(toddmgreer): Merge/reorder ranges where appropriate. bool adjustByteRangeSet(std::vector& response_ranges, const std::vector& request_range_spec, uint64_t content_length); @@ -130,7 +130,7 @@ struct LookupResult { // reorder ranges as appropriate, or may clear() response_ranges entirely. std::vector response_ranges; - // TODO(toddmgreer) Implement trailer support. + // TODO(toddmgreer): Implement trailer support. // True if the cached response has trailers. bool has_trailers = false; }; @@ -147,7 +147,7 @@ struct LookupResult { // When providing a cached response, Caches must ensure that the keys (and not // just their hashes) match. // -// TODO(toddmgreer) Ensure that stability guarantees above are accurate. +// TODO(toddmgreer): Ensure that stability guarantees above are accurate. size_t stableHashKey(const Key& key); // LookupRequest holds everything about a request that's needed to look for a diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index 92eb9cc20a1b4..aa69379f0473e 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -70,7 +70,7 @@ void Utils::eatDirectiveArgument(absl::string_view& s) { return; } if (s.front() == '"') { - // TODO(toddmgreer) handle \-escaped quotes + // TODO(toddmgreer): handle \-escaped quotes const size_t closing_quote = s.find('"', 1); s.remove_prefix(closing_quote); } else { @@ -99,7 +99,7 @@ SystemTime::duration Utils::eatLeadingDuration(absl::string_view& s) { // SystemTime::duration::zero(), or is less than the response's, the response // should be validated. // -// TODO(toddmgreer) Write a CacheControl class to fully parse the cache-control +// TODO(toddmgreer): Write a CacheControl class to fully parse the cache-control // header value. Consider sharing with the gzip filter. SystemTime::duration Utils::effectiveMaxAge(absl::string_view cache_control) { // The grammar for This Cache-Control header value should be: diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index fec49afbcea98..f9d1defb5f756 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -31,7 +31,7 @@ class SimpleLookupContext : public LookupContext { } void getTrailers(LookupTrailersCallback&& cb) override { - // TODO(toddmgreer) Support trailers. + // TODO(toddmgreer): Support trailers. ASSERT(false, "We didn't say there were trailers."); cb(nullptr); } @@ -71,7 +71,7 @@ class SimpleInsertContext : public InsertContext { } void insertTrailers(const Http::HeaderMap&) override { - ASSERT(false); // TODO(toddmgreer) support trailers + ASSERT(false); // TODO(toddmgreer): support trailers } private: @@ -96,7 +96,7 @@ void SimpleHttpCache::updateHeaders(LookupContextPtr&& lookup_context, Http::HeaderMapPtr&& response_headers) { ASSERT(lookup_context); ASSERT(response_headers); - // TODO(toddmgreer) Support updating headers. + // TODO(toddmgreer): Support updating headers. ASSERT(false); } diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index 1944e38b2fbc2..b124ceb7983d6 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -215,7 +215,7 @@ AdjustByteRangeParams satisfiable_ranges[] = {{{1, 3}}, {{1, 4}}, 4}, // to the end {{{2, 21}}, {{2, 4}}, 4}, // overlong {{{1, UINT64_MAX}}, {{1, 4}}, 4}}; // overlong -// TODO(toddmgreer) Before enabling support for multi-range requests, test it. +// TODO(toddmgreer): Before enabling support for multi-range requests, test it. class AdjustByteRangeTest : public TestWithParam {}; diff --git a/test/extensions/filters/http/cache/http_cache_utils_test.cc b/test/extensions/filters/http/cache/http_cache_utils_test.cc index b06034b8eaf4b..43c5b79b3836e 100644 --- a/test/extensions/filters/http/cache/http_cache_utils_test.cc +++ b/test/extensions/filters/http/cache/http_cache_utils_test.cc @@ -14,8 +14,8 @@ namespace HttpFilters { namespace Cache { namespace { -// TODO(toddmgreer) Add tests for eat* functions -// TODO(toddmgreer) More tests for httpTime, effectiveMaxAge +// TODO(toddmgreer): Add tests for eat* functions +// TODO(toddmgreer): More tests for httpTime, effectiveMaxAge class HttpTimeTest : public testing::TestWithParam { protected: @@ -57,11 +57,11 @@ EffectiveMaxAgeParams params[] = { {"max-age=0", 0}, {"no-cache", 0}, {"public", 0}, - // TODO(toddmgreer) parse quoted forms + // TODO(toddmgreer): parse quoted forms // {"max-age=20, s-maxage=\"25\"",25}, // {"public,max-age=\"8\",foo=11",8}, // {"public,max-age=\"8\",bar=\"11\"",8}, - // TODO(toddmgreer) parse public/private + // TODO(toddmgreer): parse public/private // {"private,max-age=10",0} // {"private",0}, // {"private,s-maxage=8",0}, diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index 63eb3068d93f6..7760df5562b79 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -141,7 +141,7 @@ TEST_F(SimpleHttpCacheTest, Miss) { TEST_F(SimpleHttpCacheTest, Fresh) { const Http::TestHeaderMapImpl response_headers = {{"date", formatter_.fromTime(current_time_)}, {"cache-control", "public, max-age=3600"}}; - // TODO(toddmgreer) Test with various date headers. + // TODO(toddmgreer): Test with various date headers. insert("/", response_headers, ""); time_source_.sleep(std::chrono::seconds(3600)); lookup("/"); @@ -151,7 +151,7 @@ TEST_F(SimpleHttpCacheTest, Fresh) { TEST_F(SimpleHttpCacheTest, Stale) { const Http::TestHeaderMapImpl response_headers = {{"date", formatter_.fromTime(current_time_)}, {"cache-control", "public, max-age=3600"}}; - // TODO(toddmgreer) Test with various date headers. + // TODO(toddmgreer): Test with various date headers. insert("/", response_headers, ""); time_source_.sleep(std::chrono::seconds(3601)); lookup("/"); From f209aef9f5b58401065f3c7292846887d00ef201 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 14 Jan 2020 16:45:55 -0800 Subject: [PATCH 073/109] Use FALLTHRU macro Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/cache_filter.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 23cc93f684a53..c836691fc3719 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -126,7 +126,7 @@ void CacheFilter::onHeadersAsync(const CacheFilterSharedPtr& self, LookupResult& case CacheEntryStatus::FoundNotModified: case CacheEntryStatus::UnsatisfiableRange: ASSERT(false); // We don't yet return or support these codes. - // FALLTHROUGH + FALLTHRU; case CacheEntryStatus::Unusable: { self->post([self] { self->onUnusableHeaders(); }); return; From 12876b3633ba3497c5eb2abcabce2183aa9827e1 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 14 Jan 2020 17:23:07 -0800 Subject: [PATCH 074/109] Simplified HttpTimeTest to make it clear that it checks the results of parsing the time strings in ok_times. Signed-off-by: Todd Greer --- .../filters/http/cache/http_cache_utils_test.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/extensions/filters/http/cache/http_cache_utils_test.cc b/test/extensions/filters/http/cache/http_cache_utils_test.cc index 43c5b79b3836e..1fb3657d1308b 100644 --- a/test/extensions/filters/http/cache/http_cache_utils_test.cc +++ b/test/extensions/filters/http/cache/http_cache_utils_test.cc @@ -17,10 +17,7 @@ namespace { // TODO(toddmgreer): Add tests for eat* functions // TODO(toddmgreer): More tests for httpTime, effectiveMaxAge -class HttpTimeTest : public testing::TestWithParam { -protected: - Http::TestHeaderMapImpl response_headers_{{"date", GetParam()}}; -}; +class HttpTimeTest : public testing::TestWithParam {}; const char* const ok_times[] = { "Sun, 06 Nov 1994 08:49:37 GMT", // IMF-fixdate @@ -31,8 +28,9 @@ const char* const ok_times[] = { INSTANTIATE_TEST_SUITE_P(Ok, HttpTimeTest, testing::ValuesIn(ok_times)); TEST_P(HttpTimeTest, Ok) { - const std::time_t time = SystemTime::clock::to_time_t(Utils::httpTime(response_headers_.Date())); - EXPECT_STREQ(ctime(&time), "Sun Nov 6 08:49:37 1994\n"); + Http::TestHeaderMapImpl response_headers{{"date", GetParam()}}; + // Manually confirmed that 784111777 is 11/6/94, 8:46:37. + EXPECT_EQ(784111777, SystemTime::clock::to_time_t(Utils::httpTime(response_headers.Date()))); } TEST(HttpTime, Null) { EXPECT_EQ(Utils::httpTime(nullptr), SystemTime()); } From 064e0838ec4cf1f51d86b13086d11b117c75e796 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 14 Jan 2020 17:41:52 -0800 Subject: [PATCH 075/109] Added _ to struct member variable names. Signed-off-by: Todd Greer --- .../filters/http/cache/cache_filter.cc | 10 +-- .../filters/http/cache/http_cache.cc | 12 +-- .../filters/http/cache/http_cache.h | 14 ++-- .../filters/http/cache/simple_http_cache.cc | 10 +-- .../filters/http/cache/simple_http_cache.h | 4 +- .../filters/http/cache/http_cache_test.cc | 76 +++++++++---------- .../http/cache/simple_http_cache_test.cc | 28 +++---- 7 files changed, 77 insertions(+), 77 deletions(-) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index c836691fc3719..d02cff985d46c 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -121,7 +121,7 @@ void CacheFilter::onUnusableHeaders() { } void CacheFilter::onHeadersAsync(const CacheFilterSharedPtr& self, LookupResult&& result) { - switch (result.cache_entry_status) { + switch (result.cache_entry_status_) { case CacheEntryStatus::RequiresValidation: case CacheEntryStatus::FoundNotModified: case CacheEntryStatus::UnsatisfiableRange: @@ -132,10 +132,10 @@ void CacheFilter::onHeadersAsync(const CacheFilterSharedPtr& self, LookupResult& return; } case CacheEntryStatus::Ok: - self->post([self, headers = result.headers.release(), - response_ranges = std::move(result.response_ranges), - content_length = result.content_length, - has_trailers = result.has_trailers]() mutable { + self->post([self, headers = result.headers_.release(), + response_ranges = std::move(result.response_ranges_), + content_length = result.content_length_, + has_trailers = result.has_trailers_]() mutable { self->onOkHeaders(absl::WrapUnique(headers), std::move(response_ranges), content_length, has_trailers); }); diff --git a/source/extensions/filters/http/cache/http_cache.cc b/source/extensions/filters/http/cache/http_cache.cc index cea8d4af1f85f..03727f419f029 100644 --- a/source/extensions/filters/http/cache/http_cache.cc +++ b/source/extensions/filters/http/cache/http_cache.cc @@ -88,14 +88,14 @@ LookupResult LookupRequest::makeLookupResult(Http::HeaderMapPtr&& response_heade // TODO(toddmgreer): Implement all HTTP caching semantics. ASSERT(response_headers); LookupResult result; - result.cache_entry_status = + result.cache_entry_status_ = isFresh(*response_headers) ? CacheEntryStatus::Ok : CacheEntryStatus::RequiresValidation; - result.headers = std::move(response_headers); - result.content_length = content_length; - if (!adjustByteRangeSet(result.response_ranges, request_range_spec_, content_length)) { - result.headers->setStatus(416); // Range Not Satisfiable + result.headers_ = std::move(response_headers); + result.content_length_ = content_length; + if (!adjustByteRangeSet(result.response_ranges_, request_range_spec_, content_length)) { + result.headers_->setStatus(416); // Range Not Satisfiable } - result.has_trailers = false; + result.has_trailers_ = false; return result; } diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 71df32f45ae57..68fa0bc8ecce3 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -108,18 +108,18 @@ bool adjustByteRangeSet(std::vector& response_ranges, // Result of a lookup operation, including cached headers and information needed // to serve a response based on it, or to attempt to validate. struct LookupResult { - // If cache_entry_status == Unusable, none of the other members are + // If cache_entry_status_ == Unusable, none of the other members are // meaningful. - CacheEntryStatus cache_entry_status = CacheEntryStatus::Unusable; + CacheEntryStatus cache_entry_status_ = CacheEntryStatus::Unusable; // Headers of the cached response. - Http::HeaderMapPtr headers; + Http::HeaderMapPtr headers_; // Size of the full response body. Cache filter will generate a content-length // header with this value, replacing any preexisting content-length header. // (This lets us dechunk responses as we insert them, then later serve them // with a content-length header.) - uint64_t content_length; + uint64_t content_length_; // Represents the subset of the cached response body that should be served to // the client. If response_ranges.empty(), the entire body should be served. @@ -128,11 +128,11 @@ struct LookupResult { // response_ranges must be in the range [0,content_length). Caches should // ensure that they can efficiently serve these ranges, and may merge and/or // reorder ranges as appropriate, or may clear() response_ranges entirely. - std::vector response_ranges; + std::vector response_ranges_; // TODO(toddmgreer): Implement trailer support. // True if the cached response has trailers. - bool has_trailers = false; + bool has_trailers_ = false; }; // Produces a hash of key that is consistent across restarts, architectures, @@ -177,7 +177,7 @@ class LookupRequest { // WARNING: Incomplete--do not use in production (yet). // Returns a LookupResult suitable for sending to the cache filter's // LookupHeadersCallback. Specifically, - // - LookupResult::cache_entry_status is set according to HTTP cache + // - LookupResult::cache_entry_status_ is set according to HTTP cache // validation logic. // - LookupResult::headers takes ownership of response_headers. // - LookupResult::content_length == content_length. diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index f9d1defb5f756..e9081265517e9 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -19,9 +19,9 @@ class SimpleLookupContext : public LookupContext { void getHeaders(LookupHeadersCallback&& cb) override { auto entry = cache_.lookup(request_); - body_ = std::move(entry.body); - cb(entry.response_headers - ? request_.makeLookupResult(std::move(entry.response_headers), body_.size()) + body_ = std::move(entry.body_); + cb(entry.response_headers_ + ? request_.makeLookupResult(std::move(entry.response_headers_), body_.size()) : LookupResult{}); } @@ -106,9 +106,9 @@ SimpleHttpCache::Entry SimpleHttpCache::lookup(const LookupRequest& request) { if (iter == map_.end()) { return Entry{}; } - ASSERT(iter->second.response_headers); + ASSERT(iter->second.response_headers_); return SimpleHttpCache::Entry{ - std::make_unique(*iter->second.response_headers), iter->second.body}; + std::make_unique(*iter->second.response_headers_), iter->second.body_}; } void SimpleHttpCache::insert(const Key& key, Http::HeaderMapPtr&& response_headers, diff --git a/source/extensions/filters/http/cache/simple_http_cache.h b/source/extensions/filters/http/cache/simple_http_cache.h index ef797bbefe2eb..96b1d239c5e5d 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.h +++ b/source/extensions/filters/http/cache/simple_http_cache.h @@ -17,8 +17,8 @@ namespace Cache { class SimpleHttpCache : public HttpCache { private: struct Entry { - Http::HeaderMapPtr response_headers; - std::string body; + Http::HeaderMapPtr response_headers_; + std::string body_; }; public: diff --git a/test/extensions/filters/http/cache/http_cache_test.cc b/test/extensions/filters/http/cache/http_cache_test.cc index b124ceb7983d6..5eee698cf0941 100644 --- a/test/extensions/filters/http/cache/http_cache_test.cc +++ b/test/extensions/filters/http/cache/http_cache_test.cc @@ -82,12 +82,12 @@ TEST_F(LookupRequestTest, MakeLookupResultNoBody) { const Http::TestHeaderMapImpl response_headers( {{"date", formatter_.fromTime(current_time_)}, {"cache-control", "public, max-age=3600"}}); const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers); - ASSERT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status); - ASSERT_TRUE(lookup_response.headers); - EXPECT_THAT(*lookup_response.headers, Http::IsSupersetOfHeaders(response_headers)); - EXPECT_EQ(lookup_response.content_length, 0); - EXPECT_TRUE(lookup_response.response_ranges.empty()); - EXPECT_FALSE(lookup_response.has_trailers); + ASSERT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status_); + ASSERT_TRUE(lookup_response.headers_); + EXPECT_THAT(*lookup_response.headers_, Http::IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(lookup_response.content_length_, 0); + EXPECT_TRUE(lookup_response.response_ranges_.empty()); + EXPECT_FALSE(lookup_response.has_trailers_); } TEST_F(LookupRequestTest, MakeLookupResultBody) { @@ -97,24 +97,24 @@ TEST_F(LookupRequestTest, MakeLookupResultBody) { const uint64_t content_length = 5; const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers, content_length); - ASSERT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status); - ASSERT_TRUE(lookup_response.headers); - EXPECT_THAT(*lookup_response.headers, Http::IsSupersetOfHeaders(response_headers)); - EXPECT_EQ(lookup_response.content_length, content_length); - EXPECT_TRUE(lookup_response.response_ranges.empty()); - EXPECT_FALSE(lookup_response.has_trailers); + ASSERT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status_); + ASSERT_TRUE(lookup_response.headers_); + EXPECT_THAT(*lookup_response.headers_, Http::IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(lookup_response.content_length_, content_length); + EXPECT_TRUE(lookup_response.response_ranges_.empty()); + EXPECT_FALSE(lookup_response.has_trailers_); } TEST_F(LookupRequestTest, MakeLookupResultNoDate) { const LookupRequest lookup_request(request_headers_, current_time_); const Http::TestHeaderMapImpl response_headers({{"cache-control", "public, max-age=3600"}}); const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers); - EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status); - ASSERT_TRUE(lookup_response.headers); - EXPECT_THAT(*lookup_response.headers, Http::IsSupersetOfHeaders(response_headers)); - EXPECT_EQ(lookup_response.content_length, 0); - EXPECT_TRUE(lookup_response.response_ranges.empty()); - EXPECT_FALSE(lookup_response.has_trailers); + EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status_); + ASSERT_TRUE(lookup_response.headers_); + EXPECT_THAT(*lookup_response.headers_, Http::IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(lookup_response.content_length_, 0); + EXPECT_TRUE(lookup_response.response_ranges_.empty()); + EXPECT_FALSE(lookup_response.has_trailers_); } TEST_F(LookupRequestTest, PrivateResponse) { @@ -127,12 +127,12 @@ TEST_F(LookupRequestTest, PrivateResponse) { // We must make sure at cache insertion time, private responses must not be // inserted. However, if the insertion did happen, it would be served at the // time of lookup. (Nothing should rely on this.) - ASSERT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status); - ASSERT_TRUE(lookup_response.headers); - EXPECT_THAT(*lookup_response.headers, Http::IsSupersetOfHeaders(response_headers)); - EXPECT_EQ(lookup_response.content_length, 0); - EXPECT_TRUE(lookup_response.response_ranges.empty()); - EXPECT_FALSE(lookup_response.has_trailers); + ASSERT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status_); + ASSERT_TRUE(lookup_response.headers_); + EXPECT_THAT(*lookup_response.headers_, Http::IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(lookup_response.content_length_, 0); + EXPECT_TRUE(lookup_response.response_ranges_.empty()); + EXPECT_FALSE(lookup_response.has_trailers_); } TEST_F(LookupRequestTest, Expired) { @@ -141,12 +141,12 @@ TEST_F(LookupRequestTest, Expired) { {{"cache-control", "public, max-age=3600"}, {"date", "Thu, 01 Jan 2019 00:00:00 GMT"}}); const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers); - EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status); - ASSERT_TRUE(lookup_response.headers); - EXPECT_THAT(*lookup_response.headers, Http::IsSupersetOfHeaders(response_headers)); - EXPECT_EQ(lookup_response.content_length, 0); - EXPECT_TRUE(lookup_response.response_ranges.empty()); - EXPECT_FALSE(lookup_response.has_trailers); + EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status_); + ASSERT_TRUE(lookup_response.headers_); + EXPECT_THAT(*lookup_response.headers_, Http::IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(lookup_response.content_length_, 0); + EXPECT_TRUE(lookup_response.response_ranges_.empty()); + EXPECT_FALSE(lookup_response.has_trailers_); } TEST_F(LookupRequestTest, ExpiredViaFallbackheader) { @@ -156,7 +156,7 @@ TEST_F(LookupRequestTest, ExpiredViaFallbackheader) { {"date", formatter_.fromTime(current_time_)}}); const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers); - EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status); + EXPECT_EQ(CacheEntryStatus::RequiresValidation, lookup_response.cache_entry_status_); } TEST_F(LookupRequestTest, NotExpiredViaFallbackheader) { @@ -165,7 +165,7 @@ TEST_F(LookupRequestTest, NotExpiredViaFallbackheader) { {{"expires", formatter_.fromTime(current_time_ + std::chrono::seconds(5))}, {"date", formatter_.fromTime(current_time_)}}); const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers); - EXPECT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status); + EXPECT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status_); } TEST_F(LookupRequestTest, FullRange) { @@ -177,12 +177,12 @@ TEST_F(LookupRequestTest, FullRange) { const uint64_t content_length = 4; const LookupResult lookup_response = makeLookupResult(lookup_request, response_headers, content_length); - ASSERT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status); - ASSERT_TRUE(lookup_response.headers); - EXPECT_THAT(*lookup_response.headers, Http::IsSupersetOfHeaders(response_headers)); - EXPECT_EQ(lookup_response.content_length, 4); - EXPECT_TRUE(lookup_response.response_ranges.empty()); - EXPECT_FALSE(lookup_response.has_trailers); + ASSERT_EQ(CacheEntryStatus::Ok, lookup_response.cache_entry_status_); + ASSERT_TRUE(lookup_response.headers_); + EXPECT_THAT(*lookup_response.headers_, Http::IsSupersetOfHeaders(response_headers)); + EXPECT_EQ(lookup_response.content_length_, 4); + EXPECT_TRUE(lookup_response.response_ranges_.empty()); + EXPECT_FALSE(lookup_response.has_trailers_); } struct AdjustByteRangeParams { diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index 7760df5562b79..c8632f0b2575c 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -67,12 +67,12 @@ class SimpleHttpCacheTest : public testing::Test { AssertionResult expectLookupSuccessWithBody(LookupContext* lookup_context, absl::string_view body) { - if (lookup_result_.cache_entry_status != CacheEntryStatus::Ok) { + if (lookup_result_.cache_entry_status_ != CacheEntryStatus::Ok) { return AssertionFailure() << "Expected: lookup_result_.cache_entry_status == " "CacheEntryStatus::Ok\n Actual: " - << lookup_result_.cache_entry_status; + << lookup_result_.cache_entry_status_; } - if (!lookup_result_.headers) { + if (!lookup_result_.headers_) { return AssertionFailure() << "Expected nonnull lookup_result_.headers"; } if (!lookup_context) { @@ -97,7 +97,7 @@ class SimpleHttpCacheTest : public testing::Test { TEST_F(SimpleHttpCacheTest, PutGet) { const std::string RequestPath1("Name"); LookupContextPtr name_lookup_context = lookup(RequestPath1); - EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); + EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status_); Http::TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, {"cache-control", "public,max-age=3600"}}; @@ -109,7 +109,7 @@ TEST_F(SimpleHttpCacheTest, PutGet) { const std::string& RequestPath2("Another Name"); LookupContextPtr another_name_lookup_context = lookup(RequestPath2); - EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); + EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status_); const std::string NewBody1("NewValue"); insert(move(name_lookup_context), response_headers, NewBody1); @@ -123,7 +123,7 @@ TEST_F(SimpleHttpCacheTest, PrivateResponse) { const std::string request_path("Name"); LookupContextPtr name_lookup_context = lookup(request_path); - EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); + EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status_); const std::string Body("Value"); // We must make sure at cache insertion time, private responses must not be @@ -135,7 +135,7 @@ TEST_F(SimpleHttpCacheTest, PrivateResponse) { TEST_F(SimpleHttpCacheTest, Miss) { LookupContextPtr name_lookup_context = lookup("Name"); - EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); + EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status_); } TEST_F(SimpleHttpCacheTest, Fresh) { @@ -145,7 +145,7 @@ TEST_F(SimpleHttpCacheTest, Fresh) { insert("/", response_headers, ""); time_source_.sleep(std::chrono::seconds(3600)); lookup("/"); - EXPECT_EQ(CacheEntryStatus::Ok, lookup_result_.cache_entry_status); + EXPECT_EQ(CacheEntryStatus::Ok, lookup_result_.cache_entry_status_); } TEST_F(SimpleHttpCacheTest, Stale) { @@ -155,14 +155,14 @@ TEST_F(SimpleHttpCacheTest, Stale) { insert("/", response_headers, ""); time_source_.sleep(std::chrono::seconds(3601)); lookup("/"); - EXPECT_EQ(CacheEntryStatus::Ok, lookup_result_.cache_entry_status); + EXPECT_EQ(CacheEntryStatus::Ok, lookup_result_.cache_entry_status_); } TEST_F(SimpleHttpCacheTest, RequestSmallMinFresh) { request_headers_.setReferenceKey(Http::Headers::get().CacheControl, "min-fresh=1000"); const std::string request_path("Name"); LookupContextPtr name_lookup_context = lookup(request_path); - EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); + EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status_); Http::TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, {"age", "6000"}, @@ -177,7 +177,7 @@ TEST_F(SimpleHttpCacheTest, ResponseStaleWithRequestLargeMaxStale) { const std::string request_path("Name"); LookupContextPtr name_lookup_context = lookup(request_path); - EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status); + EXPECT_EQ(CacheEntryStatus::Unusable, lookup_result_.cache_entry_status_); Http::TestHeaderMapImpl response_headers{{"date", formatter_.fromTime(current_time_)}, {"age", "7200"}, @@ -198,9 +198,9 @@ TEST_F(SimpleHttpCacheTest, StreamingPut) { Buffer::OwnedImpl("Hello, "), [](bool ready) { EXPECT_TRUE(ready); }, false); inserter->insertBody(Buffer::OwnedImpl("World!"), nullptr, true); LookupContextPtr name_lookup_context = lookup("request_path"); - EXPECT_EQ(CacheEntryStatus::Ok, lookup_result_.cache_entry_status); - EXPECT_NE(nullptr, lookup_result_.headers); - ASSERT_EQ(13, lookup_result_.content_length); + EXPECT_EQ(CacheEntryStatus::Ok, lookup_result_.cache_entry_status_); + EXPECT_NE(nullptr, lookup_result_.headers_); + ASSERT_EQ(13, lookup_result_.content_length_); EXPECT_EQ("Hello, World!", getBody(*name_lookup_context, 0, 13)); } From 1782f2c84e887f6d684e03d84c588c93d0293e09 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 14 Jan 2020 17:52:13 -0800 Subject: [PATCH 076/109] Add integration test TODO to test unimplemented features. Signed-off-by: Todd Greer --- .../filters/http/cache/cache_filter_integration_test.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/extensions/filters/http/cache/cache_filter_integration_test.cc b/test/extensions/filters/http/cache/cache_filter_integration_test.cc index ffa9cddd40793..f426b9109e906 100644 --- a/test/extensions/filters/http/cache/cache_filter_integration_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_integration_test.cc @@ -6,6 +6,11 @@ namespace Extensions { namespace HttpFilters { namespace Cache { +// TODO(toddmgreer): Expand integration test to include age header values, +// expiration, range headers, HEAD requests, trailers, config customizations, +// cache-control headers, and conditional header fields, as they are +// implemented. + class CacheIntegrationTest : public Event::TestUsingSimulatedTime, public HttpProtocolIntegrationTest { public: From 9e5c10f6af37f3cf4ea17fc3008be7353cd3a8d1 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 14 Jan 2020 18:09:34 -0800 Subject: [PATCH 077/109] Remove unneeded dictionary addition Signed-off-by: Todd Greer --- tools/spelling_dictionary.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/spelling_dictionary.txt b/tools/spelling_dictionary.txt index cc88470dbef9d..f7be92d54e40e 100644 --- a/tools/spelling_dictionary.txt +++ b/tools/spelling_dictionary.txt @@ -89,7 +89,6 @@ EWOULDBLOCK EXPECTing EXPECTs EXPR -FALLTHROUGH FAQ FASTOPEN FB From cffcc020255505aae69b064c8dbc0f3f90d7c2b3 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 15 Jan 2020 14:23:35 -0800 Subject: [PATCH 078/109] Rename envoy::config::filter::http::cache::v2::Cache to envoy::config::filter::http::cache::v2::CacheConfig. Signed-off-by: Todd Greer --- api/envoy/config/filter/http/cache/v2/cache.proto | 2 +- .../extensions/filters/http/cache/v3alpha/cache.proto | 8 ++++---- .../envoy/config/filter/http/cache/v2/cache.proto | 2 +- .../extensions/filters/http/cache/v3alpha/cache.proto | 8 ++++---- source/extensions/filters/http/cache/cache_filter.cc | 5 +++-- source/extensions/filters/http/cache/cache_filter.h | 10 +++++----- source/extensions/filters/http/cache/config.cc | 4 ++-- source/extensions/filters/http/cache/config.h | 9 ++++----- .../http/cache/cache_filter_integration_test.cc | 2 +- .../extensions/filters/http/cache/cache_filter_test.cc | 2 +- test/extensions/filters/http/cache/config_test.cc | 2 +- 11 files changed, 27 insertions(+), 27 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index 7fc6b2a422140..e4fdd2d3a8175 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -14,7 +14,7 @@ option (udpa.annotations.file_migrate).move_to_package = // [#protodoc-title: HTTP Cache Filter] // Pluggable HTTP Cache :ref:`overview `. // [#extension: envoy.filters.http.cache] -message Cache { +message CacheConfig { // List of query params. message QueryParams { repeated string query_string_keys = 1; diff --git a/api/envoy/extensions/filters/http/cache/v3alpha/cache.proto b/api/envoy/extensions/filters/http/cache/v3alpha/cache.proto index 1eddc48bd6de1..85962c768ac08 100644 --- a/api/envoy/extensions/filters/http/cache/v3alpha/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3alpha/cache.proto @@ -13,14 +13,14 @@ option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] // Pluggable HTTP Cache :ref:`overview `. // [#extension: envoy.filters.http.cache] -message Cache { +message CacheConfig { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v2.Cache"; + "envoy.config.filter.http.cache.v2.CacheConfig"; // List of query params. message QueryParams { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v2.Cache.QueryParams"; + "envoy.config.filter.http.cache.v2.CacheConfig.QueryParams"; repeated string query_string_keys = 1; } @@ -28,7 +28,7 @@ message Cache { // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v2.Cache.KeyCreatorParams"; + "envoy.config.filter.http.cache.v2.CacheConfig.KeyCreatorParams"; // If true, exclude the URL scheme from the cache key. Set to true if your origins always // produce the same response for http and https requests. diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto index 7fc6b2a422140..e4fdd2d3a8175 100644 --- a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto @@ -14,7 +14,7 @@ option (udpa.annotations.file_migrate).move_to_package = // [#protodoc-title: HTTP Cache Filter] // Pluggable HTTP Cache :ref:`overview `. // [#extension: envoy.filters.http.cache] -message Cache { +message CacheConfig { // List of query params. message QueryParams { repeated string query_string_keys = 1; diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto b/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto index 1eddc48bd6de1..85962c768ac08 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto @@ -13,14 +13,14 @@ option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] // Pluggable HTTP Cache :ref:`overview `. // [#extension: envoy.filters.http.cache] -message Cache { +message CacheConfig { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v2.Cache"; + "envoy.config.filter.http.cache.v2.CacheConfig"; // List of query params. message QueryParams { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v2.Cache.QueryParams"; + "envoy.config.filter.http.cache.v2.CacheConfig.QueryParams"; repeated string query_string_keys = 1; } @@ -28,7 +28,7 @@ message Cache { // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v2.Cache.KeyCreatorParams"; + "envoy.config.filter.http.cache.v2.CacheConfig.KeyCreatorParams"; // If true, exclude the URL scheme from the cache key. Set to true if your origins always // produce the same response for http and https requests. diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index d02cff985d46c..c2c1007a91648 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -35,7 +35,8 @@ bool CacheFilter::isCacheableResponse(Http::HeaderMap& headers) { return false; } -HttpCache& CacheFilter::getCache(const envoy::config::filter::http::cache::v2::Cache& config) { +HttpCache& +CacheFilter::getCache(const envoy::config::filter::http::cache::v2::CacheConfig& config) { HttpCacheFactory* factory = Registry::FactoryRegistry::getFactory(config.name()); if (!factory) { @@ -45,7 +46,7 @@ HttpCache& CacheFilter::getCache(const envoy::config::filter::http::cache::v2::C return factory->getCache(); } -CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v2::Cache& config, +CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v2::CacheConfig& config, const std::string&, Stats::Scope&, TimeSource& time_source) : time_source_(time_source), cache_(getCache(config)) {} diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index 5c02b7665c32f..8e68342b1edec 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -29,9 +29,9 @@ class CacheFilter : public Http::PassThroughFilter, public std::enable_shared_from_this { public: // Throws ProtoValidationException if no registered HttpCacheFactory for config.name. - static CacheFilterSharedPtr make(const envoy::config::filter::http::cache::v2::Cache& config, - const std::string& stats_prefix, Stats::Scope& scope, - TimeSource& time_source) { + static CacheFilterSharedPtr + make(const envoy::config::filter::http::cache::v2::CacheConfig& config, + const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source) { // Can't use make_shared due to private constructor. return std::shared_ptr(new CacheFilter(config, stats_prefix, scope, time_source)); } @@ -47,7 +47,7 @@ class CacheFilter : public Http::PassThroughFilter, // Throws EnvoyException if no registered HttpCacheFactory for config.name. // Constructor is private to enforce enable_shared_from_this's requirement that this must be owned // by a shared_ptr. - CacheFilter(const envoy::config::filter::http::cache::v2::Cache& config, + CacheFilter(const envoy::config::filter::http::cache::v2::CacheConfig& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source); void getBody(); @@ -64,7 +64,7 @@ class CacheFilter : public Http::PassThroughFilter, // These don't require private access, but are members per envoy convention. static bool isCacheableRequest(Http::HeaderMap& headers); static bool isCacheableResponse(Http::HeaderMap& headers); - static HttpCache& getCache(const envoy::config::filter::http::cache::v2::Cache& config); + static HttpCache& getCache(const envoy::config::filter::http::cache::v2::CacheConfig& config); TimeSource& time_source_; HttpCache& cache_; diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index cf0f01babb2cb..c043cf2cd4e1e 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -15,8 +15,8 @@ namespace HttpFilters { namespace Cache { Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v2::Cache& config, const std::string& stats_prefix, - Server::Configuration::FactoryContext& context) { + const envoy::config::filter::http::cache::v2::CacheConfig& config, + const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { return [config, stats_prefix, &context](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter( CacheFilter::make(config, stats_prefix, context.scope(), context.timeSource())); diff --git a/source/extensions/filters/http/cache/config.h b/source/extensions/filters/http/cache/config.h index 26dce73b251e8..d740194676925 100644 --- a/source/extensions/filters/http/cache/config.h +++ b/source/extensions/filters/http/cache/config.h @@ -13,15 +13,14 @@ namespace HttpFilters { namespace Cache { class CacheFilterFactory - : public Common::FactoryBase { + : public Common::FactoryBase { public: CacheFilterFactory() : FactoryBase(HttpFilterNames::get().Cache) {} private: - Http::FilterFactoryCb - createFilterFactoryFromProtoTyped(const envoy::config::filter::http::cache::v2::Cache& config, - const std::string& stats_prefix, - Server::Configuration::FactoryContext& context) override; + Http::FilterFactoryCb createFilterFactoryFromProtoTyped( + const envoy::config::filter::http::cache::v2::CacheConfig& config, + const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; }; } // namespace Cache diff --git a/test/extensions/filters/http/cache/cache_filter_integration_test.cc b/test/extensions/filters/http/cache/cache_filter_integration_test.cc index f426b9109e906..22522028111ea 100644 --- a/test/extensions/filters/http/cache/cache_filter_integration_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_integration_test.cc @@ -28,7 +28,7 @@ class CacheIntegrationTest : public Event::TestUsingSimulatedTime, const std::string default_config{R"EOF( name: envoy.filters.http.cache typed_config: - "@type": type.googleapis.com/envoy.config.filter.http.cache.v2.Cache + "@type": type.googleapis.com/envoy.config.filter.http.cache.v2.CacheConfig name: SimpleHttpCache )EOF"}; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index 68624761123f1..b67b2223e3b26 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -14,7 +14,7 @@ namespace { class CacheFilterTest : public ::testing::Test { protected: - envoy::config::filter::http::cache::v2::Cache config_; + envoy::config::filter::http::cache::v2::CacheConfig config_; NiceMock context_; Event::SimulatedTimeSystem time_source_; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; diff --git a/test/extensions/filters/http/cache/config_test.cc b/test/extensions/filters/http/cache/config_test.cc index 8cd7cfae4bc84..1ca960fce0148 100644 --- a/test/extensions/filters/http/cache/config_test.cc +++ b/test/extensions/filters/http/cache/config_test.cc @@ -14,7 +14,7 @@ namespace { class CacheFilterFactoryTest : public ::testing::Test { protected: - envoy::config::filter::http::cache::v2::Cache config_; + envoy::config::filter::http::cache::v2::CacheConfig config_; NiceMock context_; CacheFilterFactory factory_; Http::MockFilterChainFactoryCallbacks filter_callback_; From 9f4a1c6ad672e112f1bca004cc52f5b305cf8284 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 15 Jan 2020 18:03:46 -0800 Subject: [PATCH 079/109] Add overview doc. Signed-off-by: Todd Greer --- .../config/filter/http/cache/v2/cache.proto | 2 +- .../filters/http/cache/v3alpha/cache.proto | 2 +- .../http/http_filters/cache_filter.rst | 45 ++++++++++++++++--- .../config/filter/http/cache/v2/cache.proto | 2 +- .../filters/http/cache/v3alpha/cache.proto | 2 +- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index e4fdd2d3a8175..52db83d974449 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -12,7 +12,7 @@ option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3alpha"; // [#protodoc-title: HTTP Cache Filter] -// Pluggable HTTP Cache :ref:`overview `. +// HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] message CacheConfig { // List of query params. diff --git a/api/envoy/extensions/filters/http/cache/v3alpha/cache.proto b/api/envoy/extensions/filters/http/cache/v3alpha/cache.proto index 85962c768ac08..ad723ba60f0f4 100644 --- a/api/envoy/extensions/filters/http/cache/v3alpha/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3alpha/cache.proto @@ -11,7 +11,7 @@ option java_outer_classname = "CacheProto"; option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] -// Pluggable HTTP Cache :ref:`overview `. +// HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] message CacheConfig { option (udpa.annotations.versioning).previous_message_type = diff --git a/docs/root/configuration/http/http_filters/cache_filter.rst b/docs/root/configuration/http/http_filters/cache_filter.rst index e9d8e31b1d919..a675d71bf5a09 100644 --- a/docs/root/configuration/http/http_filters/cache_filter.rst +++ b/docs/root/configuration/http/http_filters/cache_filter.rst @@ -1,10 +1,43 @@ .. _config_http_filters_cache: -Pluggable HTTP Cache -==================== -Pluggable HTTP cache filter is not yet ready for deployment. +HTTP Cache Filter +================= +**Work in Progress--not ready for deployment** -* `Design doc `_ -* :ref:`Configuration API ` +HTTP caching can improve system throughput and latency and reduce network and +backend loads when the same content is requested multiple times. Caching is +particularly valuable for edge proxies and browser-based traffic, which +typically include many cacheable static resources, but it can be useful any time +there is enough repeatedly served content. -.. TODO(toddmgreer) Write user docs +Configuration +------------- +* :ref:`Configuration API ` +* This filter should be configured with the name *envoy.filters.http.cache*. + +The only required configuration field is :ref:`name +`, which must +specify a valid cache storage implementation linked into your Envoy +binary. Specifying 'SimpleHttpCache' will select a proof-of-concept +implementation included in the Envoy source. More implementations can (and will) +be provided by implementing Envoy::Extensions::HttpFilters::Cache::HttpCache. + +The remaining configuration options control caching behavior and limits. By +default, this filter will cache almost all responses that are considered +cacheable by `RFC7234 `_, with handling +of conditional (`RFC7232 `_), and range +(`RFC7233 `_) requests. Those RFC define +which request methods and response codes are cacheable, subject to the +cache-related headers they also define: cache-control, range, if-match, +if-none-match, if-modified-since, if-unmodified-since, if-range, authorization, +date, age, expires, and vary. Responses with a 'vary' header will only be cached +if the named headers are listed in :ref:`allowed_vary_headers +` + +Status +------ +* This filter *is* ready for developers to write cache storage plugins; please + contribute them to the Envoy repository if possible. +* This filter *is* ready for contributions to help finish its implementation of + HTTP caching semantics. +* This filter *is not* ready for actual use. Please see TODOs in the code. diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto index e4fdd2d3a8175..52db83d974449 100644 --- a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto @@ -12,7 +12,7 @@ option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3alpha"; // [#protodoc-title: HTTP Cache Filter] -// Pluggable HTTP Cache :ref:`overview `. +// HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] message CacheConfig { // List of query params. diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto b/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto index 85962c768ac08..ad723ba60f0f4 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto @@ -11,7 +11,7 @@ option java_outer_classname = "CacheProto"; option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] -// Pluggable HTTP Cache :ref:`overview `. +// HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] message CacheConfig { option (udpa.annotations.versioning).previous_message_type = From c796d4d735245b55345ed269e6976fca16c10452 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 16 Jan 2020 12:58:55 -0800 Subject: [PATCH 080/109] Change v3alpha to v3 Signed-off-by: Todd Greer --- api/BUILD | 2 +- api/envoy/config/filter/http/cache/v2/cache.proto | 3 +-- api/envoy/extensions/filters/http/cache/{v3alpha => v3}/BUILD | 0 .../extensions/filters/http/cache/{v3alpha => v3}/cache.proto | 4 ++-- generated_api_shadow/BUILD | 2 +- .../envoy/config/filter/http/cache/v2/cache.proto | 3 +-- .../envoy/extensions/filters/http/cache/{v3alpha => v3}/BUILD | 0 .../extensions/filters/http/cache/{v3alpha => v3}/cache.proto | 4 ++-- 8 files changed, 8 insertions(+), 10 deletions(-) rename api/envoy/extensions/filters/http/cache/{v3alpha => v3}/BUILD (100%) rename api/envoy/extensions/filters/http/cache/{v3alpha => v3}/cache.proto (97%) rename generated_api_shadow/envoy/extensions/filters/http/cache/{v3alpha => v3}/BUILD (100%) rename generated_api_shadow/envoy/extensions/filters/http/cache/{v3alpha => v3}/cache.proto (97%) diff --git a/api/BUILD b/api/BUILD index bdcf1d02b743e..4859faee746ce 100644 --- a/api/BUILD +++ b/api/BUILD @@ -127,7 +127,7 @@ proto_library( "//envoy/extensions/filters/common/fault/v3alpha:pkg", "//envoy/extensions/filters/http/adaptive_concurrency/v3alpha:pkg", "//envoy/extensions/filters/http/buffer/v3alpha:pkg", - "//envoy/extensions/filters/http/cache/v3alpha:pkg", + "//envoy/extensions/filters/http/cache/v3:pkg", "//envoy/extensions/filters/http/cors/v3alpha:pkg", "//envoy/extensions/filters/http/csrf/v3alpha:pkg", "//envoy/extensions/filters/http/dynamic_forward_proxy/v3alpha:pkg", diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index 52db83d974449..0bdee70d9015f 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -8,8 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; -option (udpa.annotations.file_migrate).move_to_package = - "envoy.extensions.filters.http.cache.v3alpha"; +option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3"; // [#protodoc-title: HTTP Cache Filter] // HTTP Cache Filter :ref:`overview `. diff --git a/api/envoy/extensions/filters/http/cache/v3alpha/BUILD b/api/envoy/extensions/filters/http/cache/v3/BUILD similarity index 100% rename from api/envoy/extensions/filters/http/cache/v3alpha/BUILD rename to api/envoy/extensions/filters/http/cache/v3/BUILD diff --git a/api/envoy/extensions/filters/http/cache/v3alpha/cache.proto b/api/envoy/extensions/filters/http/cache/v3/cache.proto similarity index 97% rename from api/envoy/extensions/filters/http/cache/v3alpha/cache.proto rename to api/envoy/extensions/filters/http/cache/v3/cache.proto index ad723ba60f0f4..085e88f845801 100644 --- a/api/envoy/extensions/filters/http/cache/v3alpha/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3/cache.proto @@ -1,12 +1,12 @@ syntax = "proto3"; -package envoy.extensions.filters.http.cache.v3alpha; +package envoy.extensions.filters.http.cache.v3; import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; -option java_package = "io.envoyproxy.envoy.extensions.filters.http.cache.v3alpha"; +option java_package = "io.envoyproxy.envoy.extensions.filters.http.cache.v3"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; diff --git a/generated_api_shadow/BUILD b/generated_api_shadow/BUILD index bdcf1d02b743e..4859faee746ce 100644 --- a/generated_api_shadow/BUILD +++ b/generated_api_shadow/BUILD @@ -127,7 +127,7 @@ proto_library( "//envoy/extensions/filters/common/fault/v3alpha:pkg", "//envoy/extensions/filters/http/adaptive_concurrency/v3alpha:pkg", "//envoy/extensions/filters/http/buffer/v3alpha:pkg", - "//envoy/extensions/filters/http/cache/v3alpha:pkg", + "//envoy/extensions/filters/http/cache/v3:pkg", "//envoy/extensions/filters/http/cors/v3alpha:pkg", "//envoy/extensions/filters/http/csrf/v3alpha:pkg", "//envoy/extensions/filters/http/dynamic_forward_proxy/v3alpha:pkg", diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto index 52db83d974449..0bdee70d9015f 100644 --- a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto @@ -8,8 +8,7 @@ import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; -option (udpa.annotations.file_migrate).move_to_package = - "envoy.extensions.filters.http.cache.v3alpha"; +option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3"; // [#protodoc-title: HTTP Cache Filter] // HTTP Cache Filter :ref:`overview `. diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/BUILD b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD similarity index 100% rename from generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/BUILD rename to generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto similarity index 97% rename from generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto rename to generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto index ad723ba60f0f4..085e88f845801 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3alpha/cache.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto @@ -1,12 +1,12 @@ syntax = "proto3"; -package envoy.extensions.filters.http.cache.v3alpha; +package envoy.extensions.filters.http.cache.v3; import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; -option java_package = "io.envoyproxy.envoy.extensions.filters.http.cache.v3alpha"; +option java_package = "io.envoyproxy.envoy.extensions.filters.http.cache.v3"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; From dc05534f3d0d88d45061eded6afb19bd3f318755 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 16 Jan 2020 16:25:19 -0800 Subject: [PATCH 081/109] Use api.v2.route.QueryParameterMatcher to specify query params for keys. Signed-off-by: Todd Greer --- api/envoy/config/filter/http/cache/v2/BUILD | 5 +++- .../config/filter/http/cache/v2/cache.proto | 25 +++++++---------- .../extensions/filters/http/cache/v3/BUILD | 1 + .../filters/http/cache/v3/cache.proto | 28 +++++++------------ .../envoy/config/filter/http/cache/v2/BUILD | 5 +++- .../config/filter/http/cache/v2/cache.proto | 25 +++++++---------- .../extensions/filters/http/cache/v3/BUILD | 1 + .../filters/http/cache/v3/cache.proto | 28 +++++++------------ 8 files changed, 50 insertions(+), 68 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v2/BUILD b/api/envoy/config/filter/http/cache/v2/BUILD index ef3541ebcb1df..fcb83ae10999b 100644 --- a/api/envoy/config/filter/http/cache/v2/BUILD +++ b/api/envoy/config/filter/http/cache/v2/BUILD @@ -5,5 +5,8 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], + deps = [ + "//envoy/api/v2/route:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], ) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index 0bdee70d9015f..73135eabb0611 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.config.filter.http.cache.v2; +import "envoy/api/v2/route/route_components.proto"; + import "udpa/annotations/migrate.proto"; import "validate/validate.proto"; @@ -14,11 +16,6 @@ option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filte // HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] message CacheConfig { - // List of query params. - message QueryParams { - repeated string query_string_keys = 1; - } - // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { // If true, exclude the URL scheme from the cache key. Set to true if your origins always @@ -29,17 +26,15 @@ message CacheConfig { // ever depend on host. bool exclude_host = 2; - oneof query_string_keys { - // If set, only query params in this list are included in the cache - // key. Any other query params will not affect cache lookup. If set to an - // empty list, the query string will be entirely ignored in cache lookups. - QueryParams query_string_keys_included = 3; + // If query_parameters_included is nonempty, only query parameters matched + // by one or more of its matchers are included in the cache key. Any other + // query params will not affect cache lookup. + repeated api.v2.route.QueryParameterMatcher query_parameters_included = 3; - // If set, query params in this list are excluded from the cache key, and - // will not affect cache lookup. Setting this to an empty list is - // equivalent to not setting it (but is less efficient). - QueryParams query_string_keys_excluded = 4; - } + // If query_parameters_excluded is nonempty, query parameters matched by one + // or more of its matchers are excluded from the cache key (even if also + // matched by query_parameters_included), and will not affect cache lookup. + repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; } // Name of cache implementation to use. diff --git a/api/envoy/extensions/filters/http/cache/v3/BUILD b/api/envoy/extensions/filters/http/cache/v3/BUILD index 45b9b5301b8d9..32620032c2b00 100644 --- a/api/envoy/extensions/filters/http/cache/v3/BUILD +++ b/api/envoy/extensions/filters/http/cache/v3/BUILD @@ -7,6 +7,7 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/config/filter/http/cache/v2:pkg", + "//envoy/config/route/v3alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/extensions/filters/http/cache/v3/cache.proto b/api/envoy/extensions/filters/http/cache/v3/cache.proto index 085e88f845801..163f2eb758231 100644 --- a/api/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3/cache.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.extensions.filters.http.cache.v3; +import "envoy/config/route/v3alpha/route_components.proto"; + import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; @@ -17,14 +19,6 @@ message CacheConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.cache.v2.CacheConfig"; - // List of query params. - message QueryParams { - option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v2.CacheConfig.QueryParams"; - - repeated string query_string_keys = 1; - } - // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { option (udpa.annotations.versioning).previous_message_type = @@ -38,17 +32,15 @@ message CacheConfig { // ever depend on host. bool exclude_host = 2; - oneof query_string_keys { - // If set, only query params in this list are included in the cache - // key. Any other query params will not affect cache lookup. If set to an - // empty list, the query string will be entirely ignored in cache lookups. - QueryParams query_string_keys_included = 3; + // If query_parameters_included is nonempty, only query parameters matched + // by one or more of its matchers are included in the cache key. Any other + // query params will not affect cache lookup. + repeated config.route.v3alpha.QueryParameterMatcher query_parameters_included = 3; - // If set, query params in this list are excluded from the cache key, and - // will not affect cache lookup. Setting this to an empty list is - // equivalent to not setting it (but is less efficient). - QueryParams query_string_keys_excluded = 4; - } + // If query_parameters_excluded is nonempty, query parameters matched by one + // or more of its matchers are excluded from the cache key (even if also + // matched by query_parameters_included), and will not affect cache lookup. + repeated config.route.v3alpha.QueryParameterMatcher query_parameters_excluded = 4; } // Name of cache implementation to use. diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD b/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD index ef3541ebcb1df..fcb83ae10999b 100644 --- a/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD @@ -5,5 +5,8 @@ load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") licenses(["notice"]) # Apache 2 api_proto_package( - deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], + deps = [ + "//envoy/api/v2/route:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], ) diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto index 0bdee70d9015f..73135eabb0611 100644 --- a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.config.filter.http.cache.v2; +import "envoy/api/v2/route/route_components.proto"; + import "udpa/annotations/migrate.proto"; import "validate/validate.proto"; @@ -14,11 +16,6 @@ option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filte // HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] message CacheConfig { - // List of query params. - message QueryParams { - repeated string query_string_keys = 1; - } - // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { // If true, exclude the URL scheme from the cache key. Set to true if your origins always @@ -29,17 +26,15 @@ message CacheConfig { // ever depend on host. bool exclude_host = 2; - oneof query_string_keys { - // If set, only query params in this list are included in the cache - // key. Any other query params will not affect cache lookup. If set to an - // empty list, the query string will be entirely ignored in cache lookups. - QueryParams query_string_keys_included = 3; + // If query_parameters_included is nonempty, only query parameters matched + // by one or more of its matchers are included in the cache key. Any other + // query params will not affect cache lookup. + repeated api.v2.route.QueryParameterMatcher query_parameters_included = 3; - // If set, query params in this list are excluded from the cache key, and - // will not affect cache lookup. Setting this to an empty list is - // equivalent to not setting it (but is less efficient). - QueryParams query_string_keys_excluded = 4; - } + // If query_parameters_excluded is nonempty, query parameters matched by one + // or more of its matchers are excluded from the cache key (even if also + // matched by query_parameters_included), and will not affect cache lookup. + repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; } // Name of cache implementation to use. diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD index 45b9b5301b8d9..32620032c2b00 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD @@ -7,6 +7,7 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/config/filter/http/cache/v2:pkg", + "//envoy/config/route/v3alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto index 085e88f845801..163f2eb758231 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.extensions.filters.http.cache.v3; +import "envoy/config/route/v3alpha/route_components.proto"; + import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; @@ -17,14 +19,6 @@ message CacheConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.cache.v2.CacheConfig"; - // List of query params. - message QueryParams { - option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v2.CacheConfig.QueryParams"; - - repeated string query_string_keys = 1; - } - // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { option (udpa.annotations.versioning).previous_message_type = @@ -38,17 +32,15 @@ message CacheConfig { // ever depend on host. bool exclude_host = 2; - oneof query_string_keys { - // If set, only query params in this list are included in the cache - // key. Any other query params will not affect cache lookup. If set to an - // empty list, the query string will be entirely ignored in cache lookups. - QueryParams query_string_keys_included = 3; + // If query_parameters_included is nonempty, only query parameters matched + // by one or more of its matchers are included in the cache key. Any other + // query params will not affect cache lookup. + repeated config.route.v3alpha.QueryParameterMatcher query_parameters_included = 3; - // If set, query params in this list are excluded from the cache key, and - // will not affect cache lookup. Setting this to an empty list is - // equivalent to not setting it (but is less efficient). - QueryParams query_string_keys_excluded = 4; - } + // If query_parameters_excluded is nonempty, query parameters matched by one + // or more of its matchers are excluded from the cache key (even if also + // matched by query_parameters_included), and will not affect cache lookup. + repeated config.route.v3alpha.QueryParameterMatcher query_parameters_excluded = 4; } // Name of cache implementation to use. From 118191f374b3c07011a7dc124b886dbb86cd1ffd Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 16 Jan 2020 16:50:39 -0800 Subject: [PATCH 082/109] Use StringMatcher for allowed_vary_headers. Signed-off-by: Todd Greer --- api/envoy/config/filter/http/cache/v2/BUILD | 1 + api/envoy/config/filter/http/cache/v2/cache.proto | 12 +++++++----- api/envoy/extensions/filters/http/cache/v3/BUILD | 1 + .../extensions/filters/http/cache/v3/cache.proto | 12 +++++++----- .../envoy/config/filter/http/cache/v2/BUILD | 1 + .../envoy/config/filter/http/cache/v2/cache.proto | 12 +++++++----- .../envoy/extensions/filters/http/cache/v3/BUILD | 1 + .../extensions/filters/http/cache/v3/cache.proto | 12 +++++++----- 8 files changed, 32 insertions(+), 20 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v2/BUILD b/api/envoy/config/filter/http/cache/v2/BUILD index fcb83ae10999b..87746bf482e03 100644 --- a/api/envoy/config/filter/http/cache/v2/BUILD +++ b/api/envoy/config/filter/http/cache/v2/BUILD @@ -7,6 +7,7 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/api/v2/route:pkg", + "//envoy/type/matcher:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index 73135eabb0611..9a97835fcb37d 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.config.filter.http.cache.v2; import "envoy/api/v2/route/route_components.proto"; +import "envoy/type/matcher/string.proto"; import "udpa/annotations/migrate.proto"; import "validate/validate.proto"; @@ -42,11 +43,12 @@ message CacheConfig { // TODO(toddmgreer) implement vary headers // - // List of allowed Vary headers. These headers will be provided to caches in lookups, and the - // cache filter will only insert responses whose Vary headers (if any) are all in this list. For - // example, if an origin supplies "vary:user-agent" in a response, the response will only be - // inserted if "user-agent" is in this list. - repeated string allowed_vary_headers = 2; + // List of allowed Vary headers. Headers matched by allowed_vary_headers will + // be provided to caches in lookups, and the cache filter will only insert + // responses whose Vary headers (if any) are matched my a matcher in this + // list. If allowed_vary_headers is empty, responses with 'vary' headers will + // not be cached. + repeated type.matcher.StringMatcher allowed_vary_headers = 2; // TODO(toddmgreer) implement key customization // diff --git a/api/envoy/extensions/filters/http/cache/v3/BUILD b/api/envoy/extensions/filters/http/cache/v3/BUILD index 32620032c2b00..bc709054ec56a 100644 --- a/api/envoy/extensions/filters/http/cache/v3/BUILD +++ b/api/envoy/extensions/filters/http/cache/v3/BUILD @@ -8,6 +8,7 @@ api_proto_package( deps = [ "//envoy/config/filter/http/cache/v2:pkg", "//envoy/config/route/v3alpha:pkg", + "//envoy/type/matcher/v3alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/extensions/filters/http/cache/v3/cache.proto b/api/envoy/extensions/filters/http/cache/v3/cache.proto index 163f2eb758231..624df52aa0632 100644 --- a/api/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3/cache.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.extensions.filters.http.cache.v3; import "envoy/config/route/v3alpha/route_components.proto"; +import "envoy/type/matcher/v3alpha/string.proto"; import "udpa/annotations/versioning.proto"; @@ -48,11 +49,12 @@ message CacheConfig { // TODO(toddmgreer) implement vary headers // - // List of allowed Vary headers. These headers will be provided to caches in lookups, and the - // cache filter will only insert responses whose Vary headers (if any) are all in this list. For - // example, if an origin supplies "vary:user-agent" in a response, the response will only be - // inserted if "user-agent" is in this list. - repeated string allowed_vary_headers = 2; + // List of allowed Vary headers. Headers matched by allowed_vary_headers will + // be provided to caches in lookups, and the cache filter will only insert + // responses whose Vary headers (if any) are matched my a matcher in this + // list. If allowed_vary_headers is empty, responses with 'vary' headers will + // not be cached. + repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 2; // TODO(toddmgreer) implement key customization // diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD b/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD index fcb83ae10999b..87746bf482e03 100644 --- a/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD @@ -7,6 +7,7 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/api/v2/route:pkg", + "//envoy/type/matcher:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto index 73135eabb0611..9a97835fcb37d 100644 --- a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.config.filter.http.cache.v2; import "envoy/api/v2/route/route_components.proto"; +import "envoy/type/matcher/string.proto"; import "udpa/annotations/migrate.proto"; import "validate/validate.proto"; @@ -42,11 +43,12 @@ message CacheConfig { // TODO(toddmgreer) implement vary headers // - // List of allowed Vary headers. These headers will be provided to caches in lookups, and the - // cache filter will only insert responses whose Vary headers (if any) are all in this list. For - // example, if an origin supplies "vary:user-agent" in a response, the response will only be - // inserted if "user-agent" is in this list. - repeated string allowed_vary_headers = 2; + // List of allowed Vary headers. Headers matched by allowed_vary_headers will + // be provided to caches in lookups, and the cache filter will only insert + // responses whose Vary headers (if any) are matched my a matcher in this + // list. If allowed_vary_headers is empty, responses with 'vary' headers will + // not be cached. + repeated type.matcher.StringMatcher allowed_vary_headers = 2; // TODO(toddmgreer) implement key customization // diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD index 32620032c2b00..bc709054ec56a 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD @@ -8,6 +8,7 @@ api_proto_package( deps = [ "//envoy/config/filter/http/cache/v2:pkg", "//envoy/config/route/v3alpha:pkg", + "//envoy/type/matcher/v3alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto index 163f2eb758231..624df52aa0632 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.extensions.filters.http.cache.v3; import "envoy/config/route/v3alpha/route_components.proto"; +import "envoy/type/matcher/v3alpha/string.proto"; import "udpa/annotations/versioning.proto"; @@ -48,11 +49,12 @@ message CacheConfig { // TODO(toddmgreer) implement vary headers // - // List of allowed Vary headers. These headers will be provided to caches in lookups, and the - // cache filter will only insert responses whose Vary headers (if any) are all in this list. For - // example, if an origin supplies "vary:user-agent" in a response, the response will only be - // inserted if "user-agent" is in this list. - repeated string allowed_vary_headers = 2; + // List of allowed Vary headers. Headers matched by allowed_vary_headers will + // be provided to caches in lookups, and the cache filter will only insert + // responses whose Vary headers (if any) are matched my a matcher in this + // list. If allowed_vary_headers is empty, responses with 'vary' headers will + // not be cached. + repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 2; // TODO(toddmgreer) implement key customization // From a2a9ae6b90c719ac7790866e2a0df246f5f9e6c6 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 16 Jan 2020 18:50:30 -0800 Subject: [PATCH 083/109] Add an overview of writing a cache storage implementation Signed-off-by: Todd Greer --- .../http/http_filters/cache_filter.rst | 5 +- source/docs/cache_filter_flow.png | Bin 0 -> 90750 bytes source/docs/cache_filter_plugins.md | 48 ++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 source/docs/cache_filter_flow.png create mode 100644 source/docs/cache_filter_plugins.md diff --git a/docs/root/configuration/http/http_filters/cache_filter.rst b/docs/root/configuration/http/http_filters/cache_filter.rst index a675d71bf5a09..761a4f8a649f4 100644 --- a/docs/root/configuration/http/http_filters/cache_filter.rst +++ b/docs/root/configuration/http/http_filters/cache_filter.rst @@ -20,7 +20,9 @@ The only required configuration field is :ref:`name specify a valid cache storage implementation linked into your Envoy binary. Specifying 'SimpleHttpCache' will select a proof-of-concept implementation included in the Envoy source. More implementations can (and will) -be provided by implementing Envoy::Extensions::HttpFilters::Cache::HttpCache. +be provided by implementing Envoy::Extensions::HttpFilters::Cache::HttpCache. To +write a cache storage implementation, see `Writing Cache Filter +Implementations `_ The remaining configuration options control caching behavior and limits. By default, this filter will cache almost all responses that are considered @@ -34,6 +36,7 @@ date, age, expires, and vary. Responses with a 'vary' header will only be cached if the named headers are listed in :ref:`allowed_vary_headers ` + Status ------ * This filter *is* ready for developers to write cache storage plugins; please diff --git a/source/docs/cache_filter_flow.png b/source/docs/cache_filter_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..d1d254c5d9947f2c5482fda1706870da69d3bb31 GIT binary patch literal 90750 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 S*+?|-pOU;9tW?(I<^KoG;XK#? literal 0 HcmV?d00001 diff --git a/source/docs/cache_filter_plugins.md b/source/docs/cache_filter_plugins.md new file mode 100644 index 0000000000000..51798ccccd0d9 --- /dev/null +++ b/source/docs/cache_filter_plugins.md @@ -0,0 +1,48 @@ +### Overview + +The HTTP Cache Filter handles most of the complexity of HTTP caching semantics, +but delegates the actual storage of HTTP responses to implementations of the +HttpCache interface. These implementations can cover all points on the spectrum +of persistence, performance, and distribution, from local RAM caches to globally +distributed persistent caches. They can be fully custom caches, or +wrappers/adapters around local or remote open-source or proprietary caches. + +If you write a new cache storage implementation, please add it to the Envoy +repository if possible. This is the only way to make sure it stays up-to-date +with Envoy changes, and lets other developers contribute fixes and improvements. + +As you read this, also read the example implementation in `simple_http_cache.h/.cc`. + +You need to write implementations of four small interfaces: + +## HttpCache + * Example Implementation: `SimpleHttpCache` + * `HttpCache` represents an actual cache of responses stored somewhere. It provides methods to set up cache lookups and inserts, and to update the headers of cached responses. + +## HttpCacheFactory + * Example Implementation: `SimpleHttpCacheFactory` + * `HttpCacheFactory` does what it sounds like: it creates HttpCache implementations, based on a name that came from the cache filter's config. + +## LookupContext + * Example Implementation: `SimpleLookupContext` + * `LookupContext` represents a single lookup operation; this is a good place to store whatever per-lookup state you may need during the lookup process. + +## InsertContext + * Example Implementation: `SimpleInsertContext` + * `LookupContext` represents a single insert operation; this is a good place to store whatever per-insert state you may need during the lookup process. + +### Flow + +To initiate a lookup on an HttpCache implementation, the cache filter calls +`HttpCache::makeLookupContext`, which should return a `LookupContextPtr`. The cache filter will +call `LookupContext::getHeaders` to find out if there's a cached response. If +a result is found, the `LookupContext` implementation must call +`LookupRequest::makeLookupResult`, and pass the result to the callback. + +The cache filter will then make a series of `getBody` requests followed by `getTrailers` (if needed). + +If the `LookupResult` in the callback indicates that a response wasn't found, the cache filter will let the request pass upstream. If the origin replies with a cacheable response, the filter will call `HttpCache::makeInsertContext`, and use its methods to insert the response. + +The following diagram shows a potential GET request for a 5M resource that is present and fresh in the cache, with no trailers. In the case of a synchronous in-memory cache, this all happens within `CacheFilter::decodeHeaders`. Solid arrows denote synchronous function calls, while dashed arrows denote asynchronous function calls or their callbacks. Objects that are part of the cache implementation (`HttpCache` and `LookupContext`) are blue. (Other objects are part of the cache filter, or of Envoy. + +![Cache Filter flow diagram](cache_filter_flow.png) From 0ce2a6444694be9df0e5419fb31e629a9d844ae2 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 17 Jan 2020 13:52:17 -0800 Subject: [PATCH 084/109] Highlight field names in comments Signed-off-by: Todd Greer --- api/envoy/config/filter/http/cache/v2/cache.proto | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index 9a97835fcb37d..d93b84fbdb93e 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -27,14 +27,14 @@ message CacheConfig { // ever depend on host. bool exclude_host = 2; - // If query_parameters_included is nonempty, only query parameters matched + // If *query_parameters_included* is nonempty, only query parameters matched // by one or more of its matchers are included in the cache key. Any other // query params will not affect cache lookup. repeated api.v2.route.QueryParameterMatcher query_parameters_included = 3; - // If query_parameters_excluded is nonempty, query parameters matched by one + // If *query_parameters_excluded* is nonempty, query parameters matched by one // or more of its matchers are excluded from the cache key (even if also - // matched by query_parameters_included), and will not affect cache lookup. + // matched by *query_parameters_included*), and will not affect cache lookup. repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; } From dd0703f94accc88f396a122d77274a04001c7e10 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 17 Jan 2020 14:13:53 -0800 Subject: [PATCH 085/109] Comment out TODO comments. Signed-off-by: Todd Greer --- api/envoy/config/filter/http/cache/v2/cache.proto | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index d93b84fbdb93e..edf5639961846 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -41,7 +41,7 @@ message CacheConfig { // Name of cache implementation to use. string name = 1 [(validate.rules).string = {min_bytes: 1}]; - // TODO(toddmgreer) implement vary headers + // [#comment: ] // // List of allowed Vary headers. Headers matched by allowed_vary_headers will // be provided to caches in lookups, and the cache filter will only insert @@ -50,12 +50,12 @@ message CacheConfig { // not be cached. repeated type.matcher.StringMatcher allowed_vary_headers = 2; - // TODO(toddmgreer) implement key customization + // [#comment: ] // // Modifies cache key creation by restricting which parts of the URL are included. KeyCreatorParams key_creator_params = 3; - // TODO(toddmgreer) implement size limit + // [#comment: ] // // Max body size the cache filter will insert into a cache. uint32 max_body_bytes = 4; From 4304cbc1a491157809bf5321bbcf85b4b2402270 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 17 Jan 2020 14:47:15 -0800 Subject: [PATCH 086/109] Only create date format strings once. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/http_cache_utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index aa69379f0473e..6976b38f04111 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -173,7 +173,7 @@ SystemTime Utils::httpTime(const Http::HeaderEntry* header_entry) { // Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate // Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format - const std::array rfc7231_date_formats = { + static const std::array& rfc7231_date_formats = *new std::array{ "%a, %d %b %Y %H:%M:%S GMT", "%A, %d-%b-%y %H:%M:%S GMT", "%a %b %e %H:%M:%S %Y"}; for (const std::string& format : rfc7231_date_formats) { if (absl::ParseTime(format, input, &time, nullptr)) { From 05c0566f622d5b664c35d0cf8b8233c07fea22c2 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 17 Jan 2020 14:58:05 -0800 Subject: [PATCH 087/109] simplify decl w/ auto Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/http_cache_utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/extensions/filters/http/cache/http_cache_utils.cc b/source/extensions/filters/http/cache/http_cache_utils.cc index 6976b38f04111..6eb8dff24d777 100644 --- a/source/extensions/filters/http/cache/http_cache_utils.cc +++ b/source/extensions/filters/http/cache/http_cache_utils.cc @@ -173,7 +173,7 @@ SystemTime Utils::httpTime(const Http::HeaderEntry* header_entry) { // Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate // Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format - static const std::array& rfc7231_date_formats = *new std::array{ + static const auto& rfc7231_date_formats = *new std::array{ "%a, %d %b %Y %H:%M:%S GMT", "%A, %d-%b-%y %H:%M:%S GMT", "%a %b %e %H:%M:%S %Y"}; for (const std::string& format : rfc7231_date_formats) { if (absl::ParseTime(format, input, &time, nullptr)) { From 1bc592864b131843441b63903a48cf72c54c10d3 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 21 Jan 2020 13:47:42 -0800 Subject: [PATCH 088/109] Minor doc improvements; use reverse DNS for cache storage implementations. Signed-off-by: Todd Greer --- .../config/filter/http/cache/v2/cache.proto | 3 ++- .../http/http_filters/cache_filter.rst | 16 ++++++++-------- .../extensions/filters/http/cache/http_cache.h | 1 + .../filters/http/cache/simple_http_cache.cc | 4 ++-- .../filters/http/cache/cache_filter_test.cc | 4 ++-- .../extensions/filters/http/cache/config_test.cc | 2 +- .../filters/http/cache/simple_http_cache_test.cc | 4 ++-- 7 files changed, 18 insertions(+), 16 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index edf5639961846..cdb184f2e1f67 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -38,7 +38,8 @@ message CacheConfig { repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; } - // Name of cache implementation to use. + // Name of cache implementation to use, as specified in the intended HttpCacheFactory + // implementation. Cache names should use reverse DNS format, though this is not enforced. string name = 1 [(validate.rules).string = {min_bytes: 1}]; // [#comment: ] diff --git a/docs/root/configuration/http/http_filters/cache_filter.rst b/docs/root/configuration/http/http_filters/cache_filter.rst index 761a4f8a649f4..7baca6cc9d128 100644 --- a/docs/root/configuration/http/http_filters/cache_filter.rst +++ b/docs/root/configuration/http/http_filters/cache_filter.rst @@ -4,11 +4,11 @@ HTTP Cache Filter ================= **Work in Progress--not ready for deployment** -HTTP caching can improve system throughput and latency and reduce network and -backend loads when the same content is requested multiple times. Caching is +HTTP caching can improve system throughput, latency, and network/backend load +levels when the same content is requested multiple times. Caching is particularly valuable for edge proxies and browser-based traffic, which typically include many cacheable static resources, but it can be useful any time -there is enough repeatedly served content. +there is enough repeatedly served cacheable content. Configuration ------------- @@ -18,7 +18,7 @@ Configuration The only required configuration field is :ref:`name `, which must specify a valid cache storage implementation linked into your Envoy -binary. Specifying 'SimpleHttpCache' will select a proof-of-concept +binary. Specifying 'envoy.extensions.http.cache.simple' will select a proof-of-concept implementation included in the Envoy source. More implementations can (and will) be provided by implementing Envoy::Extensions::HttpFilters::Cache::HttpCache. To write a cache storage implementation, see `Writing Cache Filter @@ -27,12 +27,12 @@ Implementations `_, with handling -of conditional (`RFC7232 `_), and range +of conditional (`RFC7232 `_), and *range* (`RFC7233 `_) requests. Those RFC define which request methods and response codes are cacheable, subject to the -cache-related headers they also define: cache-control, range, if-match, -if-none-match, if-modified-since, if-unmodified-since, if-range, authorization, -date, age, expires, and vary. Responses with a 'vary' header will only be cached +cache-related headers they also define: *cache-control*, *range*, *if-match*, +*if-none-match*, *if-modified-since*, *if-unmodified-since*, *if-range*, *authorization*, +*date*, *age*, *expires*, and *vary*. Responses with a *vary* header will only be cached if the named headers are listed in :ref:`allowed_vary_headers ` diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 68fa0bc8ecce3..42fd0ab63c79b 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -299,6 +299,7 @@ class HttpCache { // Factory interface for cache implementations to implement and register. class HttpCacheFactory { public: + // name should be in reverse DNS format, though this is not enforced. explicit HttpCacheFactory(std::string name) : name_(std::move(name)) {} const std::string& name() const { return name_; } static std::string category() { return "http_cache_factory"; } diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index e9081265517e9..c70ec4ab7867d 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -124,13 +124,13 @@ InsertContextPtr SimpleHttpCache::makeInsertContext(LookupContextPtr&& lookup_co CacheInfo SimpleHttpCache::cacheInfo() const { CacheInfo cache_info; - cache_info.name_ = "SimpleHttpCache"; + cache_info.name_ = "envoy.extensions.http.cache.simple"; return cache_info; } class SimpleHttpCacheFactory : public HttpCacheFactory { public: - SimpleHttpCacheFactory() : HttpCacheFactory("SimpleHttpCache") {} + SimpleHttpCacheFactory() : HttpCacheFactory("envoy.extensions.http.cache.simple") {} HttpCache& getCache() override { return cache_; } private: diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index b67b2223e3b26..18487c7905748 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -43,7 +43,7 @@ TEST_F(CacheFilterTest, ImmediateHitNoBody) { ON_CALL(context_.dispatcher_, post(_)).WillByDefault(::testing::InvokeArgument<0>()); // Create filter for request 1 - config_.set_name("SimpleHttpCache"); + config_.set_name("envoy.extensions.http.cache.simple"); CacheFilterSharedPtr filter = makeFilter(); ASSERT_TRUE(filter); @@ -78,7 +78,7 @@ TEST_F(CacheFilterTest, ImmediateHitBody) { ON_CALL(context_.dispatcher_, post(_)).WillByDefault(::testing::InvokeArgument<0>()); // Create filter for request 1 - config_.set_name("SimpleHttpCache"); + config_.set_name("envoy.extensions.http.cache.simple"); CacheFilterSharedPtr filter = makeFilter(); ASSERT_TRUE(filter); diff --git a/test/extensions/filters/http/cache/config_test.cc b/test/extensions/filters/http/cache/config_test.cc index 1ca960fce0148..34e78514abb9f 100644 --- a/test/extensions/filters/http/cache/config_test.cc +++ b/test/extensions/filters/http/cache/config_test.cc @@ -21,7 +21,7 @@ class CacheFilterFactoryTest : public ::testing::Test { }; TEST_F(CacheFilterFactoryTest, Basic) { - config_.set_name("SimpleHttpCache"); + config_.set_name("envoy.extensions.http.cache.simple"); Http::FilterFactoryCb cb = factory_.createFilterFactoryFromProto(config_, "stats", context_); Http::StreamFilterSharedPtr filter; EXPECT_CALL(filter_callback_, addStreamFilter(_)).WillOnce(::testing::SaveArg<0>(&filter)); diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index c8632f0b2575c..abafc2459f92f 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -206,9 +206,9 @@ TEST_F(SimpleHttpCacheTest, StreamingPut) { TEST(Registration, GetFactory) { HttpCacheFactory* factory = - Registry::FactoryRegistry::getFactory("SimpleHttpCache"); + Registry::FactoryRegistry::getFactory("envoy.extensions.http.cache.simple"); ASSERT_NE(factory, nullptr); - EXPECT_EQ(factory->getCache().cacheInfo().name_, "SimpleHttpCache"); + EXPECT_EQ(factory->getCache().cacheInfo().name_, "envoy.extensions.http.cache.simple"); } } // namespace From 6885b82d034a701e42d2295d71837d0119671f43 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 21 Jan 2020 13:51:16 -0800 Subject: [PATCH 089/109] *-highlight "vary" in comments. Signed-off-by: Todd Greer --- api/envoy/config/filter/http/cache/v2/cache.proto | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index cdb184f2e1f67..3245d6aacacc5 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -42,12 +42,12 @@ message CacheConfig { // implementation. Cache names should use reverse DNS format, though this is not enforced. string name = 1 [(validate.rules).string = {min_bytes: 1}]; - // [#comment: ] + // [#comment: ] // - // List of allowed Vary headers. Headers matched by allowed_vary_headers will + // List of allowed *Vary* headers. Headers matched by allowed_vary_headers will // be provided to caches in lookups, and the cache filter will only insert - // responses whose Vary headers (if any) are matched my a matcher in this - // list. If allowed_vary_headers is empty, responses with 'vary' headers will + // responses whose *Vary* headers (if any) are matched my a matcher in this + // list. If allowed_vary_headers is empty, responses with *vary* headers will // not be cached. repeated type.matcher.StringMatcher allowed_vary_headers = 2; From 3f128e3f587803ccc2a48fd8b83006f961fa1d80 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 21 Jan 2020 15:04:00 -0800 Subject: [PATCH 090/109] Correct missed SimpleHttpCache to envoy.extensions.http.cache.simple change. Signed-off-by: Todd Greer --- .../filters/http/cache/cache_filter_integration_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/extensions/filters/http/cache/cache_filter_integration_test.cc b/test/extensions/filters/http/cache/cache_filter_integration_test.cc index 22522028111ea..4444d51d492f1 100644 --- a/test/extensions/filters/http/cache/cache_filter_integration_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_integration_test.cc @@ -29,7 +29,7 @@ class CacheIntegrationTest : public Event::TestUsingSimulatedTime, name: envoy.filters.http.cache typed_config: "@type": type.googleapis.com/envoy.config.filter.http.cache.v2.CacheConfig - name: SimpleHttpCache + name: envoy.extensions.http.cache.simple )EOF"}; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; }; From 9b1c68af9b9fd7317602c9d6a972cd92ece76267 Mon Sep 17 00:00:00 2001 From: toddmgreer Date: Tue, 21 Jan 2020 18:38:37 -0800 Subject: [PATCH 091/109] Update source/extensions/filters/http/cache/http_cache.h Co-Authored-By: Derek Argueta Signed-off-by: toddmgreer --- source/extensions/filters/http/cache/http_cache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 42fd0ab63c79b..66deb34ebbdfc 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -310,7 +310,7 @@ class HttpCacheFactory { virtual ~HttpCacheFactory() = default; private: - std::string name_; + const std::string name_; }; using HttpCacheFactoryPtr = std::unique_ptr; } // namespace Cache From 4585b1466615b145e5f76e753a8ad4d80eb95ec2 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Tue, 21 Jan 2020 18:54:50 -0800 Subject: [PATCH 092/109] Addressing review comments from mattklien123 Signed-off-by: Todd Greer --- .../config/filter/http/cache/v2/cache.proto | 16 +++++++++++----- .../http/http_filters/cache_filter.rst | 6 +++--- .../filters/http/cache/cache_filter.cc | 1 + 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index 3245d6aacacc5..0add252bd9e9f 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -16,7 +16,9 @@ option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filte // [#protodoc-title: HTTP Cache Filter] // HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] + message CacheConfig { + // [#not-implemented-hide:] // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { // If true, exclude the URL scheme from the cache key. Set to true if your origins always @@ -42,22 +44,26 @@ message CacheConfig { // implementation. Cache names should use reverse DNS format, though this is not enforced. string name = 1 [(validate.rules).string = {min_bytes: 1}]; - // [#comment: ] + // [#not-implemented-hide:] + // // // List of allowed *Vary* headers. Headers matched by allowed_vary_headers will // be provided to caches in lookups, and the cache filter will only insert - // responses whose *Vary* headers (if any) are matched my a matcher in this + // responses whose *Vary* headers (if any) are matched by a matcher in this // list. If allowed_vary_headers is empty, responses with *vary* headers will // not be cached. repeated type.matcher.StringMatcher allowed_vary_headers = 2; - // [#comment: ] + // [#not-implemented-hide:] + // // // Modifies cache key creation by restricting which parts of the URL are included. KeyCreatorParams key_creator_params = 3; - // [#comment: ] + // [#not-implemented-hide:] + // // - // Max body size the cache filter will insert into a cache. + // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache + // storage implementation may have its own limit beyond which it will reject insertions). uint32 max_body_bytes = 4; } diff --git a/docs/root/configuration/http/http_filters/cache_filter.rst b/docs/root/configuration/http/http_filters/cache_filter.rst index 7baca6cc9d128..232ae6f8dfc8e 100644 --- a/docs/root/configuration/http/http_filters/cache_filter.rst +++ b/docs/root/configuration/http/http_filters/cache_filter.rst @@ -2,7 +2,7 @@ HTTP Cache Filter ================= -**Work in Progress--not ready for deployment** +.. attention:: Work in Progress--not ready for deployment HTTP caching can improve system throughput, latency, and network/backend load levels when the same content is requested multiple times. Caching is @@ -21,8 +21,8 @@ specify a valid cache storage implementation linked into your Envoy binary. Specifying 'envoy.extensions.http.cache.simple' will select a proof-of-concept implementation included in the Envoy source. More implementations can (and will) be provided by implementing Envoy::Extensions::HttpFilters::Cache::HttpCache. To -write a cache storage implementation, see `Writing Cache Filter -Implementations `_ +write a cache storage implementation, see :repo:`Writing Cache Filter +Implementations ` The remaining configuration options control caching behavior and limits. By default, this filter will cache almost all responses that are considered diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index c2c1007a91648..1741e6339faad 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -154,6 +154,7 @@ void CacheFilter::onBodyAsync(const CacheFilterSharedPtr& self, Buffer::Instance self->post([self, body = body.release()] { self->onBody(absl::WrapUnique(body)); }); } +// TODO(toddmgreer): Handle downstream backpressure. void CacheFilter::onBody(Buffer::InstancePtr&& body) { if (!lookup_) { return; From f09822b6aeff46170250b031abfc10366de42972 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 22 Jan 2020 14:44:38 -0800 Subject: [PATCH 093/109] Add typed_config, and use getAndCheckFactory. Signed-off-by: Todd Greer --- api/envoy/config/filter/http/cache/v2/cache.proto | 11 +++++++---- source/extensions/filters/http/cache/BUILD | 1 + source/extensions/filters/http/cache/cache_filter.cc | 10 ++-------- test/extensions/filters/http/cache/config_test.cc | 3 +-- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index 0add252bd9e9f..658e3d0b699d7 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -40,9 +40,12 @@ message CacheConfig { repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; } + // Config specific to the cache storage implementation. + google.protobuf.Any typed_config = 1; + // Name of cache implementation to use, as specified in the intended HttpCacheFactory // implementation. Cache names should use reverse DNS format, though this is not enforced. - string name = 1 [(validate.rules).string = {min_bytes: 1}]; + string name = 2; // [#not-implemented-hide:] // @@ -52,18 +55,18 @@ message CacheConfig { // responses whose *Vary* headers (if any) are matched by a matcher in this // list. If allowed_vary_headers is empty, responses with *vary* headers will // not be cached. - repeated type.matcher.StringMatcher allowed_vary_headers = 2; + repeated type.matcher.StringMatcher allowed_vary_headers = 3; // [#not-implemented-hide:] // // // Modifies cache key creation by restricting which parts of the URL are included. - KeyCreatorParams key_creator_params = 3; + KeyCreatorParams key_creator_params = 4; // [#not-implemented-hide:] // // // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache // storage implementation may have its own limit beyond which it will reject insertions). - uint32 max_body_bytes = 4; + uint32 max_body_bytes = 5; } diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index a82a40ec9faf7..e7f1375991a93 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -21,6 +21,7 @@ envoy_cc_library( "//include/envoy/registry", "//source/common/common:logger_lib", "//source/common/common:macros", + "//source/common/config:utility_lib", "//source/common/http:header_map_lib", "//source/common/http:headers_lib", "//source/common/protobuf", diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 1741e6339faad..ef9be987225e3 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -3,7 +3,7 @@ #include "envoy/registry/registry.h" #include "common/http/headers.h" - +#include "common/config/utility.h" #include "absl/memory/memory.h" #include "absl/strings/string_view.h" @@ -37,13 +37,7 @@ bool CacheFilter::isCacheableResponse(Http::HeaderMap& headers) { HttpCache& CacheFilter::getCache(const envoy::config::filter::http::cache::v2::CacheConfig& config) { - HttpCacheFactory* factory = - Registry::FactoryRegistry::getFactory(config.name()); - if (!factory) { - throw ProtoValidationException( - fmt::format("Didn't find a registered HttpCacheFactory for '{}'", config.name()), config); - } - return factory->getCache(); + return Config::Utility::getAndCheckFactory(config).getCache(); } CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v2::CacheConfig& config, diff --git a/test/extensions/filters/http/cache/config_test.cc b/test/extensions/filters/http/cache/config_test.cc index 34e78514abb9f..b3da47ac46841 100644 --- a/test/extensions/filters/http/cache/config_test.cc +++ b/test/extensions/filters/http/cache/config_test.cc @@ -37,8 +37,7 @@ TEST_F(CacheFilterFactoryTest, NoName) { TEST_F(CacheFilterFactoryTest, UnregisteredName) { config_.set_name("Wrong"); Http::FilterFactoryCb cb = factory_.createFilterFactoryFromProto(config_, "stats", context_); - EXPECT_THROW(cb(filter_callback_), ProtoValidationException); - ; + EXPECT_THROW(cb(filter_callback_), EnvoyException); } } // namespace } // namespace Cache From dbbcbd1d92a2f65af4109221bf7fd10a140dad78 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 22 Jan 2020 15:07:55 -0800 Subject: [PATCH 094/109] Pass config into cache storage implementations. Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/BUILD | 1 + source/extensions/filters/http/cache/cache_filter.cc | 5 +++-- source/extensions/filters/http/cache/http_cache.h | 4 +++- source/extensions/filters/http/cache/simple_http_cache.cc | 4 +++- test/extensions/filters/http/cache/simple_http_cache_test.cc | 3 ++- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index e7f1375991a93..384c3b39f632e 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -66,6 +66,7 @@ envoy_cc_library( "//source/common/common:assert_lib", "//source/common/http:headers_lib", "//source/common/protobuf:utility_lib", + "@envoy_api//envoy/config/filter/http/cache/v2:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index ef9be987225e3..156c0d2991902 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -2,8 +2,9 @@ #include "envoy/registry/registry.h" -#include "common/http/headers.h" #include "common/config/utility.h" +#include "common/http/headers.h" + #include "absl/memory/memory.h" #include "absl/strings/string_view.h" @@ -37,7 +38,7 @@ bool CacheFilter::isCacheableResponse(Http::HeaderMap& headers) { HttpCache& CacheFilter::getCache(const envoy::config::filter::http::cache::v2::CacheConfig& config) { - return Config::Utility::getAndCheckFactory(config).getCache(); + return Config::Utility::getAndCheckFactory(config).getCache(config); } CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v2::CacheConfig& config, diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 66deb34ebbdfc..d48e266c29980 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -6,6 +6,7 @@ #include "envoy/buffer/buffer.h" #include "envoy/common/time.h" +#include "envoy/config/filter/http/cache/v2/cache.pb.h" #include "envoy/http/header_map.h" #include "common/common/assert.h" @@ -306,7 +307,8 @@ class HttpCacheFactory { // Returns an HttpCache that will remain valid indefinitely (at least as long // as the calling CacheFilter). - virtual HttpCache& getCache() PURE; + virtual HttpCache& + getCache(const envoy::config::filter::http::cache::v2::CacheConfig& config) PURE; virtual ~HttpCacheFactory() = default; private: diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index c70ec4ab7867d..e377130877290 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -131,7 +131,9 @@ CacheInfo SimpleHttpCache::cacheInfo() const { class SimpleHttpCacheFactory : public HttpCacheFactory { public: SimpleHttpCacheFactory() : HttpCacheFactory("envoy.extensions.http.cache.simple") {} - HttpCache& getCache() override { return cache_; } + HttpCache& getCache(const envoy::config::filter::http::cache::v2::CacheConfig&) override { + return cache_; + } private: SimpleHttpCache cache_; diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index abafc2459f92f..39b6c5a1c6bd9 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -205,10 +205,11 @@ TEST_F(SimpleHttpCacheTest, StreamingPut) { } TEST(Registration, GetFactory) { + envoy::config::filter::http::cache::v2::CacheConfig config; HttpCacheFactory* factory = Registry::FactoryRegistry::getFactory("envoy.extensions.http.cache.simple"); ASSERT_NE(factory, nullptr); - EXPECT_EQ(factory->getCache().cacheInfo().name_, "envoy.extensions.http.cache.simple"); + EXPECT_EQ(factory->getCache(config).cacheInfo().name_, "envoy.extensions.http.cache.simple"); } } // namespace From 386c18b982c8e7f10c1ca75111b399d634ca6b84 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 22 Jan 2020 17:01:51 -0800 Subject: [PATCH 095/109] Put CacheConfig in v3 Signed-off-by: Todd Greer --- api/BUILD | 2 +- api/docs/BUILD | 2 +- .../config/filter/http/cache/{v2 => v3}/BUILD | 0 .../filter/http/cache/{v2 => v3}/cache.proto | 7 ++- .../extensions/filters/http/cache/v3/BUILD | 2 +- .../filters/http/cache/v3/cache.proto | 47 +++++++++----- generated_api_shadow/BUILD | 2 +- .../envoy/config/filter/http/cache/v2/BUILD | 13 ---- .../config/filter/http/cache/v2/cache.proto | 62 ------------------- .../extensions/filters/http/cache/v3/BUILD | 2 +- .../filters/http/cache/v3/cache.proto | 47 +++++++++----- source/extensions/filters/http/cache/BUILD | 6 +- .../filters/http/cache/cache_filter.cc | 4 +- .../filters/http/cache/cache_filter.h | 8 +-- .../extensions/filters/http/cache/config.cc | 4 +- source/extensions/filters/http/cache/config.h | 8 +-- .../filters/http/cache/http_cache.h | 6 +- .../filters/http/cache/simple_http_cache.cc | 2 +- .../cache/cache_filter_integration_test.cc | 2 +- .../filters/http/cache/cache_filter_test.cc | 2 +- .../filters/http/cache/config_test.cc | 2 +- .../http/cache/simple_http_cache_test.cc | 2 +- 22 files changed, 93 insertions(+), 139 deletions(-) rename api/envoy/config/filter/http/cache/{v2 => v3}/BUILD (100%) rename api/envoy/config/filter/http/cache/{v2 => v3}/cache.proto (96%) delete mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD delete mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto diff --git a/api/BUILD b/api/BUILD index 4859faee746ce..99c85eaa9cf7f 100644 --- a/api/BUILD +++ b/api/BUILD @@ -35,7 +35,7 @@ proto_library( "//envoy/config/filter/fault/v2:pkg", "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", - "//envoy/config/filter/http/cache/v2:pkg", + "//envoy/config/filter/http/cache/v3:pkg", "//envoy/config/filter/http/cors/v2:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", diff --git a/api/docs/BUILD b/api/docs/BUILD index 34055360967f5..993e44be51e8d 100644 --- a/api/docs/BUILD +++ b/api/docs/BUILD @@ -35,7 +35,7 @@ proto_library( "//envoy/config/filter/fault/v2:pkg", "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", - "//envoy/config/filter/http/cache/v2:pkg", + "//envoy/config/filter/http/cache/v3:pkg", "//envoy/config/filter/http/cors/v2:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", diff --git a/api/envoy/config/filter/http/cache/v2/BUILD b/api/envoy/config/filter/http/cache/v3/BUILD similarity index 100% rename from api/envoy/config/filter/http/cache/v2/BUILD rename to api/envoy/config/filter/http/cache/v3/BUILD diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v3/cache.proto similarity index 96% rename from api/envoy/config/filter/http/cache/v2/cache.proto rename to api/envoy/config/filter/http/cache/v3/cache.proto index 658e3d0b699d7..7f69e2f62bb45 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v3/cache.proto @@ -1,14 +1,16 @@ syntax = "proto3"; -package envoy.config.filter.http.cache.v2; +package envoy.config.filter.http.cache.v3; import "envoy/api/v2/route/route_components.proto"; import "envoy/type/matcher/string.proto"; +import "google/protobuf/any.proto"; + import "udpa/annotations/migrate.proto"; import "validate/validate.proto"; -option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2"; +option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v3"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3"; @@ -17,6 +19,7 @@ option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filte // HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] +// [#next-free-field: 6] message CacheConfig { // [#not-implemented-hide:] // Modifies cache key creation by restricting which parts of the URL are included. diff --git a/api/envoy/extensions/filters/http/cache/v3/BUILD b/api/envoy/extensions/filters/http/cache/v3/BUILD index bc709054ec56a..f0e18b47a9591 100644 --- a/api/envoy/extensions/filters/http/cache/v3/BUILD +++ b/api/envoy/extensions/filters/http/cache/v3/BUILD @@ -6,7 +6,7 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/config/filter/http/cache/v2:pkg", + "//envoy/config/filter/http/cache/v3:pkg", "//envoy/config/route/v3alpha:pkg", "//envoy/type/matcher/v3alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", diff --git a/api/envoy/extensions/filters/http/cache/v3/cache.proto b/api/envoy/extensions/filters/http/cache/v3/cache.proto index 624df52aa0632..ea6cafbf985c6 100644 --- a/api/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3/cache.proto @@ -5,6 +5,8 @@ package envoy.extensions.filters.http.cache.v3; import "envoy/config/route/v3alpha/route_components.proto"; import "envoy/type/matcher/v3alpha/string.proto"; +import "google/protobuf/any.proto"; + import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; @@ -16,14 +18,17 @@ option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] // HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] + +// [#next-free-field: 6] message CacheConfig { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v2.CacheConfig"; + "envoy.config.filter.http.cache.v3.CacheConfig"; + // [#not-implemented-hide:] // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v2.CacheConfig.KeyCreatorParams"; + "envoy.config.filter.http.cache.v3.CacheConfig.KeyCreatorParams"; // If true, exclude the URL scheme from the cache key. Set to true if your origins always // produce the same response for http and https requests. @@ -33,36 +38,44 @@ message CacheConfig { // ever depend on host. bool exclude_host = 2; - // If query_parameters_included is nonempty, only query parameters matched + // If *query_parameters_included* is nonempty, only query parameters matched // by one or more of its matchers are included in the cache key. Any other // query params will not affect cache lookup. repeated config.route.v3alpha.QueryParameterMatcher query_parameters_included = 3; - // If query_parameters_excluded is nonempty, query parameters matched by one + // If *query_parameters_excluded* is nonempty, query parameters matched by one // or more of its matchers are excluded from the cache key (even if also - // matched by query_parameters_included), and will not affect cache lookup. + // matched by *query_parameters_included*), and will not affect cache lookup. repeated config.route.v3alpha.QueryParameterMatcher query_parameters_excluded = 4; } - // Name of cache implementation to use. - string name = 1 [(validate.rules).string = {min_bytes: 1}]; + // Config specific to the cache storage implementation. + google.protobuf.Any typed_config = 1; + + // Name of cache implementation to use, as specified in the intended HttpCacheFactory + // implementation. Cache names should use reverse DNS format, though this is not enforced. + string name = 2; - // TODO(toddmgreer) implement vary headers + // [#not-implemented-hide:] + // // - // List of allowed Vary headers. Headers matched by allowed_vary_headers will + // List of allowed *Vary* headers. Headers matched by allowed_vary_headers will // be provided to caches in lookups, and the cache filter will only insert - // responses whose Vary headers (if any) are matched my a matcher in this - // list. If allowed_vary_headers is empty, responses with 'vary' headers will + // responses whose *Vary* headers (if any) are matched by a matcher in this + // list. If allowed_vary_headers is empty, responses with *vary* headers will // not be cached. - repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 2; + repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 3; - // TODO(toddmgreer) implement key customization + // [#not-implemented-hide:] + // // // Modifies cache key creation by restricting which parts of the URL are included. - KeyCreatorParams key_creator_params = 3; + KeyCreatorParams key_creator_params = 4; - // TODO(toddmgreer) implement size limit + // [#not-implemented-hide:] + // // - // Max body size the cache filter will insert into a cache. - uint32 max_body_bytes = 4; + // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache + // storage implementation may have its own limit beyond which it will reject insertions). + uint32 max_body_bytes = 5; } diff --git a/generated_api_shadow/BUILD b/generated_api_shadow/BUILD index 4859faee746ce..99c85eaa9cf7f 100644 --- a/generated_api_shadow/BUILD +++ b/generated_api_shadow/BUILD @@ -35,7 +35,7 @@ proto_library( "//envoy/config/filter/fault/v2:pkg", "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", - "//envoy/config/filter/http/cache/v2:pkg", + "//envoy/config/filter/http/cache/v3:pkg", "//envoy/config/filter/http/cors/v2:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD b/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD deleted file mode 100644 index 87746bf482e03..0000000000000 --- a/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD +++ /dev/null @@ -1,13 +0,0 @@ -# DO NOT EDIT. This file is generated by tools/proto_sync.py. - -load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") - -licenses(["notice"]) # Apache 2 - -api_proto_package( - deps = [ - "//envoy/api/v2/route:pkg", - "//envoy/type/matcher:pkg", - "@com_github_cncf_udpa//udpa/annotations:pkg", - ], -) diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto deleted file mode 100644 index 9a97835fcb37d..0000000000000 --- a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto +++ /dev/null @@ -1,62 +0,0 @@ -syntax = "proto3"; - -package envoy.config.filter.http.cache.v2; - -import "envoy/api/v2/route/route_components.proto"; -import "envoy/type/matcher/string.proto"; - -import "udpa/annotations/migrate.proto"; -import "validate/validate.proto"; - -option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2"; -option java_outer_classname = "CacheProto"; -option java_multiple_files = true; -option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3"; - -// [#protodoc-title: HTTP Cache Filter] -// HTTP Cache Filter :ref:`overview `. -// [#extension: envoy.filters.http.cache] -message CacheConfig { - // Modifies cache key creation by restricting which parts of the URL are included. - message KeyCreatorParams { - // If true, exclude the URL scheme from the cache key. Set to true if your origins always - // produce the same response for http and https requests. - bool exclude_scheme = 1; - - // If true, exclude the host from the cache key. Set to true if your origins' responses don't - // ever depend on host. - bool exclude_host = 2; - - // If query_parameters_included is nonempty, only query parameters matched - // by one or more of its matchers are included in the cache key. Any other - // query params will not affect cache lookup. - repeated api.v2.route.QueryParameterMatcher query_parameters_included = 3; - - // If query_parameters_excluded is nonempty, query parameters matched by one - // or more of its matchers are excluded from the cache key (even if also - // matched by query_parameters_included), and will not affect cache lookup. - repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; - } - - // Name of cache implementation to use. - string name = 1 [(validate.rules).string = {min_bytes: 1}]; - - // TODO(toddmgreer) implement vary headers - // - // List of allowed Vary headers. Headers matched by allowed_vary_headers will - // be provided to caches in lookups, and the cache filter will only insert - // responses whose Vary headers (if any) are matched my a matcher in this - // list. If allowed_vary_headers is empty, responses with 'vary' headers will - // not be cached. - repeated type.matcher.StringMatcher allowed_vary_headers = 2; - - // TODO(toddmgreer) implement key customization - // - // Modifies cache key creation by restricting which parts of the URL are included. - KeyCreatorParams key_creator_params = 3; - - // TODO(toddmgreer) implement size limit - // - // Max body size the cache filter will insert into a cache. - uint32 max_body_bytes = 4; -} diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD index bc709054ec56a..f0e18b47a9591 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD @@ -6,7 +6,7 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/config/filter/http/cache/v2:pkg", + "//envoy/config/filter/http/cache/v3:pkg", "//envoy/config/route/v3alpha:pkg", "//envoy/type/matcher/v3alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto index 624df52aa0632..ea6cafbf985c6 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto @@ -5,6 +5,8 @@ package envoy.extensions.filters.http.cache.v3; import "envoy/config/route/v3alpha/route_components.proto"; import "envoy/type/matcher/v3alpha/string.proto"; +import "google/protobuf/any.proto"; + import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; @@ -16,14 +18,17 @@ option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] // HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] + +// [#next-free-field: 6] message CacheConfig { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v2.CacheConfig"; + "envoy.config.filter.http.cache.v3.CacheConfig"; + // [#not-implemented-hide:] // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v2.CacheConfig.KeyCreatorParams"; + "envoy.config.filter.http.cache.v3.CacheConfig.KeyCreatorParams"; // If true, exclude the URL scheme from the cache key. Set to true if your origins always // produce the same response for http and https requests. @@ -33,36 +38,44 @@ message CacheConfig { // ever depend on host. bool exclude_host = 2; - // If query_parameters_included is nonempty, only query parameters matched + // If *query_parameters_included* is nonempty, only query parameters matched // by one or more of its matchers are included in the cache key. Any other // query params will not affect cache lookup. repeated config.route.v3alpha.QueryParameterMatcher query_parameters_included = 3; - // If query_parameters_excluded is nonempty, query parameters matched by one + // If *query_parameters_excluded* is nonempty, query parameters matched by one // or more of its matchers are excluded from the cache key (even if also - // matched by query_parameters_included), and will not affect cache lookup. + // matched by *query_parameters_included*), and will not affect cache lookup. repeated config.route.v3alpha.QueryParameterMatcher query_parameters_excluded = 4; } - // Name of cache implementation to use. - string name = 1 [(validate.rules).string = {min_bytes: 1}]; + // Config specific to the cache storage implementation. + google.protobuf.Any typed_config = 1; + + // Name of cache implementation to use, as specified in the intended HttpCacheFactory + // implementation. Cache names should use reverse DNS format, though this is not enforced. + string name = 2; - // TODO(toddmgreer) implement vary headers + // [#not-implemented-hide:] + // // - // List of allowed Vary headers. Headers matched by allowed_vary_headers will + // List of allowed *Vary* headers. Headers matched by allowed_vary_headers will // be provided to caches in lookups, and the cache filter will only insert - // responses whose Vary headers (if any) are matched my a matcher in this - // list. If allowed_vary_headers is empty, responses with 'vary' headers will + // responses whose *Vary* headers (if any) are matched by a matcher in this + // list. If allowed_vary_headers is empty, responses with *vary* headers will // not be cached. - repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 2; + repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 3; - // TODO(toddmgreer) implement key customization + // [#not-implemented-hide:] + // // // Modifies cache key creation by restricting which parts of the URL are included. - KeyCreatorParams key_creator_params = 3; + KeyCreatorParams key_creator_params = 4; - // TODO(toddmgreer) implement size limit + // [#not-implemented-hide:] + // // - // Max body size the cache filter will insert into a cache. - uint32 max_body_bytes = 4; + // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache + // storage implementation may have its own limit beyond which it will reject insertions). + uint32 max_body_bytes = 5; } diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index 384c3b39f632e..d3b4e9bd27081 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -26,7 +26,7 @@ envoy_cc_library( "//source/common/http:headers_lib", "//source/common/protobuf", "//source/extensions/filters/http/common:pass_through_filter_lib", - "@envoy_api//envoy/config/filter/http/cache/v2:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/cache/v3:pkg_cc_proto", ], ) @@ -44,7 +44,7 @@ envoy_cc_extension( "//include/envoy/stats:stats_interface", "//source/extensions/filters/http:well_known_names", "//source/extensions/filters/http/common:factory_base_lib", - "@envoy_api//envoy/config/filter/http/cache/v2:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/cache/v3:pkg_cc_proto", ], ) @@ -66,7 +66,7 @@ envoy_cc_library( "//source/common/common:assert_lib", "//source/common/http:headers_lib", "//source/common/protobuf:utility_lib", - "@envoy_api//envoy/config/filter/http/cache/v2:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/cache/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 156c0d2991902..ca0bffb440d90 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -37,11 +37,11 @@ bool CacheFilter::isCacheableResponse(Http::HeaderMap& headers) { } HttpCache& -CacheFilter::getCache(const envoy::config::filter::http::cache::v2::CacheConfig& config) { +CacheFilter::getCache(const envoy::config::filter::http::cache::v3::CacheConfig& config) { return Config::Utility::getAndCheckFactory(config).getCache(config); } -CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v2::CacheConfig& config, +CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v3::CacheConfig& config, const std::string&, Stats::Scope&, TimeSource& time_source) : time_source_(time_source), cache_(getCache(config)) {} diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index 8e68342b1edec..e5d7bd34ec62f 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -5,7 +5,7 @@ #include #include -#include "envoy/config/filter/http/cache/v2/cache.pb.h" +#include "envoy/config/filter/http/cache/v3/cache.pb.h" #include "common/common/logger.h" @@ -30,7 +30,7 @@ class CacheFilter : public Http::PassThroughFilter, public: // Throws ProtoValidationException if no registered HttpCacheFactory for config.name. static CacheFilterSharedPtr - make(const envoy::config::filter::http::cache::v2::CacheConfig& config, + make(const envoy::config::filter::http::cache::v3::CacheConfig& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source) { // Can't use make_shared due to private constructor. return std::shared_ptr(new CacheFilter(config, stats_prefix, scope, time_source)); @@ -47,7 +47,7 @@ class CacheFilter : public Http::PassThroughFilter, // Throws EnvoyException if no registered HttpCacheFactory for config.name. // Constructor is private to enforce enable_shared_from_this's requirement that this must be owned // by a shared_ptr. - CacheFilter(const envoy::config::filter::http::cache::v2::CacheConfig& config, + CacheFilter(const envoy::config::filter::http::cache::v3::CacheConfig& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source); void getBody(); @@ -64,7 +64,7 @@ class CacheFilter : public Http::PassThroughFilter, // These don't require private access, but are members per envoy convention. static bool isCacheableRequest(Http::HeaderMap& headers); static bool isCacheableResponse(Http::HeaderMap& headers); - static HttpCache& getCache(const envoy::config::filter::http::cache::v2::CacheConfig& config); + static HttpCache& getCache(const envoy::config::filter::http::cache::v3::CacheConfig& config); TimeSource& time_source_; HttpCache& cache_; diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index c043cf2cd4e1e..2a790b6548e18 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -3,7 +3,7 @@ #include #include "envoy/common/time.h" -#include "envoy/config/filter/http/cache/v2/cache.pb.validate.h" +#include "envoy/config/filter/http/cache/v3/cache.pb.validate.h" #include "envoy/registry/registry.h" #include "envoy/stats/scope.h" @@ -15,7 +15,7 @@ namespace HttpFilters { namespace Cache { Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v2::CacheConfig& config, + const envoy::config::filter::http::cache::v3::CacheConfig& config, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { return [config, stats_prefix, &context](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter( diff --git a/source/extensions/filters/http/cache/config.h b/source/extensions/filters/http/cache/config.h index d740194676925..36b99cf009932 100644 --- a/source/extensions/filters/http/cache/config.h +++ b/source/extensions/filters/http/cache/config.h @@ -1,7 +1,7 @@ #pragma once -#include "envoy/config/filter/http/cache/v2/cache.pb.h" -#include "envoy/config/filter/http/cache/v2/cache.pb.validate.h" +#include "envoy/config/filter/http/cache/v3/cache.pb.h" +#include "envoy/config/filter/http/cache/v3/cache.pb.validate.h" #include "extensions/filters/http/cache/cache_filter.h" #include "extensions/filters/http/common/factory_base.h" @@ -13,13 +13,13 @@ namespace HttpFilters { namespace Cache { class CacheFilterFactory - : public Common::FactoryBase { + : public Common::FactoryBase { public: CacheFilterFactory() : FactoryBase(HttpFilterNames::get().Cache) {} private: Http::FilterFactoryCb createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v2::CacheConfig& config, + const envoy::config::filter::http::cache::v3::CacheConfig& config, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; }; diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index d48e266c29980..c49703c49dd6d 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -6,7 +6,7 @@ #include "envoy/buffer/buffer.h" #include "envoy/common/time.h" -#include "envoy/config/filter/http/cache/v2/cache.pb.h" +#include "envoy/config/filter/http/cache/v3/cache.pb.h" #include "envoy/http/header_map.h" #include "common/common/assert.h" @@ -167,7 +167,7 @@ class LookupRequest { Key& key() { return key_; } // Returns the subset of this request's headers that are listed in - // envoy::config::filter::http::cache::v2alpha::Cache::allowed_vary_headers. + // envoy::config::filter::http::cache::v3::CacheConfig::allowed_vary_headers. // Only needed if the cache implementation finds a variant response. HeaderVector& vary_headers() { return vary_headers_; } const HeaderVector& vary_headers() const { return vary_headers_; } @@ -308,7 +308,7 @@ class HttpCacheFactory { // Returns an HttpCache that will remain valid indefinitely (at least as long // as the calling CacheFilter). virtual HttpCache& - getCache(const envoy::config::filter::http::cache::v2::CacheConfig& config) PURE; + getCache(const envoy::config::filter::http::cache::v3::CacheConfig& config) PURE; virtual ~HttpCacheFactory() = default; private: diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index e377130877290..620619a1cc890 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -131,7 +131,7 @@ CacheInfo SimpleHttpCache::cacheInfo() const { class SimpleHttpCacheFactory : public HttpCacheFactory { public: SimpleHttpCacheFactory() : HttpCacheFactory("envoy.extensions.http.cache.simple") {} - HttpCache& getCache(const envoy::config::filter::http::cache::v2::CacheConfig&) override { + HttpCache& getCache(const envoy::config::filter::http::cache::v3::CacheConfig&) override { return cache_; } diff --git a/test/extensions/filters/http/cache/cache_filter_integration_test.cc b/test/extensions/filters/http/cache/cache_filter_integration_test.cc index 4444d51d492f1..a429bd73f9101 100644 --- a/test/extensions/filters/http/cache/cache_filter_integration_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_integration_test.cc @@ -28,7 +28,7 @@ class CacheIntegrationTest : public Event::TestUsingSimulatedTime, const std::string default_config{R"EOF( name: envoy.filters.http.cache typed_config: - "@type": type.googleapis.com/envoy.config.filter.http.cache.v2.CacheConfig + "@type": type.googleapis.com/envoy.config.filter.http.cache.v3.CacheConfig name: envoy.extensions.http.cache.simple )EOF"}; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index 18487c7905748..bf53fe0ca4a24 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -14,7 +14,7 @@ namespace { class CacheFilterTest : public ::testing::Test { protected: - envoy::config::filter::http::cache::v2::CacheConfig config_; + envoy::config::filter::http::cache::v3::CacheConfig config_; NiceMock context_; Event::SimulatedTimeSystem time_source_; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; diff --git a/test/extensions/filters/http/cache/config_test.cc b/test/extensions/filters/http/cache/config_test.cc index b3da47ac46841..e0b5f5ccf75cf 100644 --- a/test/extensions/filters/http/cache/config_test.cc +++ b/test/extensions/filters/http/cache/config_test.cc @@ -14,7 +14,7 @@ namespace { class CacheFilterFactoryTest : public ::testing::Test { protected: - envoy::config::filter::http::cache::v2::CacheConfig config_; + envoy::config::filter::http::cache::v3::CacheConfig config_; NiceMock context_; CacheFilterFactory factory_; Http::MockFilterChainFactoryCallbacks filter_callback_; diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index 39b6c5a1c6bd9..0369cbc2699d5 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -205,7 +205,7 @@ TEST_F(SimpleHttpCacheTest, StreamingPut) { } TEST(Registration, GetFactory) { - envoy::config::filter::http::cache::v2::CacheConfig config; + envoy::config::filter::http::cache::v3::CacheConfig config; HttpCacheFactory* factory = Registry::FactoryRegistry::getFactory("envoy.extensions.http.cache.simple"); ASSERT_NE(factory, nullptr); From adb947e12a54b2b72e75e42f0c73dda8d4b9d7f4 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Wed, 22 Jan 2020 17:47:08 -0800 Subject: [PATCH 096/109] Elaborated on the effects of allowed_vary_headers. Signed-off-by: Todd Greer --- .../config/filter/http/cache/v3/cache.proto | 17 ++++++++++++----- .../extensions/filters/http/cache/http_cache.h | 6 ++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/api/envoy/config/filter/http/cache/v3/cache.proto b/api/envoy/config/filter/http/cache/v3/cache.proto index 7f69e2f62bb45..d7f075f42d07c 100644 --- a/api/envoy/config/filter/http/cache/v3/cache.proto +++ b/api/envoy/config/filter/http/cache/v3/cache.proto @@ -53,11 +53,18 @@ message CacheConfig { // [#not-implemented-hide:] // // - // List of allowed *Vary* headers. Headers matched by allowed_vary_headers will - // be provided to caches in lookups, and the cache filter will only insert - // responses whose *Vary* headers (if any) are matched by a matcher in this - // list. If allowed_vary_headers is empty, responses with *vary* headers will - // not be cached. + // List of allowed *Vary* headers. + // + // The *vary* response header holds a list of header names that affect the + // contents of a response, as described by + // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. + // + // During insertion, *allowed_vary_headers* acts as a whitelist: if a + // response's *vary* header mentions any header names that aren't in + // *allowed_vary_headers*, that response will not be cached. + // + // During lookup, *allowed_vary_headers* controls what request headers will be + // sent to the cache storage implementation. repeated type.matcher.StringMatcher allowed_vary_headers = 3; // [#not-implemented-hide:] diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index c49703c49dd6d..a332edb36e412 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -167,8 +167,10 @@ class LookupRequest { Key& key() { return key_; } // Returns the subset of this request's headers that are listed in - // envoy::config::filter::http::cache::v3::CacheConfig::allowed_vary_headers. - // Only needed if the cache implementation finds a variant response. + // envoy::config::filter::http::cache::v3::CacheConfig::allowed_vary_headers. If a cache storage + // implementation forwards lookup requests to a remote cache server that supports *vary* headers, + // that server may need to see these headers. For local implementations, it may be simpler to + // instead call makeLookupResult with each potential response. HeaderVector& vary_headers() { return vary_headers_; } const HeaderVector& vary_headers() const { return vary_headers_; } From 1903ae0450a931e94f6a3889ea464b3baf408c44 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 23 Jan 2020 11:41:27 -0800 Subject: [PATCH 097/109] Fix test error due to no longer requiring CacheConfig.name Signed-off-by: Todd Greer --- test/extensions/filters/http/cache/config_test.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/extensions/filters/http/cache/config_test.cc b/test/extensions/filters/http/cache/config_test.cc index e0b5f5ccf75cf..04b265830bce9 100644 --- a/test/extensions/filters/http/cache/config_test.cc +++ b/test/extensions/filters/http/cache/config_test.cc @@ -31,7 +31,9 @@ TEST_F(CacheFilterFactoryTest, Basic) { } TEST_F(CacheFilterFactoryTest, NoName) { - EXPECT_THROW(factory_.createFilterFactoryFromProto(config_, "stats", context_), EnvoyException); + Http::FilterFactoryCb cb = factory_.createFilterFactoryFromProto(config_, "stats", context_); + Http::StreamFilterSharedPtr filter; + EXPECT_THROW(cb(filter_callback_), EnvoyException); } TEST_F(CacheFilterFactoryTest, UnregisteredName) { From 4f26ac8de2441074c53939ab5fe21fc765821d9b Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 23 Jan 2020 11:46:25 -0800 Subject: [PATCH 098/109] Reran tools/proto_format.sh fix Signed-off-by: Todd Greer --- .../filters/http/cache/v3/cache.proto | 17 ++-- .../envoy/config/filter/http/cache/v3/BUILD | 13 +++ .../config/filter/http/cache/v3/cache.proto | 82 +++++++++++++++++++ .../filters/http/cache/v3/cache.proto | 17 ++-- 4 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD create mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto diff --git a/api/envoy/extensions/filters/http/cache/v3/cache.proto b/api/envoy/extensions/filters/http/cache/v3/cache.proto index ea6cafbf985c6..2916d40af006e 100644 --- a/api/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3/cache.proto @@ -59,11 +59,18 @@ message CacheConfig { // [#not-implemented-hide:] // // - // List of allowed *Vary* headers. Headers matched by allowed_vary_headers will - // be provided to caches in lookups, and the cache filter will only insert - // responses whose *Vary* headers (if any) are matched by a matcher in this - // list. If allowed_vary_headers is empty, responses with *vary* headers will - // not be cached. + // List of allowed *Vary* headers. + // + // The *vary* response header holds a list of header names that affect the + // contents of a response, as described by + // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. + // + // During insertion, *allowed_vary_headers* acts as a whitelist: if a + // response's *vary* header mentions any header names that aren't in + // *allowed_vary_headers*, that response will not be cached. + // + // During lookup, *allowed_vary_headers* controls what request headers will be + // sent to the cache storage implementation. repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 3; // [#not-implemented-hide:] diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD b/generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD new file mode 100644 index 0000000000000..87746bf482e03 --- /dev/null +++ b/generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/api/v2/route:pkg", + "//envoy/type/matcher:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto new file mode 100644 index 0000000000000..d7f075f42d07c --- /dev/null +++ b/generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto @@ -0,0 +1,82 @@ +syntax = "proto3"; + +package envoy.config.filter.http.cache.v3; + +import "envoy/api/v2/route/route_components.proto"; +import "envoy/type/matcher/string.proto"; + +import "google/protobuf/any.proto"; + +import "udpa/annotations/migrate.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v3"; +option java_outer_classname = "CacheProto"; +option java_multiple_files = true; +option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3"; + +// [#protodoc-title: HTTP Cache Filter] +// HTTP Cache Filter :ref:`overview `. +// [#extension: envoy.filters.http.cache] + +// [#next-free-field: 6] +message CacheConfig { + // [#not-implemented-hide:] + // Modifies cache key creation by restricting which parts of the URL are included. + message KeyCreatorParams { + // If true, exclude the URL scheme from the cache key. Set to true if your origins always + // produce the same response for http and https requests. + bool exclude_scheme = 1; + + // If true, exclude the host from the cache key. Set to true if your origins' responses don't + // ever depend on host. + bool exclude_host = 2; + + // If *query_parameters_included* is nonempty, only query parameters matched + // by one or more of its matchers are included in the cache key. Any other + // query params will not affect cache lookup. + repeated api.v2.route.QueryParameterMatcher query_parameters_included = 3; + + // If *query_parameters_excluded* is nonempty, query parameters matched by one + // or more of its matchers are excluded from the cache key (even if also + // matched by *query_parameters_included*), and will not affect cache lookup. + repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; + } + + // Config specific to the cache storage implementation. + google.protobuf.Any typed_config = 1; + + // Name of cache implementation to use, as specified in the intended HttpCacheFactory + // implementation. Cache names should use reverse DNS format, though this is not enforced. + string name = 2; + + // [#not-implemented-hide:] + // + // + // List of allowed *Vary* headers. + // + // The *vary* response header holds a list of header names that affect the + // contents of a response, as described by + // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. + // + // During insertion, *allowed_vary_headers* acts as a whitelist: if a + // response's *vary* header mentions any header names that aren't in + // *allowed_vary_headers*, that response will not be cached. + // + // During lookup, *allowed_vary_headers* controls what request headers will be + // sent to the cache storage implementation. + repeated type.matcher.StringMatcher allowed_vary_headers = 3; + + // [#not-implemented-hide:] + // + // + // Modifies cache key creation by restricting which parts of the URL are included. + KeyCreatorParams key_creator_params = 4; + + // [#not-implemented-hide:] + // + // + // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache + // storage implementation may have its own limit beyond which it will reject insertions). + uint32 max_body_bytes = 5; +} diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto index ea6cafbf985c6..2916d40af006e 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto @@ -59,11 +59,18 @@ message CacheConfig { // [#not-implemented-hide:] // // - // List of allowed *Vary* headers. Headers matched by allowed_vary_headers will - // be provided to caches in lookups, and the cache filter will only insert - // responses whose *Vary* headers (if any) are matched by a matcher in this - // list. If allowed_vary_headers is empty, responses with *vary* headers will - // not be cached. + // List of allowed *Vary* headers. + // + // The *vary* response header holds a list of header names that affect the + // contents of a response, as described by + // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. + // + // During insertion, *allowed_vary_headers* acts as a whitelist: if a + // response's *vary* header mentions any header names that aren't in + // *allowed_vary_headers*, that response will not be cached. + // + // During lookup, *allowed_vary_headers* controls what request headers will be + // sent to the cache storage implementation. repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 3; // [#not-implemented-hide:] From 2028b999d271f801255b723e4bfd33c5888f27d4 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 23 Jan 2020 13:49:23 -0800 Subject: [PATCH 099/109] Update v2->v3 Signed-off-by: Todd Greer --- .../http/http_filters/cache_filter.rst | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/root/configuration/http/http_filters/cache_filter.rst b/docs/root/configuration/http/http_filters/cache_filter.rst index 232ae6f8dfc8e..9a8427ea22cde 100644 --- a/docs/root/configuration/http/http_filters/cache_filter.rst +++ b/docs/root/configuration/http/http_filters/cache_filter.rst @@ -12,11 +12,11 @@ there is enough repeatedly served cacheable content. Configuration ------------- -* :ref:`Configuration API ` +* :ref:`Configuration API ` * This filter should be configured with the name *envoy.filters.http.cache*. The only required configuration field is :ref:`name -`, which must +`, which must specify a valid cache storage implementation linked into your Envoy binary. Specifying 'envoy.extensions.http.cache.simple' will select a proof-of-concept implementation included in the Envoy source. More implementations can (and will) @@ -24,17 +24,18 @@ be provided by implementing Envoy::Extensions::HttpFilters::Cache::HttpCache. To write a cache storage implementation, see :repo:`Writing Cache Filter Implementations ` -The remaining configuration options control caching behavior and limits. By -default, this filter will cache almost all responses that are considered -cacheable by `RFC7234 `_, with handling -of conditional (`RFC7232 `_), and *range* -(`RFC7233 `_) requests. Those RFC define -which request methods and response codes are cacheable, subject to the -cache-related headers they also define: *cache-control*, *range*, *if-match*, -*if-none-match*, *if-modified-since*, *if-unmodified-since*, *if-range*, *authorization*, -*date*, *age*, *expires*, and *vary*. Responses with a *vary* header will only be cached -if the named headers are listed in :ref:`allowed_vary_headers -` +.. TODO(toddmgreer) Describe other fields as they get implemented. + The remaining configuration fields control caching behavior and limits. By + default, this filter will cache almost all responses that are considered + cacheable by `RFC7234 `_, with handling + of conditional (`RFC7232 `_), and *range* + (`RFC7233 `_) requests. Those RFC define + which request methods and response codes are cacheable, subject to the + cache-related headers they also define: *cache-control*, *range*, *if-match*, + *if-none-match*, *if-modified-since*, *if-unmodified-since*, *if-range*, *authorization*, + *date*, *age*, *expires*, and *vary*. Responses with a *vary* header will only be cached + if the named headers are listed in :ref:`allowed_vary_headers + ` Status From 56ebd0244c72539267f790dedd8a7071c4bf3bc0 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 23 Jan 2020 17:17:30 -0800 Subject: [PATCH 100/109] Fix various v2->v3 issues Signed-off-by: Todd Greer --- api/envoy/config/filter/http/cache/v3/BUILD | 13 --- .../config/filter/http/cache/v3/cache.proto | 82 ------------------- .../extensions/filters/http/cache/v3/BUILD | 1 - .../filters/http/cache/v3/cache.proto | 8 +- .../envoy/config/filter/http/cache/v3/BUILD | 13 --- .../config/filter/http/cache/v3/cache.proto | 82 ------------------- .../extensions/filters/http/cache/v3/BUILD | 1 - source/extensions/filters/http/cache/BUILD | 6 +- .../filters/http/cache/cache_filter.cc | 4 +- .../filters/http/cache/cache_filter.h | 8 +- .../extensions/filters/http/cache/config.cc | 4 +- source/extensions/filters/http/cache/config.h | 8 +- .../filters/http/cache/http_cache.h | 6 +- .../filters/http/cache/simple_http_cache.cc | 6 +- .../cache/cache_filter_integration_test.cc | 4 +- .../filters/http/cache/cache_filter_test.cc | 6 +- .../filters/http/cache/config_test.cc | 4 +- .../http/cache/simple_http_cache_test.cc | 6 +- 18 files changed, 32 insertions(+), 230 deletions(-) delete mode 100644 api/envoy/config/filter/http/cache/v3/BUILD delete mode 100644 api/envoy/config/filter/http/cache/v3/cache.proto delete mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD delete mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto diff --git a/api/envoy/config/filter/http/cache/v3/BUILD b/api/envoy/config/filter/http/cache/v3/BUILD deleted file mode 100644 index 87746bf482e03..0000000000000 --- a/api/envoy/config/filter/http/cache/v3/BUILD +++ /dev/null @@ -1,13 +0,0 @@ -# DO NOT EDIT. This file is generated by tools/proto_sync.py. - -load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") - -licenses(["notice"]) # Apache 2 - -api_proto_package( - deps = [ - "//envoy/api/v2/route:pkg", - "//envoy/type/matcher:pkg", - "@com_github_cncf_udpa//udpa/annotations:pkg", - ], -) diff --git a/api/envoy/config/filter/http/cache/v3/cache.proto b/api/envoy/config/filter/http/cache/v3/cache.proto deleted file mode 100644 index d7f075f42d07c..0000000000000 --- a/api/envoy/config/filter/http/cache/v3/cache.proto +++ /dev/null @@ -1,82 +0,0 @@ -syntax = "proto3"; - -package envoy.config.filter.http.cache.v3; - -import "envoy/api/v2/route/route_components.proto"; -import "envoy/type/matcher/string.proto"; - -import "google/protobuf/any.proto"; - -import "udpa/annotations/migrate.proto"; -import "validate/validate.proto"; - -option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v3"; -option java_outer_classname = "CacheProto"; -option java_multiple_files = true; -option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3"; - -// [#protodoc-title: HTTP Cache Filter] -// HTTP Cache Filter :ref:`overview `. -// [#extension: envoy.filters.http.cache] - -// [#next-free-field: 6] -message CacheConfig { - // [#not-implemented-hide:] - // Modifies cache key creation by restricting which parts of the URL are included. - message KeyCreatorParams { - // If true, exclude the URL scheme from the cache key. Set to true if your origins always - // produce the same response for http and https requests. - bool exclude_scheme = 1; - - // If true, exclude the host from the cache key. Set to true if your origins' responses don't - // ever depend on host. - bool exclude_host = 2; - - // If *query_parameters_included* is nonempty, only query parameters matched - // by one or more of its matchers are included in the cache key. Any other - // query params will not affect cache lookup. - repeated api.v2.route.QueryParameterMatcher query_parameters_included = 3; - - // If *query_parameters_excluded* is nonempty, query parameters matched by one - // or more of its matchers are excluded from the cache key (even if also - // matched by *query_parameters_included*), and will not affect cache lookup. - repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; - } - - // Config specific to the cache storage implementation. - google.protobuf.Any typed_config = 1; - - // Name of cache implementation to use, as specified in the intended HttpCacheFactory - // implementation. Cache names should use reverse DNS format, though this is not enforced. - string name = 2; - - // [#not-implemented-hide:] - // - // - // List of allowed *Vary* headers. - // - // The *vary* response header holds a list of header names that affect the - // contents of a response, as described by - // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. - // - // During insertion, *allowed_vary_headers* acts as a whitelist: if a - // response's *vary* header mentions any header names that aren't in - // *allowed_vary_headers*, that response will not be cached. - // - // During lookup, *allowed_vary_headers* controls what request headers will be - // sent to the cache storage implementation. - repeated type.matcher.StringMatcher allowed_vary_headers = 3; - - // [#not-implemented-hide:] - // - // - // Modifies cache key creation by restricting which parts of the URL are included. - KeyCreatorParams key_creator_params = 4; - - // [#not-implemented-hide:] - // - // - // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache - // storage implementation may have its own limit beyond which it will reject insertions). - uint32 max_body_bytes = 5; -} diff --git a/api/envoy/extensions/filters/http/cache/v3/BUILD b/api/envoy/extensions/filters/http/cache/v3/BUILD index f0e18b47a9591..5ccc8d206ba3b 100644 --- a/api/envoy/extensions/filters/http/cache/v3/BUILD +++ b/api/envoy/extensions/filters/http/cache/v3/BUILD @@ -6,7 +6,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/config/filter/http/cache/v3:pkg", "//envoy/config/route/v3alpha:pkg", "//envoy/type/matcher/v3alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", diff --git a/api/envoy/extensions/filters/http/cache/v3/cache.proto b/api/envoy/extensions/filters/http/cache/v3/cache.proto index 2916d40af006e..defe08a721124 100644 --- a/api/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3/cache.proto @@ -17,19 +17,13 @@ option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] // HTTP Cache Filter :ref:`overview `. -// [#extension: envoy.filters.http.cache] +// [#extension: envoy.extensions.filters.http.cache] // [#next-free-field: 6] message CacheConfig { - option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v3.CacheConfig"; - // [#not-implemented-hide:] // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { - option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v3.CacheConfig.KeyCreatorParams"; - // If true, exclude the URL scheme from the cache key. Set to true if your origins always // produce the same response for http and https requests. bool exclude_scheme = 1; diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD b/generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD deleted file mode 100644 index 87746bf482e03..0000000000000 --- a/generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD +++ /dev/null @@ -1,13 +0,0 @@ -# DO NOT EDIT. This file is generated by tools/proto_sync.py. - -load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") - -licenses(["notice"]) # Apache 2 - -api_proto_package( - deps = [ - "//envoy/api/v2/route:pkg", - "//envoy/type/matcher:pkg", - "@com_github_cncf_udpa//udpa/annotations:pkg", - ], -) diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto deleted file mode 100644 index d7f075f42d07c..0000000000000 --- a/generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto +++ /dev/null @@ -1,82 +0,0 @@ -syntax = "proto3"; - -package envoy.config.filter.http.cache.v3; - -import "envoy/api/v2/route/route_components.proto"; -import "envoy/type/matcher/string.proto"; - -import "google/protobuf/any.proto"; - -import "udpa/annotations/migrate.proto"; -import "validate/validate.proto"; - -option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v3"; -option java_outer_classname = "CacheProto"; -option java_multiple_files = true; -option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3"; - -// [#protodoc-title: HTTP Cache Filter] -// HTTP Cache Filter :ref:`overview `. -// [#extension: envoy.filters.http.cache] - -// [#next-free-field: 6] -message CacheConfig { - // [#not-implemented-hide:] - // Modifies cache key creation by restricting which parts of the URL are included. - message KeyCreatorParams { - // If true, exclude the URL scheme from the cache key. Set to true if your origins always - // produce the same response for http and https requests. - bool exclude_scheme = 1; - - // If true, exclude the host from the cache key. Set to true if your origins' responses don't - // ever depend on host. - bool exclude_host = 2; - - // If *query_parameters_included* is nonempty, only query parameters matched - // by one or more of its matchers are included in the cache key. Any other - // query params will not affect cache lookup. - repeated api.v2.route.QueryParameterMatcher query_parameters_included = 3; - - // If *query_parameters_excluded* is nonempty, query parameters matched by one - // or more of its matchers are excluded from the cache key (even if also - // matched by *query_parameters_included*), and will not affect cache lookup. - repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; - } - - // Config specific to the cache storage implementation. - google.protobuf.Any typed_config = 1; - - // Name of cache implementation to use, as specified in the intended HttpCacheFactory - // implementation. Cache names should use reverse DNS format, though this is not enforced. - string name = 2; - - // [#not-implemented-hide:] - // - // - // List of allowed *Vary* headers. - // - // The *vary* response header holds a list of header names that affect the - // contents of a response, as described by - // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. - // - // During insertion, *allowed_vary_headers* acts as a whitelist: if a - // response's *vary* header mentions any header names that aren't in - // *allowed_vary_headers*, that response will not be cached. - // - // During lookup, *allowed_vary_headers* controls what request headers will be - // sent to the cache storage implementation. - repeated type.matcher.StringMatcher allowed_vary_headers = 3; - - // [#not-implemented-hide:] - // - // - // Modifies cache key creation by restricting which parts of the URL are included. - KeyCreatorParams key_creator_params = 4; - - // [#not-implemented-hide:] - // - // - // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache - // storage implementation may have its own limit beyond which it will reject insertions). - uint32 max_body_bytes = 5; -} diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD index f0e18b47a9591..5ccc8d206ba3b 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD @@ -6,7 +6,6 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/config/filter/http/cache/v3:pkg", "//envoy/config/route/v3alpha:pkg", "//envoy/type/matcher/v3alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index d3b4e9bd27081..33469461f517d 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -26,7 +26,7 @@ envoy_cc_library( "//source/common/http:headers_lib", "//source/common/protobuf", "//source/extensions/filters/http/common:pass_through_filter_lib", - "@envoy_api//envoy/config/filter/http/cache/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/http/cache/v3:pkg_cc_proto", ], ) @@ -44,7 +44,7 @@ envoy_cc_extension( "//include/envoy/stats:stats_interface", "//source/extensions/filters/http:well_known_names", "//source/extensions/filters/http/common:factory_base_lib", - "@envoy_api//envoy/config/filter/http/cache/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/http/cache/v3:pkg_cc_proto", ], ) @@ -66,7 +66,7 @@ envoy_cc_library( "//source/common/common:assert_lib", "//source/common/http:headers_lib", "//source/common/protobuf:utility_lib", - "@envoy_api//envoy/config/filter/http/cache/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/http/cache/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index ca0bffb440d90..0b83666e11ff7 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -37,11 +37,11 @@ bool CacheFilter::isCacheableResponse(Http::HeaderMap& headers) { } HttpCache& -CacheFilter::getCache(const envoy::config::filter::http::cache::v3::CacheConfig& config) { +CacheFilter::getCache(const envoy::extensions::filters::http::cache::v3::CacheConfig& config) { return Config::Utility::getAndCheckFactory(config).getCache(config); } -CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v3::CacheConfig& config, +CacheFilter::CacheFilter(const envoy::extensions::filters::http::cache::v3::CacheConfig& config, const std::string&, Stats::Scope&, TimeSource& time_source) : time_source_(time_source), cache_(getCache(config)) {} diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index e5d7bd34ec62f..658864e851d95 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -5,7 +5,7 @@ #include #include -#include "envoy/config/filter/http/cache/v3/cache.pb.h" +#include "envoy/extensions/filters/http/cache/v3/cache.pb.h" #include "common/common/logger.h" @@ -30,7 +30,7 @@ class CacheFilter : public Http::PassThroughFilter, public: // Throws ProtoValidationException if no registered HttpCacheFactory for config.name. static CacheFilterSharedPtr - make(const envoy::config::filter::http::cache::v3::CacheConfig& config, + make(const envoy::extensions::filters::http::cache::v3::CacheConfig& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source) { // Can't use make_shared due to private constructor. return std::shared_ptr(new CacheFilter(config, stats_prefix, scope, time_source)); @@ -47,7 +47,7 @@ class CacheFilter : public Http::PassThroughFilter, // Throws EnvoyException if no registered HttpCacheFactory for config.name. // Constructor is private to enforce enable_shared_from_this's requirement that this must be owned // by a shared_ptr. - CacheFilter(const envoy::config::filter::http::cache::v3::CacheConfig& config, + CacheFilter(const envoy::extensions::filters::http::cache::v3::CacheConfig& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source); void getBody(); @@ -64,7 +64,7 @@ class CacheFilter : public Http::PassThroughFilter, // These don't require private access, but are members per envoy convention. static bool isCacheableRequest(Http::HeaderMap& headers); static bool isCacheableResponse(Http::HeaderMap& headers); - static HttpCache& getCache(const envoy::config::filter::http::cache::v3::CacheConfig& config); + static HttpCache& getCache(const envoy::extensions::filters::http::cache::v3::CacheConfig& config); TimeSource& time_source_; HttpCache& cache_; diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index 2a790b6548e18..1d5ab396fece9 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -3,7 +3,7 @@ #include #include "envoy/common/time.h" -#include "envoy/config/filter/http/cache/v3/cache.pb.validate.h" +#include "envoy/extensions/filters/http/cache/v3/cache.pb.validate.h" #include "envoy/registry/registry.h" #include "envoy/stats/scope.h" @@ -15,7 +15,7 @@ namespace HttpFilters { namespace Cache { Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v3::CacheConfig& config, + const envoy::extensions::filters::http::cache::v3::CacheConfig& config, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { return [config, stats_prefix, &context](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter( diff --git a/source/extensions/filters/http/cache/config.h b/source/extensions/filters/http/cache/config.h index 36b99cf009932..b909664849250 100644 --- a/source/extensions/filters/http/cache/config.h +++ b/source/extensions/filters/http/cache/config.h @@ -1,7 +1,7 @@ #pragma once -#include "envoy/config/filter/http/cache/v3/cache.pb.h" -#include "envoy/config/filter/http/cache/v3/cache.pb.validate.h" +#include "envoy/extensions/filters/http/cache/v3/cache.pb.h" +#include "envoy/extensions/filters/http/cache/v3/cache.pb.validate.h" #include "extensions/filters/http/cache/cache_filter.h" #include "extensions/filters/http/common/factory_base.h" @@ -13,13 +13,13 @@ namespace HttpFilters { namespace Cache { class CacheFilterFactory - : public Common::FactoryBase { + : public Common::FactoryBase { public: CacheFilterFactory() : FactoryBase(HttpFilterNames::get().Cache) {} private: Http::FilterFactoryCb createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v3::CacheConfig& config, + const envoy::extensions::filters::http::cache::v3::CacheConfig& config, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; }; diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index a332edb36e412..7cd87f45b4fc4 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -6,7 +6,7 @@ #include "envoy/buffer/buffer.h" #include "envoy/common/time.h" -#include "envoy/config/filter/http/cache/v3/cache.pb.h" +#include "envoy/extensions/filters/http/cache/v3/cache.pb.h" #include "envoy/http/header_map.h" #include "common/common/assert.h" @@ -167,7 +167,7 @@ class LookupRequest { Key& key() { return key_; } // Returns the subset of this request's headers that are listed in - // envoy::config::filter::http::cache::v3::CacheConfig::allowed_vary_headers. If a cache storage + // envoy::extensions::filters::http::cache::v3::CacheConfig::allowed_vary_headers. If a cache storage // implementation forwards lookup requests to a remote cache server that supports *vary* headers, // that server may need to see these headers. For local implementations, it may be simpler to // instead call makeLookupResult with each potential response. @@ -310,7 +310,7 @@ class HttpCacheFactory { // Returns an HttpCache that will remain valid indefinitely (at least as long // as the calling CacheFilter). virtual HttpCache& - getCache(const envoy::config::filter::http::cache::v3::CacheConfig& config) PURE; + getCache(const envoy::extensions::filters::http::cache::v3::CacheConfig& config) PURE; virtual ~HttpCacheFactory() = default; private: diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index 620619a1cc890..d4ae671399cb6 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -124,14 +124,14 @@ InsertContextPtr SimpleHttpCache::makeInsertContext(LookupContextPtr&& lookup_co CacheInfo SimpleHttpCache::cacheInfo() const { CacheInfo cache_info; - cache_info.name_ = "envoy.extensions.http.cache.simple"; + cache_info.name_ = "envoy.extensions.filters.http.cache.simple"; return cache_info; } class SimpleHttpCacheFactory : public HttpCacheFactory { public: - SimpleHttpCacheFactory() : HttpCacheFactory("envoy.extensions.http.cache.simple") {} - HttpCache& getCache(const envoy::config::filter::http::cache::v3::CacheConfig&) override { + SimpleHttpCacheFactory() : HttpCacheFactory("envoy.extensions.filters.http.cache.simple") {} + HttpCache& getCache(const envoy::extensions::filters::http::cache::v3::CacheConfig&) override { return cache_; } diff --git a/test/extensions/filters/http/cache/cache_filter_integration_test.cc b/test/extensions/filters/http/cache/cache_filter_integration_test.cc index a429bd73f9101..c0c521537612f 100644 --- a/test/extensions/filters/http/cache/cache_filter_integration_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_integration_test.cc @@ -28,8 +28,8 @@ class CacheIntegrationTest : public Event::TestUsingSimulatedTime, const std::string default_config{R"EOF( name: envoy.filters.http.cache typed_config: - "@type": type.googleapis.com/envoy.config.filter.http.cache.v3.CacheConfig - name: envoy.extensions.http.cache.simple + "@type": type.googleapis.com/envoy.extensions.filters.http.cache.v3.CacheConfig + name: envoy.extensions.filters.http.cache.simple )EOF"}; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; }; diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index bf53fe0ca4a24..ec6a2661c19d7 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -14,7 +14,7 @@ namespace { class CacheFilterTest : public ::testing::Test { protected: - envoy::config::filter::http::cache::v3::CacheConfig config_; + envoy::extensions::filters::http::cache::v3::CacheConfig config_; NiceMock context_; Event::SimulatedTimeSystem time_source_; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; @@ -43,7 +43,7 @@ TEST_F(CacheFilterTest, ImmediateHitNoBody) { ON_CALL(context_.dispatcher_, post(_)).WillByDefault(::testing::InvokeArgument<0>()); // Create filter for request 1 - config_.set_name("envoy.extensions.http.cache.simple"); + config_.set_name("envoy.extensions.filters.http.cache.simple"); CacheFilterSharedPtr filter = makeFilter(); ASSERT_TRUE(filter); @@ -78,7 +78,7 @@ TEST_F(CacheFilterTest, ImmediateHitBody) { ON_CALL(context_.dispatcher_, post(_)).WillByDefault(::testing::InvokeArgument<0>()); // Create filter for request 1 - config_.set_name("envoy.extensions.http.cache.simple"); + config_.set_name("envoy.extensions.filters.http.cache.simple"); CacheFilterSharedPtr filter = makeFilter(); ASSERT_TRUE(filter); diff --git a/test/extensions/filters/http/cache/config_test.cc b/test/extensions/filters/http/cache/config_test.cc index 04b265830bce9..01e32df4b6767 100644 --- a/test/extensions/filters/http/cache/config_test.cc +++ b/test/extensions/filters/http/cache/config_test.cc @@ -14,14 +14,14 @@ namespace { class CacheFilterFactoryTest : public ::testing::Test { protected: - envoy::config::filter::http::cache::v3::CacheConfig config_; + envoy::extensions::filters::http::cache::v3::CacheConfig config_; NiceMock context_; CacheFilterFactory factory_; Http::MockFilterChainFactoryCallbacks filter_callback_; }; TEST_F(CacheFilterFactoryTest, Basic) { - config_.set_name("envoy.extensions.http.cache.simple"); + config_.set_name("envoy.extensions.filters.http.cache.simple"); Http::FilterFactoryCb cb = factory_.createFilterFactoryFromProto(config_, "stats", context_); Http::StreamFilterSharedPtr filter; EXPECT_CALL(filter_callback_, addStreamFilter(_)).WillOnce(::testing::SaveArg<0>(&filter)); diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index 0369cbc2699d5..dff7a45360fa3 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -205,11 +205,11 @@ TEST_F(SimpleHttpCacheTest, StreamingPut) { } TEST(Registration, GetFactory) { - envoy::config::filter::http::cache::v3::CacheConfig config; + envoy::extensions::filters::http::cache::v3::CacheConfig config; HttpCacheFactory* factory = - Registry::FactoryRegistry::getFactory("envoy.extensions.http.cache.simple"); + Registry::FactoryRegistry::getFactory("envoy.extensions.filters.http.cache.simple"); ASSERT_NE(factory, nullptr); - EXPECT_EQ(factory->getCache(config).cacheInfo().name_, "envoy.extensions.http.cache.simple"); + EXPECT_EQ(factory->getCache(config).cacheInfo().name_, "envoy.extensions.filters.http.cache.simple"); } } // namespace From 8e10a41f54643d3c8329315d539f7c09c7172b03 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Thu, 23 Jan 2020 18:06:19 -0800 Subject: [PATCH 101/109] bazel build -c fastbuild @envoy_api_canonical//envoy/... now works Signed-off-by: Todd Greer --- api/BUILD | 1 - api/docs/BUILD | 2 +- api/envoy/extensions/filters/http/cache/v3/BUILD | 2 +- api/envoy/extensions/filters/http/cache/v3/cache.proto | 6 +++--- generated_api_shadow/BUILD | 1 - 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/api/BUILD b/api/BUILD index 99c85eaa9cf7f..b02693b16fad1 100644 --- a/api/BUILD +++ b/api/BUILD @@ -35,7 +35,6 @@ proto_library( "//envoy/config/filter/fault/v2:pkg", "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", - "//envoy/config/filter/http/cache/v3:pkg", "//envoy/config/filter/http/cors/v2:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", diff --git a/api/docs/BUILD b/api/docs/BUILD index 993e44be51e8d..8019ca910bf0f 100644 --- a/api/docs/BUILD +++ b/api/docs/BUILD @@ -35,7 +35,6 @@ proto_library( "//envoy/config/filter/fault/v2:pkg", "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", - "//envoy/config/filter/http/cache/v3:pkg", "//envoy/config/filter/http/cors/v2:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", @@ -104,6 +103,7 @@ proto_library( "//envoy/data/cluster/v2alpha:pkg", "//envoy/data/core/v2alpha:pkg", "//envoy/data/tap/v2alpha:pkg", + "//envoy/extensions/filters/http/cache/v3:pkg", "//envoy/service/accesslog/v2:pkg", "//envoy/service/auth/v2:pkg", "//envoy/service/discovery/v2:pkg", diff --git a/api/envoy/extensions/filters/http/cache/v3/BUILD b/api/envoy/extensions/filters/http/cache/v3/BUILD index 5ccc8d206ba3b..28cbf8da9fc14 100644 --- a/api/envoy/extensions/filters/http/cache/v3/BUILD +++ b/api/envoy/extensions/filters/http/cache/v3/BUILD @@ -6,7 +6,7 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/config/route/v3alpha:pkg", + "//envoy/api/v2/route:pkg", "//envoy/type/matcher/v3alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/extensions/filters/http/cache/v3/cache.proto b/api/envoy/extensions/filters/http/cache/v3/cache.proto index defe08a721124..33750892d313f 100644 --- a/api/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3/cache.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package envoy.extensions.filters.http.cache.v3; -import "envoy/config/route/v3alpha/route_components.proto"; +import "envoy/api/v2/route/route_components.proto"; import "envoy/type/matcher/v3alpha/string.proto"; import "google/protobuf/any.proto"; @@ -35,12 +35,12 @@ message CacheConfig { // If *query_parameters_included* is nonempty, only query parameters matched // by one or more of its matchers are included in the cache key. Any other // query params will not affect cache lookup. - repeated config.route.v3alpha.QueryParameterMatcher query_parameters_included = 3; + repeated envoy.api.v2.route.QueryParameterMatcher query_parameters_included = 3; // If *query_parameters_excluded* is nonempty, query parameters matched by one // or more of its matchers are excluded from the cache key (even if also // matched by *query_parameters_included*), and will not affect cache lookup. - repeated config.route.v3alpha.QueryParameterMatcher query_parameters_excluded = 4; + repeated envoy.api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; } // Config specific to the cache storage implementation. diff --git a/generated_api_shadow/BUILD b/generated_api_shadow/BUILD index 99c85eaa9cf7f..b02693b16fad1 100644 --- a/generated_api_shadow/BUILD +++ b/generated_api_shadow/BUILD @@ -35,7 +35,6 @@ proto_library( "//envoy/config/filter/fault/v2:pkg", "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", - "//envoy/config/filter/http/cache/v3:pkg", "//envoy/config/filter/http/cors/v2:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", From 063ba6da4b7fb339d10074d3f57bde8048ee4f34 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 24 Jan 2020 13:34:56 -0800 Subject: [PATCH 102/109] Revert v3 changes This reverts commit 56ebd0244c72539267f790dedd8a7071c4bf3bc0. Signed-off-by: Todd Greer --- api/envoy/config/filter/http/cache/v3/BUILD | 13 +++ .../config/filter/http/cache/v3/cache.proto | 82 +++++++++++++++++++ .../extensions/filters/http/cache/v3/BUILD | 3 +- .../filters/http/cache/v3/cache.proto | 8 +- .../envoy/config/filter/http/cache/v3/BUILD | 13 +++ .../config/filter/http/cache/v3/cache.proto | 82 +++++++++++++++++++ .../extensions/filters/http/cache/v3/BUILD | 1 + source/extensions/filters/http/cache/BUILD | 6 +- .../filters/http/cache/cache_filter.cc | 4 +- .../filters/http/cache/cache_filter.h | 8 +- .../extensions/filters/http/cache/config.cc | 4 +- source/extensions/filters/http/cache/config.h | 8 +- .../filters/http/cache/http_cache.h | 6 +- .../filters/http/cache/simple_http_cache.cc | 6 +- .../cache/cache_filter_integration_test.cc | 4 +- .../filters/http/cache/cache_filter_test.cc | 6 +- .../filters/http/cache/config_test.cc | 4 +- .../http/cache/simple_http_cache_test.cc | 6 +- 18 files changed, 231 insertions(+), 33 deletions(-) create mode 100644 api/envoy/config/filter/http/cache/v3/BUILD create mode 100644 api/envoy/config/filter/http/cache/v3/cache.proto create mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD create mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto diff --git a/api/envoy/config/filter/http/cache/v3/BUILD b/api/envoy/config/filter/http/cache/v3/BUILD new file mode 100644 index 0000000000000..87746bf482e03 --- /dev/null +++ b/api/envoy/config/filter/http/cache/v3/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/api/v2/route:pkg", + "//envoy/type/matcher:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/config/filter/http/cache/v3/cache.proto b/api/envoy/config/filter/http/cache/v3/cache.proto new file mode 100644 index 0000000000000..d7f075f42d07c --- /dev/null +++ b/api/envoy/config/filter/http/cache/v3/cache.proto @@ -0,0 +1,82 @@ +syntax = "proto3"; + +package envoy.config.filter.http.cache.v3; + +import "envoy/api/v2/route/route_components.proto"; +import "envoy/type/matcher/string.proto"; + +import "google/protobuf/any.proto"; + +import "udpa/annotations/migrate.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v3"; +option java_outer_classname = "CacheProto"; +option java_multiple_files = true; +option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3"; + +// [#protodoc-title: HTTP Cache Filter] +// HTTP Cache Filter :ref:`overview `. +// [#extension: envoy.filters.http.cache] + +// [#next-free-field: 6] +message CacheConfig { + // [#not-implemented-hide:] + // Modifies cache key creation by restricting which parts of the URL are included. + message KeyCreatorParams { + // If true, exclude the URL scheme from the cache key. Set to true if your origins always + // produce the same response for http and https requests. + bool exclude_scheme = 1; + + // If true, exclude the host from the cache key. Set to true if your origins' responses don't + // ever depend on host. + bool exclude_host = 2; + + // If *query_parameters_included* is nonempty, only query parameters matched + // by one or more of its matchers are included in the cache key. Any other + // query params will not affect cache lookup. + repeated api.v2.route.QueryParameterMatcher query_parameters_included = 3; + + // If *query_parameters_excluded* is nonempty, query parameters matched by one + // or more of its matchers are excluded from the cache key (even if also + // matched by *query_parameters_included*), and will not affect cache lookup. + repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; + } + + // Config specific to the cache storage implementation. + google.protobuf.Any typed_config = 1; + + // Name of cache implementation to use, as specified in the intended HttpCacheFactory + // implementation. Cache names should use reverse DNS format, though this is not enforced. + string name = 2; + + // [#not-implemented-hide:] + // + // + // List of allowed *Vary* headers. + // + // The *vary* response header holds a list of header names that affect the + // contents of a response, as described by + // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. + // + // During insertion, *allowed_vary_headers* acts as a whitelist: if a + // response's *vary* header mentions any header names that aren't in + // *allowed_vary_headers*, that response will not be cached. + // + // During lookup, *allowed_vary_headers* controls what request headers will be + // sent to the cache storage implementation. + repeated type.matcher.StringMatcher allowed_vary_headers = 3; + + // [#not-implemented-hide:] + // + // + // Modifies cache key creation by restricting which parts of the URL are included. + KeyCreatorParams key_creator_params = 4; + + // [#not-implemented-hide:] + // + // + // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache + // storage implementation may have its own limit beyond which it will reject insertions). + uint32 max_body_bytes = 5; +} diff --git a/api/envoy/extensions/filters/http/cache/v3/BUILD b/api/envoy/extensions/filters/http/cache/v3/BUILD index 28cbf8da9fc14..f0e18b47a9591 100644 --- a/api/envoy/extensions/filters/http/cache/v3/BUILD +++ b/api/envoy/extensions/filters/http/cache/v3/BUILD @@ -6,7 +6,8 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/api/v2/route:pkg", + "//envoy/config/filter/http/cache/v3:pkg", + "//envoy/config/route/v3alpha:pkg", "//envoy/type/matcher/v3alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], diff --git a/api/envoy/extensions/filters/http/cache/v3/cache.proto b/api/envoy/extensions/filters/http/cache/v3/cache.proto index 33750892d313f..52e55e07d2f0f 100644 --- a/api/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3/cache.proto @@ -17,13 +17,19 @@ option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] // HTTP Cache Filter :ref:`overview `. -// [#extension: envoy.extensions.filters.http.cache] +// [#extension: envoy.filters.http.cache] // [#next-free-field: 6] message CacheConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.filter.http.cache.v3.CacheConfig"; + // [#not-implemented-hide:] // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.filter.http.cache.v3.CacheConfig.KeyCreatorParams"; + // If true, exclude the URL scheme from the cache key. Set to true if your origins always // produce the same response for http and https requests. bool exclude_scheme = 1; diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD b/generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD new file mode 100644 index 0000000000000..87746bf482e03 --- /dev/null +++ b/generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/api/v2/route:pkg", + "//envoy/type/matcher:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto new file mode 100644 index 0000000000000..d7f075f42d07c --- /dev/null +++ b/generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto @@ -0,0 +1,82 @@ +syntax = "proto3"; + +package envoy.config.filter.http.cache.v3; + +import "envoy/api/v2/route/route_components.proto"; +import "envoy/type/matcher/string.proto"; + +import "google/protobuf/any.proto"; + +import "udpa/annotations/migrate.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v3"; +option java_outer_classname = "CacheProto"; +option java_multiple_files = true; +option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3"; + +// [#protodoc-title: HTTP Cache Filter] +// HTTP Cache Filter :ref:`overview `. +// [#extension: envoy.filters.http.cache] + +// [#next-free-field: 6] +message CacheConfig { + // [#not-implemented-hide:] + // Modifies cache key creation by restricting which parts of the URL are included. + message KeyCreatorParams { + // If true, exclude the URL scheme from the cache key. Set to true if your origins always + // produce the same response for http and https requests. + bool exclude_scheme = 1; + + // If true, exclude the host from the cache key. Set to true if your origins' responses don't + // ever depend on host. + bool exclude_host = 2; + + // If *query_parameters_included* is nonempty, only query parameters matched + // by one or more of its matchers are included in the cache key. Any other + // query params will not affect cache lookup. + repeated api.v2.route.QueryParameterMatcher query_parameters_included = 3; + + // If *query_parameters_excluded* is nonempty, query parameters matched by one + // or more of its matchers are excluded from the cache key (even if also + // matched by *query_parameters_included*), and will not affect cache lookup. + repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; + } + + // Config specific to the cache storage implementation. + google.protobuf.Any typed_config = 1; + + // Name of cache implementation to use, as specified in the intended HttpCacheFactory + // implementation. Cache names should use reverse DNS format, though this is not enforced. + string name = 2; + + // [#not-implemented-hide:] + // + // + // List of allowed *Vary* headers. + // + // The *vary* response header holds a list of header names that affect the + // contents of a response, as described by + // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. + // + // During insertion, *allowed_vary_headers* acts as a whitelist: if a + // response's *vary* header mentions any header names that aren't in + // *allowed_vary_headers*, that response will not be cached. + // + // During lookup, *allowed_vary_headers* controls what request headers will be + // sent to the cache storage implementation. + repeated type.matcher.StringMatcher allowed_vary_headers = 3; + + // [#not-implemented-hide:] + // + // + // Modifies cache key creation by restricting which parts of the URL are included. + KeyCreatorParams key_creator_params = 4; + + // [#not-implemented-hide:] + // + // + // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache + // storage implementation may have its own limit beyond which it will reject insertions). + uint32 max_body_bytes = 5; +} diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD index 5ccc8d206ba3b..f0e18b47a9591 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD @@ -6,6 +6,7 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ + "//envoy/config/filter/http/cache/v3:pkg", "//envoy/config/route/v3alpha:pkg", "//envoy/type/matcher/v3alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index 33469461f517d..d3b4e9bd27081 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -26,7 +26,7 @@ envoy_cc_library( "//source/common/http:headers_lib", "//source/common/protobuf", "//source/extensions/filters/http/common:pass_through_filter_lib", - "@envoy_api//envoy/extensions/filters/http/cache/v3:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/cache/v3:pkg_cc_proto", ], ) @@ -44,7 +44,7 @@ envoy_cc_extension( "//include/envoy/stats:stats_interface", "//source/extensions/filters/http:well_known_names", "//source/extensions/filters/http/common:factory_base_lib", - "@envoy_api//envoy/extensions/filters/http/cache/v3:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/cache/v3:pkg_cc_proto", ], ) @@ -66,7 +66,7 @@ envoy_cc_library( "//source/common/common:assert_lib", "//source/common/http:headers_lib", "//source/common/protobuf:utility_lib", - "@envoy_api//envoy/extensions/filters/http/cache/v3:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/cache/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 0b83666e11ff7..ca0bffb440d90 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -37,11 +37,11 @@ bool CacheFilter::isCacheableResponse(Http::HeaderMap& headers) { } HttpCache& -CacheFilter::getCache(const envoy::extensions::filters::http::cache::v3::CacheConfig& config) { +CacheFilter::getCache(const envoy::config::filter::http::cache::v3::CacheConfig& config) { return Config::Utility::getAndCheckFactory(config).getCache(config); } -CacheFilter::CacheFilter(const envoy::extensions::filters::http::cache::v3::CacheConfig& config, +CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v3::CacheConfig& config, const std::string&, Stats::Scope&, TimeSource& time_source) : time_source_(time_source), cache_(getCache(config)) {} diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index 658864e851d95..e5d7bd34ec62f 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -5,7 +5,7 @@ #include #include -#include "envoy/extensions/filters/http/cache/v3/cache.pb.h" +#include "envoy/config/filter/http/cache/v3/cache.pb.h" #include "common/common/logger.h" @@ -30,7 +30,7 @@ class CacheFilter : public Http::PassThroughFilter, public: // Throws ProtoValidationException if no registered HttpCacheFactory for config.name. static CacheFilterSharedPtr - make(const envoy::extensions::filters::http::cache::v3::CacheConfig& config, + make(const envoy::config::filter::http::cache::v3::CacheConfig& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source) { // Can't use make_shared due to private constructor. return std::shared_ptr(new CacheFilter(config, stats_prefix, scope, time_source)); @@ -47,7 +47,7 @@ class CacheFilter : public Http::PassThroughFilter, // Throws EnvoyException if no registered HttpCacheFactory for config.name. // Constructor is private to enforce enable_shared_from_this's requirement that this must be owned // by a shared_ptr. - CacheFilter(const envoy::extensions::filters::http::cache::v3::CacheConfig& config, + CacheFilter(const envoy::config::filter::http::cache::v3::CacheConfig& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source); void getBody(); @@ -64,7 +64,7 @@ class CacheFilter : public Http::PassThroughFilter, // These don't require private access, but are members per envoy convention. static bool isCacheableRequest(Http::HeaderMap& headers); static bool isCacheableResponse(Http::HeaderMap& headers); - static HttpCache& getCache(const envoy::extensions::filters::http::cache::v3::CacheConfig& config); + static HttpCache& getCache(const envoy::config::filter::http::cache::v3::CacheConfig& config); TimeSource& time_source_; HttpCache& cache_; diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index 1d5ab396fece9..2a790b6548e18 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -3,7 +3,7 @@ #include #include "envoy/common/time.h" -#include "envoy/extensions/filters/http/cache/v3/cache.pb.validate.h" +#include "envoy/config/filter/http/cache/v3/cache.pb.validate.h" #include "envoy/registry/registry.h" #include "envoy/stats/scope.h" @@ -15,7 +15,7 @@ namespace HttpFilters { namespace Cache { Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( - const envoy::extensions::filters::http::cache::v3::CacheConfig& config, + const envoy::config::filter::http::cache::v3::CacheConfig& config, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { return [config, stats_prefix, &context](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter( diff --git a/source/extensions/filters/http/cache/config.h b/source/extensions/filters/http/cache/config.h index b909664849250..36b99cf009932 100644 --- a/source/extensions/filters/http/cache/config.h +++ b/source/extensions/filters/http/cache/config.h @@ -1,7 +1,7 @@ #pragma once -#include "envoy/extensions/filters/http/cache/v3/cache.pb.h" -#include "envoy/extensions/filters/http/cache/v3/cache.pb.validate.h" +#include "envoy/config/filter/http/cache/v3/cache.pb.h" +#include "envoy/config/filter/http/cache/v3/cache.pb.validate.h" #include "extensions/filters/http/cache/cache_filter.h" #include "extensions/filters/http/common/factory_base.h" @@ -13,13 +13,13 @@ namespace HttpFilters { namespace Cache { class CacheFilterFactory - : public Common::FactoryBase { + : public Common::FactoryBase { public: CacheFilterFactory() : FactoryBase(HttpFilterNames::get().Cache) {} private: Http::FilterFactoryCb createFilterFactoryFromProtoTyped( - const envoy::extensions::filters::http::cache::v3::CacheConfig& config, + const envoy::config::filter::http::cache::v3::CacheConfig& config, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; }; diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 7cd87f45b4fc4..a332edb36e412 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -6,7 +6,7 @@ #include "envoy/buffer/buffer.h" #include "envoy/common/time.h" -#include "envoy/extensions/filters/http/cache/v3/cache.pb.h" +#include "envoy/config/filter/http/cache/v3/cache.pb.h" #include "envoy/http/header_map.h" #include "common/common/assert.h" @@ -167,7 +167,7 @@ class LookupRequest { Key& key() { return key_; } // Returns the subset of this request's headers that are listed in - // envoy::extensions::filters::http::cache::v3::CacheConfig::allowed_vary_headers. If a cache storage + // envoy::config::filter::http::cache::v3::CacheConfig::allowed_vary_headers. If a cache storage // implementation forwards lookup requests to a remote cache server that supports *vary* headers, // that server may need to see these headers. For local implementations, it may be simpler to // instead call makeLookupResult with each potential response. @@ -310,7 +310,7 @@ class HttpCacheFactory { // Returns an HttpCache that will remain valid indefinitely (at least as long // as the calling CacheFilter). virtual HttpCache& - getCache(const envoy::extensions::filters::http::cache::v3::CacheConfig& config) PURE; + getCache(const envoy::config::filter::http::cache::v3::CacheConfig& config) PURE; virtual ~HttpCacheFactory() = default; private: diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index d4ae671399cb6..620619a1cc890 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -124,14 +124,14 @@ InsertContextPtr SimpleHttpCache::makeInsertContext(LookupContextPtr&& lookup_co CacheInfo SimpleHttpCache::cacheInfo() const { CacheInfo cache_info; - cache_info.name_ = "envoy.extensions.filters.http.cache.simple"; + cache_info.name_ = "envoy.extensions.http.cache.simple"; return cache_info; } class SimpleHttpCacheFactory : public HttpCacheFactory { public: - SimpleHttpCacheFactory() : HttpCacheFactory("envoy.extensions.filters.http.cache.simple") {} - HttpCache& getCache(const envoy::extensions::filters::http::cache::v3::CacheConfig&) override { + SimpleHttpCacheFactory() : HttpCacheFactory("envoy.extensions.http.cache.simple") {} + HttpCache& getCache(const envoy::config::filter::http::cache::v3::CacheConfig&) override { return cache_; } diff --git a/test/extensions/filters/http/cache/cache_filter_integration_test.cc b/test/extensions/filters/http/cache/cache_filter_integration_test.cc index c0c521537612f..a429bd73f9101 100644 --- a/test/extensions/filters/http/cache/cache_filter_integration_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_integration_test.cc @@ -28,8 +28,8 @@ class CacheIntegrationTest : public Event::TestUsingSimulatedTime, const std::string default_config{R"EOF( name: envoy.filters.http.cache typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.cache.v3.CacheConfig - name: envoy.extensions.filters.http.cache.simple + "@type": type.googleapis.com/envoy.config.filter.http.cache.v3.CacheConfig + name: envoy.extensions.http.cache.simple )EOF"}; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; }; diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index ec6a2661c19d7..bf53fe0ca4a24 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -14,7 +14,7 @@ namespace { class CacheFilterTest : public ::testing::Test { protected: - envoy::extensions::filters::http::cache::v3::CacheConfig config_; + envoy::config::filter::http::cache::v3::CacheConfig config_; NiceMock context_; Event::SimulatedTimeSystem time_source_; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; @@ -43,7 +43,7 @@ TEST_F(CacheFilterTest, ImmediateHitNoBody) { ON_CALL(context_.dispatcher_, post(_)).WillByDefault(::testing::InvokeArgument<0>()); // Create filter for request 1 - config_.set_name("envoy.extensions.filters.http.cache.simple"); + config_.set_name("envoy.extensions.http.cache.simple"); CacheFilterSharedPtr filter = makeFilter(); ASSERT_TRUE(filter); @@ -78,7 +78,7 @@ TEST_F(CacheFilterTest, ImmediateHitBody) { ON_CALL(context_.dispatcher_, post(_)).WillByDefault(::testing::InvokeArgument<0>()); // Create filter for request 1 - config_.set_name("envoy.extensions.filters.http.cache.simple"); + config_.set_name("envoy.extensions.http.cache.simple"); CacheFilterSharedPtr filter = makeFilter(); ASSERT_TRUE(filter); diff --git a/test/extensions/filters/http/cache/config_test.cc b/test/extensions/filters/http/cache/config_test.cc index 01e32df4b6767..04b265830bce9 100644 --- a/test/extensions/filters/http/cache/config_test.cc +++ b/test/extensions/filters/http/cache/config_test.cc @@ -14,14 +14,14 @@ namespace { class CacheFilterFactoryTest : public ::testing::Test { protected: - envoy::extensions::filters::http::cache::v3::CacheConfig config_; + envoy::config::filter::http::cache::v3::CacheConfig config_; NiceMock context_; CacheFilterFactory factory_; Http::MockFilterChainFactoryCallbacks filter_callback_; }; TEST_F(CacheFilterFactoryTest, Basic) { - config_.set_name("envoy.extensions.filters.http.cache.simple"); + config_.set_name("envoy.extensions.http.cache.simple"); Http::FilterFactoryCb cb = factory_.createFilterFactoryFromProto(config_, "stats", context_); Http::StreamFilterSharedPtr filter; EXPECT_CALL(filter_callback_, addStreamFilter(_)).WillOnce(::testing::SaveArg<0>(&filter)); diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index dff7a45360fa3..0369cbc2699d5 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -205,11 +205,11 @@ TEST_F(SimpleHttpCacheTest, StreamingPut) { } TEST(Registration, GetFactory) { - envoy::extensions::filters::http::cache::v3::CacheConfig config; + envoy::config::filter::http::cache::v3::CacheConfig config; HttpCacheFactory* factory = - Registry::FactoryRegistry::getFactory("envoy.extensions.filters.http.cache.simple"); + Registry::FactoryRegistry::getFactory("envoy.extensions.http.cache.simple"); ASSERT_NE(factory, nullptr); - EXPECT_EQ(factory->getCache(config).cacheInfo().name_, "envoy.extensions.filters.http.cache.simple"); + EXPECT_EQ(factory->getCache(config).cacheInfo().name_, "envoy.extensions.http.cache.simple"); } } // namespace From 50d20a6e819935ce2e977f4fdcd5cd31a55db461 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 24 Jan 2020 13:39:58 -0800 Subject: [PATCH 103/109] Revert "Update v2->v3" This reverts commit 2028b999d271f801255b723e4bfd33c5888f27d4. Signed-off-by: Todd Greer --- .../http/http_filters/cache_filter.rst | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/docs/root/configuration/http/http_filters/cache_filter.rst b/docs/root/configuration/http/http_filters/cache_filter.rst index 9a8427ea22cde..232ae6f8dfc8e 100644 --- a/docs/root/configuration/http/http_filters/cache_filter.rst +++ b/docs/root/configuration/http/http_filters/cache_filter.rst @@ -12,11 +12,11 @@ there is enough repeatedly served cacheable content. Configuration ------------- -* :ref:`Configuration API ` +* :ref:`Configuration API ` * This filter should be configured with the name *envoy.filters.http.cache*. The only required configuration field is :ref:`name -`, which must +`, which must specify a valid cache storage implementation linked into your Envoy binary. Specifying 'envoy.extensions.http.cache.simple' will select a proof-of-concept implementation included in the Envoy source. More implementations can (and will) @@ -24,18 +24,17 @@ be provided by implementing Envoy::Extensions::HttpFilters::Cache::HttpCache. To write a cache storage implementation, see :repo:`Writing Cache Filter Implementations ` -.. TODO(toddmgreer) Describe other fields as they get implemented. - The remaining configuration fields control caching behavior and limits. By - default, this filter will cache almost all responses that are considered - cacheable by `RFC7234 `_, with handling - of conditional (`RFC7232 `_), and *range* - (`RFC7233 `_) requests. Those RFC define - which request methods and response codes are cacheable, subject to the - cache-related headers they also define: *cache-control*, *range*, *if-match*, - *if-none-match*, *if-modified-since*, *if-unmodified-since*, *if-range*, *authorization*, - *date*, *age*, *expires*, and *vary*. Responses with a *vary* header will only be cached - if the named headers are listed in :ref:`allowed_vary_headers - ` +The remaining configuration options control caching behavior and limits. By +default, this filter will cache almost all responses that are considered +cacheable by `RFC7234 `_, with handling +of conditional (`RFC7232 `_), and *range* +(`RFC7233 `_) requests. Those RFC define +which request methods and response codes are cacheable, subject to the +cache-related headers they also define: *cache-control*, *range*, *if-match*, +*if-none-match*, *if-modified-since*, *if-unmodified-since*, *if-range*, *authorization*, +*date*, *age*, *expires*, and *vary*. Responses with a *vary* header will only be cached +if the named headers are listed in :ref:`allowed_vary_headers +` Status From fdbc1c54fce3ac19bc11d17fd087b6a12692a058 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 24 Jan 2020 13:40:05 -0800 Subject: [PATCH 104/109] Revert "Reran tools/proto_format.sh fix" This reverts commit 4f26ac8de2441074c53939ab5fe21fc765821d9b. Signed-off-by: Todd Greer --- .../filters/http/cache/v3/cache.proto | 17 ++-- .../envoy/config/filter/http/cache/v3/BUILD | 13 --- .../config/filter/http/cache/v3/cache.proto | 82 ------------------- .../filters/http/cache/v3/cache.proto | 17 ++-- 4 files changed, 10 insertions(+), 119 deletions(-) delete mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD delete mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto diff --git a/api/envoy/extensions/filters/http/cache/v3/cache.proto b/api/envoy/extensions/filters/http/cache/v3/cache.proto index 52e55e07d2f0f..778609306ac14 100644 --- a/api/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3/cache.proto @@ -59,18 +59,11 @@ message CacheConfig { // [#not-implemented-hide:] // // - // List of allowed *Vary* headers. - // - // The *vary* response header holds a list of header names that affect the - // contents of a response, as described by - // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. - // - // During insertion, *allowed_vary_headers* acts as a whitelist: if a - // response's *vary* header mentions any header names that aren't in - // *allowed_vary_headers*, that response will not be cached. - // - // During lookup, *allowed_vary_headers* controls what request headers will be - // sent to the cache storage implementation. + // List of allowed *Vary* headers. Headers matched by allowed_vary_headers will + // be provided to caches in lookups, and the cache filter will only insert + // responses whose *Vary* headers (if any) are matched by a matcher in this + // list. If allowed_vary_headers is empty, responses with *vary* headers will + // not be cached. repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 3; // [#not-implemented-hide:] diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD b/generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD deleted file mode 100644 index 87746bf482e03..0000000000000 --- a/generated_api_shadow/envoy/config/filter/http/cache/v3/BUILD +++ /dev/null @@ -1,13 +0,0 @@ -# DO NOT EDIT. This file is generated by tools/proto_sync.py. - -load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") - -licenses(["notice"]) # Apache 2 - -api_proto_package( - deps = [ - "//envoy/api/v2/route:pkg", - "//envoy/type/matcher:pkg", - "@com_github_cncf_udpa//udpa/annotations:pkg", - ], -) diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto deleted file mode 100644 index d7f075f42d07c..0000000000000 --- a/generated_api_shadow/envoy/config/filter/http/cache/v3/cache.proto +++ /dev/null @@ -1,82 +0,0 @@ -syntax = "proto3"; - -package envoy.config.filter.http.cache.v3; - -import "envoy/api/v2/route/route_components.proto"; -import "envoy/type/matcher/string.proto"; - -import "google/protobuf/any.proto"; - -import "udpa/annotations/migrate.proto"; -import "validate/validate.proto"; - -option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v3"; -option java_outer_classname = "CacheProto"; -option java_multiple_files = true; -option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3"; - -// [#protodoc-title: HTTP Cache Filter] -// HTTP Cache Filter :ref:`overview `. -// [#extension: envoy.filters.http.cache] - -// [#next-free-field: 6] -message CacheConfig { - // [#not-implemented-hide:] - // Modifies cache key creation by restricting which parts of the URL are included. - message KeyCreatorParams { - // If true, exclude the URL scheme from the cache key. Set to true if your origins always - // produce the same response for http and https requests. - bool exclude_scheme = 1; - - // If true, exclude the host from the cache key. Set to true if your origins' responses don't - // ever depend on host. - bool exclude_host = 2; - - // If *query_parameters_included* is nonempty, only query parameters matched - // by one or more of its matchers are included in the cache key. Any other - // query params will not affect cache lookup. - repeated api.v2.route.QueryParameterMatcher query_parameters_included = 3; - - // If *query_parameters_excluded* is nonempty, query parameters matched by one - // or more of its matchers are excluded from the cache key (even if also - // matched by *query_parameters_included*), and will not affect cache lookup. - repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; - } - - // Config specific to the cache storage implementation. - google.protobuf.Any typed_config = 1; - - // Name of cache implementation to use, as specified in the intended HttpCacheFactory - // implementation. Cache names should use reverse DNS format, though this is not enforced. - string name = 2; - - // [#not-implemented-hide:] - // - // - // List of allowed *Vary* headers. - // - // The *vary* response header holds a list of header names that affect the - // contents of a response, as described by - // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. - // - // During insertion, *allowed_vary_headers* acts as a whitelist: if a - // response's *vary* header mentions any header names that aren't in - // *allowed_vary_headers*, that response will not be cached. - // - // During lookup, *allowed_vary_headers* controls what request headers will be - // sent to the cache storage implementation. - repeated type.matcher.StringMatcher allowed_vary_headers = 3; - - // [#not-implemented-hide:] - // - // - // Modifies cache key creation by restricting which parts of the URL are included. - KeyCreatorParams key_creator_params = 4; - - // [#not-implemented-hide:] - // - // - // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache - // storage implementation may have its own limit beyond which it will reject insertions). - uint32 max_body_bytes = 5; -} diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto index 2916d40af006e..ea6cafbf985c6 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto @@ -59,18 +59,11 @@ message CacheConfig { // [#not-implemented-hide:] // // - // List of allowed *Vary* headers. - // - // The *vary* response header holds a list of header names that affect the - // contents of a response, as described by - // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. - // - // During insertion, *allowed_vary_headers* acts as a whitelist: if a - // response's *vary* header mentions any header names that aren't in - // *allowed_vary_headers*, that response will not be cached. - // - // During lookup, *allowed_vary_headers* controls what request headers will be - // sent to the cache storage implementation. + // List of allowed *Vary* headers. Headers matched by allowed_vary_headers will + // be provided to caches in lookups, and the cache filter will only insert + // responses whose *Vary* headers (if any) are matched by a matcher in this + // list. If allowed_vary_headers is empty, responses with *vary* headers will + // not be cached. repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 3; // [#not-implemented-hide:] From 353e8a8665ed3812bda6102c3fa8e75bd6e46cdb Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 24 Jan 2020 13:44:52 -0800 Subject: [PATCH 105/109] Revert "Put CacheConfig in v3" This reverts commit 386c18b982c8e7f10c1ca75111b399d634ca6b84. Signed-off-by: Todd Greer --- api/BUILD | 2 +- api/docs/BUILD | 2 +- .../config/filter/http/cache/{v3 => v2}/BUILD | 0 .../filter/http/cache/{v3 => v2}/cache.proto | 7 +-- .../extensions/filters/http/cache/v3/BUILD | 2 +- .../filters/http/cache/v3/cache.proto | 49 ++++++--------- generated_api_shadow/BUILD | 2 +- .../envoy/config/filter/http/cache/v2/BUILD | 13 ++++ .../config/filter/http/cache/v2/cache.proto | 62 +++++++++++++++++++ .../extensions/filters/http/cache/v3/BUILD | 2 +- .../filters/http/cache/v3/cache.proto | 47 +++++--------- source/extensions/filters/http/cache/BUILD | 6 +- .../filters/http/cache/cache_filter.cc | 4 +- .../filters/http/cache/cache_filter.h | 8 +-- .../extensions/filters/http/cache/config.cc | 4 +- source/extensions/filters/http/cache/config.h | 8 +-- .../filters/http/cache/http_cache.h | 6 +- .../filters/http/cache/simple_http_cache.cc | 2 +- .../cache/cache_filter_integration_test.cc | 2 +- .../filters/http/cache/cache_filter_test.cc | 2 +- .../filters/http/cache/config_test.cc | 2 +- .../http/cache/simple_http_cache_test.cc | 2 +- 22 files changed, 140 insertions(+), 94 deletions(-) rename api/envoy/config/filter/http/cache/{v3 => v2}/BUILD (100%) rename api/envoy/config/filter/http/cache/{v3 => v2}/cache.proto (96%) create mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD create mode 100644 generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto diff --git a/api/BUILD b/api/BUILD index b02693b16fad1..c2485ab3f7786 100644 --- a/api/BUILD +++ b/api/BUILD @@ -35,6 +35,7 @@ proto_library( "//envoy/config/filter/fault/v2:pkg", "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", + "//envoy/config/filter/http/cache/v2:pkg", "//envoy/config/filter/http/cors/v2:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", @@ -126,7 +127,6 @@ proto_library( "//envoy/extensions/filters/common/fault/v3alpha:pkg", "//envoy/extensions/filters/http/adaptive_concurrency/v3alpha:pkg", "//envoy/extensions/filters/http/buffer/v3alpha:pkg", - "//envoy/extensions/filters/http/cache/v3:pkg", "//envoy/extensions/filters/http/cors/v3alpha:pkg", "//envoy/extensions/filters/http/csrf/v3alpha:pkg", "//envoy/extensions/filters/http/dynamic_forward_proxy/v3alpha:pkg", diff --git a/api/docs/BUILD b/api/docs/BUILD index 8019ca910bf0f..34055360967f5 100644 --- a/api/docs/BUILD +++ b/api/docs/BUILD @@ -35,6 +35,7 @@ proto_library( "//envoy/config/filter/fault/v2:pkg", "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", + "//envoy/config/filter/http/cache/v2:pkg", "//envoy/config/filter/http/cors/v2:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", @@ -103,7 +104,6 @@ proto_library( "//envoy/data/cluster/v2alpha:pkg", "//envoy/data/core/v2alpha:pkg", "//envoy/data/tap/v2alpha:pkg", - "//envoy/extensions/filters/http/cache/v3:pkg", "//envoy/service/accesslog/v2:pkg", "//envoy/service/auth/v2:pkg", "//envoy/service/discovery/v2:pkg", diff --git a/api/envoy/config/filter/http/cache/v3/BUILD b/api/envoy/config/filter/http/cache/v2/BUILD similarity index 100% rename from api/envoy/config/filter/http/cache/v3/BUILD rename to api/envoy/config/filter/http/cache/v2/BUILD diff --git a/api/envoy/config/filter/http/cache/v3/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto similarity index 96% rename from api/envoy/config/filter/http/cache/v3/cache.proto rename to api/envoy/config/filter/http/cache/v2/cache.proto index d7f075f42d07c..78876822cdf84 100644 --- a/api/envoy/config/filter/http/cache/v3/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -1,16 +1,14 @@ syntax = "proto3"; -package envoy.config.filter.http.cache.v3; +package envoy.config.filter.http.cache.v2; import "envoy/api/v2/route/route_components.proto"; import "envoy/type/matcher/string.proto"; -import "google/protobuf/any.proto"; - import "udpa/annotations/migrate.proto"; import "validate/validate.proto"; -option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v3"; +option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2"; option java_outer_classname = "CacheProto"; option java_multiple_files = true; option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3"; @@ -19,7 +17,6 @@ option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filte // HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] -// [#next-free-field: 6] message CacheConfig { // [#not-implemented-hide:] // Modifies cache key creation by restricting which parts of the URL are included. diff --git a/api/envoy/extensions/filters/http/cache/v3/BUILD b/api/envoy/extensions/filters/http/cache/v3/BUILD index f0e18b47a9591..bc709054ec56a 100644 --- a/api/envoy/extensions/filters/http/cache/v3/BUILD +++ b/api/envoy/extensions/filters/http/cache/v3/BUILD @@ -6,7 +6,7 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/config/filter/http/cache/v3:pkg", + "//envoy/config/filter/http/cache/v2:pkg", "//envoy/config/route/v3alpha:pkg", "//envoy/type/matcher/v3alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", diff --git a/api/envoy/extensions/filters/http/cache/v3/cache.proto b/api/envoy/extensions/filters/http/cache/v3/cache.proto index 778609306ac14..4d9c0be5c9814 100644 --- a/api/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3/cache.proto @@ -5,8 +5,6 @@ package envoy.extensions.filters.http.cache.v3; import "envoy/api/v2/route/route_components.proto"; import "envoy/type/matcher/v3alpha/string.proto"; -import "google/protobuf/any.proto"; - import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; @@ -18,17 +16,14 @@ option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] // HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] - -// [#next-free-field: 6] message CacheConfig { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v3.CacheConfig"; + "envoy.config.filter.http.cache.v2.CacheConfig"; - // [#not-implemented-hide:] // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v3.CacheConfig.KeyCreatorParams"; + "envoy.config.filter.http.cache.v2.CacheConfig.KeyCreatorParams"; // If true, exclude the URL scheme from the cache key. Set to true if your origins always // produce the same response for http and https requests. @@ -38,44 +33,36 @@ message CacheConfig { // ever depend on host. bool exclude_host = 2; - // If *query_parameters_included* is nonempty, only query parameters matched + // If query_parameters_included is nonempty, only query parameters matched // by one or more of its matchers are included in the cache key. Any other // query params will not affect cache lookup. repeated envoy.api.v2.route.QueryParameterMatcher query_parameters_included = 3; - // If *query_parameters_excluded* is nonempty, query parameters matched by one + // If query_parameters_excluded is nonempty, query parameters matched by one // or more of its matchers are excluded from the cache key (even if also - // matched by *query_parameters_included*), and will not affect cache lookup. - repeated envoy.api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; + // matched by query_parameters_included), and will not affect cache lookup. + repeated config.route.v3alpha.QueryParameterMatcher query_parameters_excluded = 4; } - // Config specific to the cache storage implementation. - google.protobuf.Any typed_config = 1; - - // Name of cache implementation to use, as specified in the intended HttpCacheFactory - // implementation. Cache names should use reverse DNS format, though this is not enforced. - string name = 2; + // Name of cache implementation to use. + string name = 1 [(validate.rules).string = {min_bytes: 1}]; - // [#not-implemented-hide:] - // + // TODO(toddmgreer) implement vary headers // - // List of allowed *Vary* headers. Headers matched by allowed_vary_headers will + // List of allowed Vary headers. Headers matched by allowed_vary_headers will // be provided to caches in lookups, and the cache filter will only insert - // responses whose *Vary* headers (if any) are matched by a matcher in this - // list. If allowed_vary_headers is empty, responses with *vary* headers will + // responses whose Vary headers (if any) are matched my a matcher in this + // list. If allowed_vary_headers is empty, responses with 'vary' headers will // not be cached. - repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 3; + repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 2; - // [#not-implemented-hide:] - // + // TODO(toddmgreer) implement key customization // // Modifies cache key creation by restricting which parts of the URL are included. - KeyCreatorParams key_creator_params = 4; + KeyCreatorParams key_creator_params = 3; - // [#not-implemented-hide:] - // + // TODO(toddmgreer) implement size limit // - // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache - // storage implementation may have its own limit beyond which it will reject insertions). - uint32 max_body_bytes = 5; + // Max body size the cache filter will insert into a cache. + uint32 max_body_bytes = 4; } diff --git a/generated_api_shadow/BUILD b/generated_api_shadow/BUILD index b02693b16fad1..c2485ab3f7786 100644 --- a/generated_api_shadow/BUILD +++ b/generated_api_shadow/BUILD @@ -35,6 +35,7 @@ proto_library( "//envoy/config/filter/fault/v2:pkg", "//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg", "//envoy/config/filter/http/buffer/v2:pkg", + "//envoy/config/filter/http/cache/v2:pkg", "//envoy/config/filter/http/cors/v2:pkg", "//envoy/config/filter/http/csrf/v2:pkg", "//envoy/config/filter/http/dynamic_forward_proxy/v2alpha:pkg", @@ -126,7 +127,6 @@ proto_library( "//envoy/extensions/filters/common/fault/v3alpha:pkg", "//envoy/extensions/filters/http/adaptive_concurrency/v3alpha:pkg", "//envoy/extensions/filters/http/buffer/v3alpha:pkg", - "//envoy/extensions/filters/http/cache/v3:pkg", "//envoy/extensions/filters/http/cors/v3alpha:pkg", "//envoy/extensions/filters/http/csrf/v3alpha:pkg", "//envoy/extensions/filters/http/dynamic_forward_proxy/v3alpha:pkg", diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD b/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD new file mode 100644 index 0000000000000..87746bf482e03 --- /dev/null +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/api/v2/route:pkg", + "//envoy/type/matcher:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto new file mode 100644 index 0000000000000..9a97835fcb37d --- /dev/null +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto @@ -0,0 +1,62 @@ +syntax = "proto3"; + +package envoy.config.filter.http.cache.v2; + +import "envoy/api/v2/route/route_components.proto"; +import "envoy/type/matcher/string.proto"; + +import "udpa/annotations/migrate.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.config.filter.http.cache.v2"; +option java_outer_classname = "CacheProto"; +option java_multiple_files = true; +option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filters.http.cache.v3"; + +// [#protodoc-title: HTTP Cache Filter] +// HTTP Cache Filter :ref:`overview `. +// [#extension: envoy.filters.http.cache] +message CacheConfig { + // Modifies cache key creation by restricting which parts of the URL are included. + message KeyCreatorParams { + // If true, exclude the URL scheme from the cache key. Set to true if your origins always + // produce the same response for http and https requests. + bool exclude_scheme = 1; + + // If true, exclude the host from the cache key. Set to true if your origins' responses don't + // ever depend on host. + bool exclude_host = 2; + + // If query_parameters_included is nonempty, only query parameters matched + // by one or more of its matchers are included in the cache key. Any other + // query params will not affect cache lookup. + repeated api.v2.route.QueryParameterMatcher query_parameters_included = 3; + + // If query_parameters_excluded is nonempty, query parameters matched by one + // or more of its matchers are excluded from the cache key (even if also + // matched by query_parameters_included), and will not affect cache lookup. + repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; + } + + // Name of cache implementation to use. + string name = 1 [(validate.rules).string = {min_bytes: 1}]; + + // TODO(toddmgreer) implement vary headers + // + // List of allowed Vary headers. Headers matched by allowed_vary_headers will + // be provided to caches in lookups, and the cache filter will only insert + // responses whose Vary headers (if any) are matched my a matcher in this + // list. If allowed_vary_headers is empty, responses with 'vary' headers will + // not be cached. + repeated type.matcher.StringMatcher allowed_vary_headers = 2; + + // TODO(toddmgreer) implement key customization + // + // Modifies cache key creation by restricting which parts of the URL are included. + KeyCreatorParams key_creator_params = 3; + + // TODO(toddmgreer) implement size limit + // + // Max body size the cache filter will insert into a cache. + uint32 max_body_bytes = 4; +} diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD index f0e18b47a9591..bc709054ec56a 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD @@ -6,7 +6,7 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ - "//envoy/config/filter/http/cache/v3:pkg", + "//envoy/config/filter/http/cache/v2:pkg", "//envoy/config/route/v3alpha:pkg", "//envoy/type/matcher/v3alpha:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto index ea6cafbf985c6..624df52aa0632 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto @@ -5,8 +5,6 @@ package envoy.extensions.filters.http.cache.v3; import "envoy/config/route/v3alpha/route_components.proto"; import "envoy/type/matcher/v3alpha/string.proto"; -import "google/protobuf/any.proto"; - import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; @@ -18,17 +16,14 @@ option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] // HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] - -// [#next-free-field: 6] message CacheConfig { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v3.CacheConfig"; + "envoy.config.filter.http.cache.v2.CacheConfig"; - // [#not-implemented-hide:] // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { option (udpa.annotations.versioning).previous_message_type = - "envoy.config.filter.http.cache.v3.CacheConfig.KeyCreatorParams"; + "envoy.config.filter.http.cache.v2.CacheConfig.KeyCreatorParams"; // If true, exclude the URL scheme from the cache key. Set to true if your origins always // produce the same response for http and https requests. @@ -38,44 +33,36 @@ message CacheConfig { // ever depend on host. bool exclude_host = 2; - // If *query_parameters_included* is nonempty, only query parameters matched + // If query_parameters_included is nonempty, only query parameters matched // by one or more of its matchers are included in the cache key. Any other // query params will not affect cache lookup. repeated config.route.v3alpha.QueryParameterMatcher query_parameters_included = 3; - // If *query_parameters_excluded* is nonempty, query parameters matched by one + // If query_parameters_excluded is nonempty, query parameters matched by one // or more of its matchers are excluded from the cache key (even if also - // matched by *query_parameters_included*), and will not affect cache lookup. + // matched by query_parameters_included), and will not affect cache lookup. repeated config.route.v3alpha.QueryParameterMatcher query_parameters_excluded = 4; } - // Config specific to the cache storage implementation. - google.protobuf.Any typed_config = 1; - - // Name of cache implementation to use, as specified in the intended HttpCacheFactory - // implementation. Cache names should use reverse DNS format, though this is not enforced. - string name = 2; + // Name of cache implementation to use. + string name = 1 [(validate.rules).string = {min_bytes: 1}]; - // [#not-implemented-hide:] - // + // TODO(toddmgreer) implement vary headers // - // List of allowed *Vary* headers. Headers matched by allowed_vary_headers will + // List of allowed Vary headers. Headers matched by allowed_vary_headers will // be provided to caches in lookups, and the cache filter will only insert - // responses whose *Vary* headers (if any) are matched by a matcher in this - // list. If allowed_vary_headers is empty, responses with *vary* headers will + // responses whose Vary headers (if any) are matched my a matcher in this + // list. If allowed_vary_headers is empty, responses with 'vary' headers will // not be cached. - repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 3; + repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 2; - // [#not-implemented-hide:] - // + // TODO(toddmgreer) implement key customization // // Modifies cache key creation by restricting which parts of the URL are included. - KeyCreatorParams key_creator_params = 4; + KeyCreatorParams key_creator_params = 3; - // [#not-implemented-hide:] - // + // TODO(toddmgreer) implement size limit // - // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache - // storage implementation may have its own limit beyond which it will reject insertions). - uint32 max_body_bytes = 5; + // Max body size the cache filter will insert into a cache. + uint32 max_body_bytes = 4; } diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index d3b4e9bd27081..384c3b39f632e 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -26,7 +26,7 @@ envoy_cc_library( "//source/common/http:headers_lib", "//source/common/protobuf", "//source/extensions/filters/http/common:pass_through_filter_lib", - "@envoy_api//envoy/config/filter/http/cache/v3:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/cache/v2:pkg_cc_proto", ], ) @@ -44,7 +44,7 @@ envoy_cc_extension( "//include/envoy/stats:stats_interface", "//source/extensions/filters/http:well_known_names", "//source/extensions/filters/http/common:factory_base_lib", - "@envoy_api//envoy/config/filter/http/cache/v3:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/cache/v2:pkg_cc_proto", ], ) @@ -66,7 +66,7 @@ envoy_cc_library( "//source/common/common:assert_lib", "//source/common/http:headers_lib", "//source/common/protobuf:utility_lib", - "@envoy_api//envoy/config/filter/http/cache/v3:pkg_cc_proto", + "@envoy_api//envoy/config/filter/http/cache/v2:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index ca0bffb440d90..156c0d2991902 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -37,11 +37,11 @@ bool CacheFilter::isCacheableResponse(Http::HeaderMap& headers) { } HttpCache& -CacheFilter::getCache(const envoy::config::filter::http::cache::v3::CacheConfig& config) { +CacheFilter::getCache(const envoy::config::filter::http::cache::v2::CacheConfig& config) { return Config::Utility::getAndCheckFactory(config).getCache(config); } -CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v3::CacheConfig& config, +CacheFilter::CacheFilter(const envoy::config::filter::http::cache::v2::CacheConfig& config, const std::string&, Stats::Scope&, TimeSource& time_source) : time_source_(time_source), cache_(getCache(config)) {} diff --git a/source/extensions/filters/http/cache/cache_filter.h b/source/extensions/filters/http/cache/cache_filter.h index e5d7bd34ec62f..8e68342b1edec 100644 --- a/source/extensions/filters/http/cache/cache_filter.h +++ b/source/extensions/filters/http/cache/cache_filter.h @@ -5,7 +5,7 @@ #include #include -#include "envoy/config/filter/http/cache/v3/cache.pb.h" +#include "envoy/config/filter/http/cache/v2/cache.pb.h" #include "common/common/logger.h" @@ -30,7 +30,7 @@ class CacheFilter : public Http::PassThroughFilter, public: // Throws ProtoValidationException if no registered HttpCacheFactory for config.name. static CacheFilterSharedPtr - make(const envoy::config::filter::http::cache::v3::CacheConfig& config, + make(const envoy::config::filter::http::cache::v2::CacheConfig& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source) { // Can't use make_shared due to private constructor. return std::shared_ptr(new CacheFilter(config, stats_prefix, scope, time_source)); @@ -47,7 +47,7 @@ class CacheFilter : public Http::PassThroughFilter, // Throws EnvoyException if no registered HttpCacheFactory for config.name. // Constructor is private to enforce enable_shared_from_this's requirement that this must be owned // by a shared_ptr. - CacheFilter(const envoy::config::filter::http::cache::v3::CacheConfig& config, + CacheFilter(const envoy::config::filter::http::cache::v2::CacheConfig& config, const std::string& stats_prefix, Stats::Scope& scope, TimeSource& time_source); void getBody(); @@ -64,7 +64,7 @@ class CacheFilter : public Http::PassThroughFilter, // These don't require private access, but are members per envoy convention. static bool isCacheableRequest(Http::HeaderMap& headers); static bool isCacheableResponse(Http::HeaderMap& headers); - static HttpCache& getCache(const envoy::config::filter::http::cache::v3::CacheConfig& config); + static HttpCache& getCache(const envoy::config::filter::http::cache::v2::CacheConfig& config); TimeSource& time_source_; HttpCache& cache_; diff --git a/source/extensions/filters/http/cache/config.cc b/source/extensions/filters/http/cache/config.cc index 2a790b6548e18..c043cf2cd4e1e 100644 --- a/source/extensions/filters/http/cache/config.cc +++ b/source/extensions/filters/http/cache/config.cc @@ -3,7 +3,7 @@ #include #include "envoy/common/time.h" -#include "envoy/config/filter/http/cache/v3/cache.pb.validate.h" +#include "envoy/config/filter/http/cache/v2/cache.pb.validate.h" #include "envoy/registry/registry.h" #include "envoy/stats/scope.h" @@ -15,7 +15,7 @@ namespace HttpFilters { namespace Cache { Http::FilterFactoryCb CacheFilterFactory::createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v3::CacheConfig& config, + const envoy::config::filter::http::cache::v2::CacheConfig& config, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { return [config, stats_prefix, &context](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter( diff --git a/source/extensions/filters/http/cache/config.h b/source/extensions/filters/http/cache/config.h index 36b99cf009932..d740194676925 100644 --- a/source/extensions/filters/http/cache/config.h +++ b/source/extensions/filters/http/cache/config.h @@ -1,7 +1,7 @@ #pragma once -#include "envoy/config/filter/http/cache/v3/cache.pb.h" -#include "envoy/config/filter/http/cache/v3/cache.pb.validate.h" +#include "envoy/config/filter/http/cache/v2/cache.pb.h" +#include "envoy/config/filter/http/cache/v2/cache.pb.validate.h" #include "extensions/filters/http/cache/cache_filter.h" #include "extensions/filters/http/common/factory_base.h" @@ -13,13 +13,13 @@ namespace HttpFilters { namespace Cache { class CacheFilterFactory - : public Common::FactoryBase { + : public Common::FactoryBase { public: CacheFilterFactory() : FactoryBase(HttpFilterNames::get().Cache) {} private: Http::FilterFactoryCb createFilterFactoryFromProtoTyped( - const envoy::config::filter::http::cache::v3::CacheConfig& config, + const envoy::config::filter::http::cache::v2::CacheConfig& config, const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; }; diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index a332edb36e412..2c2dfe2cc26db 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -6,7 +6,7 @@ #include "envoy/buffer/buffer.h" #include "envoy/common/time.h" -#include "envoy/config/filter/http/cache/v3/cache.pb.h" +#include "envoy/config/filter/http/cache/v2/cache.pb.h" #include "envoy/http/header_map.h" #include "common/common/assert.h" @@ -167,7 +167,7 @@ class LookupRequest { Key& key() { return key_; } // Returns the subset of this request's headers that are listed in - // envoy::config::filter::http::cache::v3::CacheConfig::allowed_vary_headers. If a cache storage + // envoy::config::filter::http::cache::v2alpha::CacheConfig::allowed_vary_headers. If a cache storage // implementation forwards lookup requests to a remote cache server that supports *vary* headers, // that server may need to see these headers. For local implementations, it may be simpler to // instead call makeLookupResult with each potential response. @@ -310,7 +310,7 @@ class HttpCacheFactory { // Returns an HttpCache that will remain valid indefinitely (at least as long // as the calling CacheFilter). virtual HttpCache& - getCache(const envoy::config::filter::http::cache::v3::CacheConfig& config) PURE; + getCache(const envoy::config::filter::http::cache::v2::CacheConfig& config) PURE; virtual ~HttpCacheFactory() = default; private: diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index 620619a1cc890..e377130877290 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -131,7 +131,7 @@ CacheInfo SimpleHttpCache::cacheInfo() const { class SimpleHttpCacheFactory : public HttpCacheFactory { public: SimpleHttpCacheFactory() : HttpCacheFactory("envoy.extensions.http.cache.simple") {} - HttpCache& getCache(const envoy::config::filter::http::cache::v3::CacheConfig&) override { + HttpCache& getCache(const envoy::config::filter::http::cache::v2::CacheConfig&) override { return cache_; } diff --git a/test/extensions/filters/http/cache/cache_filter_integration_test.cc b/test/extensions/filters/http/cache/cache_filter_integration_test.cc index a429bd73f9101..4444d51d492f1 100644 --- a/test/extensions/filters/http/cache/cache_filter_integration_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_integration_test.cc @@ -28,7 +28,7 @@ class CacheIntegrationTest : public Event::TestUsingSimulatedTime, const std::string default_config{R"EOF( name: envoy.filters.http.cache typed_config: - "@type": type.googleapis.com/envoy.config.filter.http.cache.v3.CacheConfig + "@type": type.googleapis.com/envoy.config.filter.http.cache.v2.CacheConfig name: envoy.extensions.http.cache.simple )EOF"}; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index bf53fe0ca4a24..18487c7905748 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -14,7 +14,7 @@ namespace { class CacheFilterTest : public ::testing::Test { protected: - envoy::config::filter::http::cache::v3::CacheConfig config_; + envoy::config::filter::http::cache::v2::CacheConfig config_; NiceMock context_; Event::SimulatedTimeSystem time_source_; DateFormatter formatter_{"%a, %d %b %Y %H:%M:%S GMT"}; diff --git a/test/extensions/filters/http/cache/config_test.cc b/test/extensions/filters/http/cache/config_test.cc index 04b265830bce9..c2b942c88408d 100644 --- a/test/extensions/filters/http/cache/config_test.cc +++ b/test/extensions/filters/http/cache/config_test.cc @@ -14,7 +14,7 @@ namespace { class CacheFilterFactoryTest : public ::testing::Test { protected: - envoy::config::filter::http::cache::v3::CacheConfig config_; + envoy::config::filter::http::cache::v2::CacheConfig config_; NiceMock context_; CacheFilterFactory factory_; Http::MockFilterChainFactoryCallbacks filter_callback_; diff --git a/test/extensions/filters/http/cache/simple_http_cache_test.cc b/test/extensions/filters/http/cache/simple_http_cache_test.cc index 0369cbc2699d5..39b6c5a1c6bd9 100644 --- a/test/extensions/filters/http/cache/simple_http_cache_test.cc +++ b/test/extensions/filters/http/cache/simple_http_cache_test.cc @@ -205,7 +205,7 @@ TEST_F(SimpleHttpCacheTest, StreamingPut) { } TEST(Registration, GetFactory) { - envoy::config::filter::http::cache::v3::CacheConfig config; + envoy::config::filter::http::cache::v2::CacheConfig config; HttpCacheFactory* factory = Registry::FactoryRegistry::getFactory("envoy.extensions.http.cache.simple"); ASSERT_NE(factory, nullptr); From 4027ab3040e83c70d2e78a438c0e2e9ebeb9b2c7 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 24 Jan 2020 13:52:14 -0800 Subject: [PATCH 106/109] Got 'tools/proto_format.sh fix' to run successfully. Signed-off-by: Todd Greer --- api/BUILD | 1 + .../config/filter/http/cache/v2/cache.proto | 3 + .../filters/http/cache/v3/cache.proto | 60 ++++++++++++------- generated_api_shadow/BUILD | 1 + .../config/filter/http/cache/v2/cache.proto | 56 +++++++++++------ .../filters/http/cache/v3/cache.proto | 56 +++++++++++------ 6 files changed, 121 insertions(+), 56 deletions(-) diff --git a/api/BUILD b/api/BUILD index c2485ab3f7786..4859faee746ce 100644 --- a/api/BUILD +++ b/api/BUILD @@ -127,6 +127,7 @@ proto_library( "//envoy/extensions/filters/common/fault/v3alpha:pkg", "//envoy/extensions/filters/http/adaptive_concurrency/v3alpha:pkg", "//envoy/extensions/filters/http/buffer/v3alpha:pkg", + "//envoy/extensions/filters/http/cache/v3:pkg", "//envoy/extensions/filters/http/cors/v3alpha:pkg", "//envoy/extensions/filters/http/csrf/v3alpha:pkg", "//envoy/extensions/filters/http/dynamic_forward_proxy/v3alpha:pkg", diff --git a/api/envoy/config/filter/http/cache/v2/cache.proto b/api/envoy/config/filter/http/cache/v2/cache.proto index 78876822cdf84..59c0ffa78b86f 100644 --- a/api/envoy/config/filter/http/cache/v2/cache.proto +++ b/api/envoy/config/filter/http/cache/v2/cache.proto @@ -5,6 +5,8 @@ package envoy.config.filter.http.cache.v2; import "envoy/api/v2/route/route_components.proto"; import "envoy/type/matcher/string.proto"; +import "google/protobuf/any.proto"; + import "udpa/annotations/migrate.proto"; import "validate/validate.proto"; @@ -17,6 +19,7 @@ option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filte // HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] +// [#next-free-field: 6] message CacheConfig { // [#not-implemented-hide:] // Modifies cache key creation by restricting which parts of the URL are included. diff --git a/api/envoy/extensions/filters/http/cache/v3/cache.proto b/api/envoy/extensions/filters/http/cache/v3/cache.proto index 4d9c0be5c9814..7dc35b93889ff 100644 --- a/api/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3/cache.proto @@ -2,9 +2,11 @@ syntax = "proto3"; package envoy.extensions.filters.http.cache.v3; -import "envoy/api/v2/route/route_components.proto"; +import "envoy/config/route/v3alpha/route_components.proto"; import "envoy/type/matcher/v3alpha/string.proto"; +import "google/protobuf/any.proto"; + import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; @@ -16,10 +18,13 @@ option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] // HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] + +// [#next-free-field: 6] message CacheConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.cache.v2.CacheConfig"; + // [#not-implemented-hide:] // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { option (udpa.annotations.versioning).previous_message_type = @@ -33,36 +38,51 @@ message CacheConfig { // ever depend on host. bool exclude_host = 2; - // If query_parameters_included is nonempty, only query parameters matched + // If *query_parameters_included* is nonempty, only query parameters matched // by one or more of its matchers are included in the cache key. Any other // query params will not affect cache lookup. - repeated envoy.api.v2.route.QueryParameterMatcher query_parameters_included = 3; + repeated config.route.v3alpha.QueryParameterMatcher query_parameters_included = 3; - // If query_parameters_excluded is nonempty, query parameters matched by one + // If *query_parameters_excluded* is nonempty, query parameters matched by one // or more of its matchers are excluded from the cache key (even if also - // matched by query_parameters_included), and will not affect cache lookup. + // matched by *query_parameters_included*), and will not affect cache lookup. repeated config.route.v3alpha.QueryParameterMatcher query_parameters_excluded = 4; } - // Name of cache implementation to use. - string name = 1 [(validate.rules).string = {min_bytes: 1}]; + // Config specific to the cache storage implementation. + google.protobuf.Any typed_config = 1; - // TODO(toddmgreer) implement vary headers + // Name of cache implementation to use, as specified in the intended HttpCacheFactory + // implementation. Cache names should use reverse DNS format, though this is not enforced. + string name = 2; + + // [#not-implemented-hide:] + // + // + // List of allowed *Vary* headers. // - // List of allowed Vary headers. Headers matched by allowed_vary_headers will - // be provided to caches in lookups, and the cache filter will only insert - // responses whose Vary headers (if any) are matched my a matcher in this - // list. If allowed_vary_headers is empty, responses with 'vary' headers will - // not be cached. - repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 2; - - // TODO(toddmgreer) implement key customization + // The *vary* response header holds a list of header names that affect the + // contents of a response, as described by + // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. + // + // During insertion, *allowed_vary_headers* acts as a whitelist: if a + // response's *vary* header mentions any header names that aren't in + // *allowed_vary_headers*, that response will not be cached. + // + // During lookup, *allowed_vary_headers* controls what request headers will be + // sent to the cache storage implementation. + repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 3; + + // [#not-implemented-hide:] + // // // Modifies cache key creation by restricting which parts of the URL are included. - KeyCreatorParams key_creator_params = 3; + KeyCreatorParams key_creator_params = 4; - // TODO(toddmgreer) implement size limit + // [#not-implemented-hide:] + // // - // Max body size the cache filter will insert into a cache. - uint32 max_body_bytes = 4; + // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache + // storage implementation may have its own limit beyond which it will reject insertions). + uint32 max_body_bytes = 5; } diff --git a/generated_api_shadow/BUILD b/generated_api_shadow/BUILD index c2485ab3f7786..4859faee746ce 100644 --- a/generated_api_shadow/BUILD +++ b/generated_api_shadow/BUILD @@ -127,6 +127,7 @@ proto_library( "//envoy/extensions/filters/common/fault/v3alpha:pkg", "//envoy/extensions/filters/http/adaptive_concurrency/v3alpha:pkg", "//envoy/extensions/filters/http/buffer/v3alpha:pkg", + "//envoy/extensions/filters/http/cache/v3:pkg", "//envoy/extensions/filters/http/cors/v3alpha:pkg", "//envoy/extensions/filters/http/csrf/v3alpha:pkg", "//envoy/extensions/filters/http/dynamic_forward_proxy/v3alpha:pkg", diff --git a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto index 9a97835fcb37d..59c0ffa78b86f 100644 --- a/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto +++ b/generated_api_shadow/envoy/config/filter/http/cache/v2/cache.proto @@ -5,6 +5,8 @@ package envoy.config.filter.http.cache.v2; import "envoy/api/v2/route/route_components.proto"; import "envoy/type/matcher/string.proto"; +import "google/protobuf/any.proto"; + import "udpa/annotations/migrate.proto"; import "validate/validate.proto"; @@ -16,7 +18,10 @@ option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filte // [#protodoc-title: HTTP Cache Filter] // HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] + +// [#next-free-field: 6] message CacheConfig { + // [#not-implemented-hide:] // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { // If true, exclude the URL scheme from the cache key. Set to true if your origins always @@ -27,36 +32,51 @@ message CacheConfig { // ever depend on host. bool exclude_host = 2; - // If query_parameters_included is nonempty, only query parameters matched + // If *query_parameters_included* is nonempty, only query parameters matched // by one or more of its matchers are included in the cache key. Any other // query params will not affect cache lookup. repeated api.v2.route.QueryParameterMatcher query_parameters_included = 3; - // If query_parameters_excluded is nonempty, query parameters matched by one + // If *query_parameters_excluded* is nonempty, query parameters matched by one // or more of its matchers are excluded from the cache key (even if also - // matched by query_parameters_included), and will not affect cache lookup. + // matched by *query_parameters_included*), and will not affect cache lookup. repeated api.v2.route.QueryParameterMatcher query_parameters_excluded = 4; } - // Name of cache implementation to use. - string name = 1 [(validate.rules).string = {min_bytes: 1}]; + // Config specific to the cache storage implementation. + google.protobuf.Any typed_config = 1; - // TODO(toddmgreer) implement vary headers + // Name of cache implementation to use, as specified in the intended HttpCacheFactory + // implementation. Cache names should use reverse DNS format, though this is not enforced. + string name = 2; + + // [#not-implemented-hide:] + // + // + // List of allowed *Vary* headers. // - // List of allowed Vary headers. Headers matched by allowed_vary_headers will - // be provided to caches in lookups, and the cache filter will only insert - // responses whose Vary headers (if any) are matched my a matcher in this - // list. If allowed_vary_headers is empty, responses with 'vary' headers will - // not be cached. - repeated type.matcher.StringMatcher allowed_vary_headers = 2; - - // TODO(toddmgreer) implement key customization + // The *vary* response header holds a list of header names that affect the + // contents of a response, as described by + // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. + // + // During insertion, *allowed_vary_headers* acts as a whitelist: if a + // response's *vary* header mentions any header names that aren't in + // *allowed_vary_headers*, that response will not be cached. + // + // During lookup, *allowed_vary_headers* controls what request headers will be + // sent to the cache storage implementation. + repeated type.matcher.StringMatcher allowed_vary_headers = 3; + + // [#not-implemented-hide:] + // // // Modifies cache key creation by restricting which parts of the URL are included. - KeyCreatorParams key_creator_params = 3; + KeyCreatorParams key_creator_params = 4; - // TODO(toddmgreer) implement size limit + // [#not-implemented-hide:] + // // - // Max body size the cache filter will insert into a cache. - uint32 max_body_bytes = 4; + // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache + // storage implementation may have its own limit beyond which it will reject insertions). + uint32 max_body_bytes = 5; } diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto index 624df52aa0632..7dc35b93889ff 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto @@ -5,6 +5,8 @@ package envoy.extensions.filters.http.cache.v3; import "envoy/config/route/v3alpha/route_components.proto"; import "envoy/type/matcher/v3alpha/string.proto"; +import "google/protobuf/any.proto"; + import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; @@ -16,10 +18,13 @@ option java_multiple_files = true; // [#protodoc-title: HTTP Cache Filter] // HTTP Cache Filter :ref:`overview `. // [#extension: envoy.filters.http.cache] + +// [#next-free-field: 6] message CacheConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.cache.v2.CacheConfig"; + // [#not-implemented-hide:] // Modifies cache key creation by restricting which parts of the URL are included. message KeyCreatorParams { option (udpa.annotations.versioning).previous_message_type = @@ -33,36 +38,51 @@ message CacheConfig { // ever depend on host. bool exclude_host = 2; - // If query_parameters_included is nonempty, only query parameters matched + // If *query_parameters_included* is nonempty, only query parameters matched // by one or more of its matchers are included in the cache key. Any other // query params will not affect cache lookup. repeated config.route.v3alpha.QueryParameterMatcher query_parameters_included = 3; - // If query_parameters_excluded is nonempty, query parameters matched by one + // If *query_parameters_excluded* is nonempty, query parameters matched by one // or more of its matchers are excluded from the cache key (even if also - // matched by query_parameters_included), and will not affect cache lookup. + // matched by *query_parameters_included*), and will not affect cache lookup. repeated config.route.v3alpha.QueryParameterMatcher query_parameters_excluded = 4; } - // Name of cache implementation to use. - string name = 1 [(validate.rules).string = {min_bytes: 1}]; + // Config specific to the cache storage implementation. + google.protobuf.Any typed_config = 1; - // TODO(toddmgreer) implement vary headers + // Name of cache implementation to use, as specified in the intended HttpCacheFactory + // implementation. Cache names should use reverse DNS format, though this is not enforced. + string name = 2; + + // [#not-implemented-hide:] + // + // + // List of allowed *Vary* headers. // - // List of allowed Vary headers. Headers matched by allowed_vary_headers will - // be provided to caches in lookups, and the cache filter will only insert - // responses whose Vary headers (if any) are matched my a matcher in this - // list. If allowed_vary_headers is empty, responses with 'vary' headers will - // not be cached. - repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 2; - - // TODO(toddmgreer) implement key customization + // The *vary* response header holds a list of header names that affect the + // contents of a response, as described by + // https://httpwg.org/specs/rfc7234.html#caching.negotiated.responses. + // + // During insertion, *allowed_vary_headers* acts as a whitelist: if a + // response's *vary* header mentions any header names that aren't in + // *allowed_vary_headers*, that response will not be cached. + // + // During lookup, *allowed_vary_headers* controls what request headers will be + // sent to the cache storage implementation. + repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 3; + + // [#not-implemented-hide:] + // // // Modifies cache key creation by restricting which parts of the URL are included. - KeyCreatorParams key_creator_params = 3; + KeyCreatorParams key_creator_params = 4; - // TODO(toddmgreer) implement size limit + // [#not-implemented-hide:] + // // - // Max body size the cache filter will insert into a cache. - uint32 max_body_bytes = 4; + // Max body size the cache filter will insert into a cache. 0 means unlimited (though the cache + // storage implementation may have its own limit beyond which it will reject insertions). + uint32 max_body_bytes = 5; } From 1b73cf0f65866868bd9c8946b2f0476440f9d930 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 24 Jan 2020 14:04:01 -0800 Subject: [PATCH 107/109] tools/proto_format.sh fix Signed-off-by: Todd Greer --- api/envoy/extensions/filters/http/cache/v3/BUILD | 4 ++-- api/envoy/extensions/filters/http/cache/v3/cache.proto | 10 +++++----- .../envoy/extensions/filters/http/cache/v3/BUILD | 4 ++-- .../envoy/extensions/filters/http/cache/v3/cache.proto | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/api/envoy/extensions/filters/http/cache/v3/BUILD b/api/envoy/extensions/filters/http/cache/v3/BUILD index bc709054ec56a..0fce9c8de8807 100644 --- a/api/envoy/extensions/filters/http/cache/v3/BUILD +++ b/api/envoy/extensions/filters/http/cache/v3/BUILD @@ -7,8 +7,8 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/config/filter/http/cache/v2:pkg", - "//envoy/config/route/v3alpha:pkg", - "//envoy/type/matcher/v3alpha:pkg", + "//envoy/config/route/v3:pkg", + "//envoy/type/matcher/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/extensions/filters/http/cache/v3/cache.proto b/api/envoy/extensions/filters/http/cache/v3/cache.proto index 7dc35b93889ff..e45f158f5f198 100644 --- a/api/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/api/envoy/extensions/filters/http/cache/v3/cache.proto @@ -2,8 +2,8 @@ syntax = "proto3"; package envoy.extensions.filters.http.cache.v3; -import "envoy/config/route/v3alpha/route_components.proto"; -import "envoy/type/matcher/v3alpha/string.proto"; +import "envoy/config/route/v3/route_components.proto"; +import "envoy/type/matcher/v3/string.proto"; import "google/protobuf/any.proto"; @@ -41,12 +41,12 @@ message CacheConfig { // If *query_parameters_included* is nonempty, only query parameters matched // by one or more of its matchers are included in the cache key. Any other // query params will not affect cache lookup. - repeated config.route.v3alpha.QueryParameterMatcher query_parameters_included = 3; + repeated config.route.v3.QueryParameterMatcher query_parameters_included = 3; // If *query_parameters_excluded* is nonempty, query parameters matched by one // or more of its matchers are excluded from the cache key (even if also // matched by *query_parameters_included*), and will not affect cache lookup. - repeated config.route.v3alpha.QueryParameterMatcher query_parameters_excluded = 4; + repeated config.route.v3.QueryParameterMatcher query_parameters_excluded = 4; } // Config specific to the cache storage implementation. @@ -71,7 +71,7 @@ message CacheConfig { // // During lookup, *allowed_vary_headers* controls what request headers will be // sent to the cache storage implementation. - repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 3; + repeated type.matcher.v3.StringMatcher allowed_vary_headers = 3; // [#not-implemented-hide:] // diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD index bc709054ec56a..0fce9c8de8807 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/BUILD @@ -7,8 +7,8 @@ licenses(["notice"]) # Apache 2 api_proto_package( deps = [ "//envoy/config/filter/http/cache/v2:pkg", - "//envoy/config/route/v3alpha:pkg", - "//envoy/type/matcher/v3alpha:pkg", + "//envoy/config/route/v3:pkg", + "//envoy/type/matcher/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto index 7dc35b93889ff..e45f158f5f198 100644 --- a/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto +++ b/generated_api_shadow/envoy/extensions/filters/http/cache/v3/cache.proto @@ -2,8 +2,8 @@ syntax = "proto3"; package envoy.extensions.filters.http.cache.v3; -import "envoy/config/route/v3alpha/route_components.proto"; -import "envoy/type/matcher/v3alpha/string.proto"; +import "envoy/config/route/v3/route_components.proto"; +import "envoy/type/matcher/v3/string.proto"; import "google/protobuf/any.proto"; @@ -41,12 +41,12 @@ message CacheConfig { // If *query_parameters_included* is nonempty, only query parameters matched // by one or more of its matchers are included in the cache key. Any other // query params will not affect cache lookup. - repeated config.route.v3alpha.QueryParameterMatcher query_parameters_included = 3; + repeated config.route.v3.QueryParameterMatcher query_parameters_included = 3; // If *query_parameters_excluded* is nonempty, query parameters matched by one // or more of its matchers are excluded from the cache key (even if also // matched by *query_parameters_included*), and will not affect cache lookup. - repeated config.route.v3alpha.QueryParameterMatcher query_parameters_excluded = 4; + repeated config.route.v3.QueryParameterMatcher query_parameters_excluded = 4; } // Config specific to the cache storage implementation. @@ -71,7 +71,7 @@ message CacheConfig { // // During lookup, *allowed_vary_headers* controls what request headers will be // sent to the cache storage implementation. - repeated type.matcher.v3alpha.StringMatcher allowed_vary_headers = 3; + repeated type.matcher.v3.StringMatcher allowed_vary_headers = 3; // [#not-implemented-hide:] // From 62d01c81f84f6b5e93d742206ccf083554084b6a Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 24 Jan 2020 14:49:01 -0800 Subject: [PATCH 108/109] Adapt to upstream factory changes Signed-off-by: Todd Greer --- source/extensions/filters/http/cache/BUILD | 1 + source/extensions/filters/http/cache/http_cache.h | 15 ++++++++------- .../filters/http/cache/simple_http_cache.cc | 3 +++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/source/extensions/filters/http/cache/BUILD b/source/extensions/filters/http/cache/BUILD index 384c3b39f632e..0cce18df0d32b 100644 --- a/source/extensions/filters/http/cache/BUILD +++ b/source/extensions/filters/http/cache/BUILD @@ -62,6 +62,7 @@ envoy_cc_library( ":key_cc_proto", "//include/envoy/buffer:buffer_interface", "//include/envoy/common:time_interface", + "//include/envoy/config:typed_config_interface", "//include/envoy/http:header_map_interface", "//source/common/common:assert_lib", "//source/common/http:headers_lib", diff --git a/source/extensions/filters/http/cache/http_cache.h b/source/extensions/filters/http/cache/http_cache.h index 2c2dfe2cc26db..743959e90edd9 100644 --- a/source/extensions/filters/http/cache/http_cache.h +++ b/source/extensions/filters/http/cache/http_cache.h @@ -7,6 +7,7 @@ #include "envoy/buffer/buffer.h" #include "envoy/common/time.h" #include "envoy/config/filter/http/cache/v2/cache.pb.h" +#include "envoy/config/typed_config.h" #include "envoy/http/header_map.h" #include "common/common/assert.h" @@ -167,10 +168,10 @@ class LookupRequest { Key& key() { return key_; } // Returns the subset of this request's headers that are listed in - // envoy::config::filter::http::cache::v2alpha::CacheConfig::allowed_vary_headers. If a cache storage - // implementation forwards lookup requests to a remote cache server that supports *vary* headers, - // that server may need to see these headers. For local implementations, it may be simpler to - // instead call makeLookupResult with each potential response. + // envoy::config::filter::http::cache::v2alpha::CacheConfig::allowed_vary_headers. If a cache + // storage implementation forwards lookup requests to a remote cache server that supports *vary* + // headers, that server may need to see these headers. For local implementations, it may be + // simpler to instead call makeLookupResult with each potential response. HeaderVector& vary_headers() { return vary_headers_; } const HeaderVector& vary_headers() const { return vary_headers_; } @@ -300,12 +301,12 @@ class HttpCache { }; // Factory interface for cache implementations to implement and register. -class HttpCacheFactory { +class HttpCacheFactory : public Config::TypedFactory { public: // name should be in reverse DNS format, though this is not enforced. explicit HttpCacheFactory(std::string name) : name_(std::move(name)) {} - const std::string& name() const { return name_; } - static std::string category() { return "http_cache_factory"; } + std::string name() const override { return name_; } + std::string category() const override { return "http_cache_factory"; } // Returns an HttpCache that will remain valid indefinitely (at least as long // as the calling CacheFilter). diff --git a/source/extensions/filters/http/cache/simple_http_cache.cc b/source/extensions/filters/http/cache/simple_http_cache.cc index e377130877290..ce6717648b1c1 100644 --- a/source/extensions/filters/http/cache/simple_http_cache.cc +++ b/source/extensions/filters/http/cache/simple_http_cache.cc @@ -131,6 +131,9 @@ CacheInfo SimpleHttpCache::cacheInfo() const { class SimpleHttpCacheFactory : public HttpCacheFactory { public: SimpleHttpCacheFactory() : HttpCacheFactory("envoy.extensions.http.cache.simple") {} + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique(); + } HttpCache& getCache(const envoy::config::filter::http::cache::v2::CacheConfig&) override { return cache_; } From c0223c324125b020c16812895cf1131bc0c50aa1 Mon Sep 17 00:00:00 2001 From: Todd Greer Date: Fri, 24 Jan 2020 16:04:11 -0800 Subject: [PATCH 109/109] Comment out rst docs about unenabled config fields, to avoid dangling ref. Signed-off-by: Todd Greer --- .../http/http_filters/cache_filter.rst | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/root/configuration/http/http_filters/cache_filter.rst b/docs/root/configuration/http/http_filters/cache_filter.rst index 232ae6f8dfc8e..6584795f12999 100644 --- a/docs/root/configuration/http/http_filters/cache_filter.rst +++ b/docs/root/configuration/http/http_filters/cache_filter.rst @@ -24,18 +24,18 @@ be provided by implementing Envoy::Extensions::HttpFilters::Cache::HttpCache. To write a cache storage implementation, see :repo:`Writing Cache Filter Implementations ` -The remaining configuration options control caching behavior and limits. By -default, this filter will cache almost all responses that are considered -cacheable by `RFC7234 `_, with handling -of conditional (`RFC7232 `_), and *range* -(`RFC7233 `_) requests. Those RFC define -which request methods and response codes are cacheable, subject to the -cache-related headers they also define: *cache-control*, *range*, *if-match*, -*if-none-match*, *if-modified-since*, *if-unmodified-since*, *if-range*, *authorization*, -*date*, *age*, *expires*, and *vary*. Responses with a *vary* header will only be cached -if the named headers are listed in :ref:`allowed_vary_headers -` - +.. TODO(toddmgreer) Describe other fields as they get implemented. + The remaining configuration fields control caching behavior and limits. By + default, this filter will cache almost all responses that are considered + cacheable by `RFC7234 `_, with handling + of conditional (`RFC7232 `_), and *range* + (`RFC7233 `_) requests. Those RFC define + which request methods and response codes are cacheable, subject to the + cache-related headers they also define: *cache-control*, *range*, *if-match*, + *if-none-match*, *if-modified-since*, *if-unmodified-since*, *if-range*, *authorization*, + *date*, *age*, *expires*, and *vary*. Responses with a *vary* header will only be cached + if the named headers are listed in :ref:`allowed_vary_headers + ` Status ------