Line data Source code
1 : #pragma once 2 : 3 : #include <chrono> 4 : #include <functional> 5 : #include <memory> 6 : 7 : #include "envoy/extensions/http/stateful_session/cookie/v3/cookie.pb.h" 8 : #include "envoy/http/hash_policy.h" 9 : #include "envoy/http/stateful_session.h" 10 : 11 : #include "source/common/common/base64.h" 12 : #include "source/common/http/utility.h" 13 : #include "source/extensions/http/stateful_session/cookie/cookie.pb.h" 14 : 15 : namespace Envoy { 16 : namespace Extensions { 17 : namespace Http { 18 : namespace StatefulSession { 19 : namespace Cookie { 20 : 21 : using CookieBasedSessionStateProto = 22 : envoy::extensions::http::stateful_session::cookie::v3::CookieBasedSessionState; 23 : 24 : class CookieBasedSessionStateFactory : public Envoy::Http::SessionStateFactory { 25 : public: 26 : class SessionStateImpl : public Envoy::Http::SessionState { 27 : public: 28 : SessionStateImpl(absl::optional<std::string> address, 29 : const CookieBasedSessionStateFactory& factory, TimeSource& time_source) 30 0 : : upstream_address_(std::move(address)), factory_(factory), time_source_(time_source) {} 31 : 32 0 : absl::optional<absl::string_view> upstreamAddress() const override { return upstream_address_; } 33 : void onUpdate(const Upstream::HostDescription& host, 34 : Envoy::Http::ResponseHeaderMap& headers) override; 35 : 36 : private: 37 : absl::optional<std::string> upstream_address_; 38 : const CookieBasedSessionStateFactory& factory_; 39 : TimeSource& time_source_; 40 : }; 41 : 42 : CookieBasedSessionStateFactory(const CookieBasedSessionStateProto& config, 43 : TimeSource& time_source); 44 : 45 0 : Envoy::Http::SessionStatePtr create(const Envoy::Http::RequestHeaderMap& headers) const override { 46 0 : if (!requestPathMatch(headers.getPathValue())) { 47 0 : return nullptr; 48 0 : } 49 : 50 0 : return std::make_unique<SessionStateImpl>(parseAddress(headers), *this, time_source_); 51 0 : } 52 : 53 0 : bool requestPathMatch(absl::string_view request_path) const { 54 0 : ASSERT(path_matcher_ != nullptr); 55 0 : return path_matcher_(request_path); 56 0 : } 57 : 58 : private: 59 0 : absl::optional<std::string> parseAddress(const Envoy::Http::RequestHeaderMap& headers) const { 60 0 : const std::string cookie_value = Envoy::Http::Utility::parseCookieValue(headers, name_); 61 0 : if (cookie_value.empty()) { 62 0 : return absl::nullopt; 63 0 : } 64 0 : const std::string decoded_value = Envoy::Base64::decode(cookie_value); 65 0 : std::string address; 66 : 67 : // Try to interpret the cookie as proto payload. 68 : // Otherwise treat it as "old" style format, which is ip-address:port. 69 0 : envoy::Cookie cookie; 70 0 : if (cookie.ParseFromString(decoded_value)) { 71 0 : address = cookie.address(); 72 0 : if (address.empty() || (cookie.expires() == 0)) { 73 0 : return absl::nullopt; 74 0 : } 75 : 76 0 : std::chrono::seconds expiry_time(cookie.expires()); 77 0 : auto now = std::chrono::duration_cast<std::chrono::seconds>( 78 0 : (time_source_.monotonicTime()).time_since_epoch()); 79 0 : if (now > expiry_time) { 80 : // Ignore the address extracted from the cookie. This will cause 81 : // upstream cluster to select a new host and new cookie will be generated. 82 0 : return absl::nullopt; 83 0 : } 84 0 : } else { 85 0 : ENVOY_LOG_ONCE_MISC( 86 0 : warn, "Non-proto cookie format detected. This format will be rejected in the future."); 87 0 : address = decoded_value; 88 0 : } 89 : 90 0 : return address.empty() ? absl::nullopt : absl::make_optional(std::move(address)); 91 0 : } 92 : 93 0 : std::string makeSetCookie(const std::string& address) const { 94 0 : return Envoy::Http::Utility::makeSetCookieValue(name_, address, path_, ttl_, true, attributes_); 95 0 : } 96 : 97 : const std::string name_; 98 : const std::chrono::seconds ttl_; 99 : const std::string path_; 100 : const Envoy::Http::CookieAttributeRefVector attributes_; 101 : TimeSource& time_source_; 102 : 103 : std::function<bool(absl::string_view)> path_matcher_; 104 : }; 105 : 106 : } // namespace Cookie 107 : } // namespace StatefulSession 108 : } // namespace Http 109 : } // namespace Extensions 110 : } // namespace Envoy