Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
f85d68d
router: add start timestamp header config
dio May 2, 2018
b0b741b
docs: update release notes
dio May 2, 2018
4c80a6d
review: add comment
dio May 8, 2018
3110ce0
review: test that the x-request-start header is not present before th…
dio May 8, 2018
42bd291
review: use header manipulation facility instead
dio May 9, 2018
8fcc471
Merge remote-tracking branch 'upstream/master'
dio May 9, 2018
4e74570
Add a test sample to TestParseInternal
dio May 9, 2018
79a3320
tsan: check RatelimitIntegrationTest failure
dio May 9, 2018
f94e020
Rename it to START_TIME_SINCE_EPOCH
dio May 9, 2018
07dc56f
Fix format
dio May 10, 2018
91fb3da
Merge remote-tracking branch 'upstream/master'
dio May 14, 2018
aef2ed9
Allow header parser to accept START_TIME(ARG)
dio May 14, 2018
1970913
Add replacers
dio May 14, 2018
9004414
Use %f as subsecond formatter
dio May 15, 2018
dd15262
Merge remote-tracking branch 'upstream/master'
dio May 15, 2018
ce1d3a0
Add test in accesss_log_formatter
dio May 16, 2018
e98f1a4
Retry ipv6_test
dio May 16, 2018
edbc437
Add a parser to initialize the format_string_
dio May 24, 2018
8919c50
Add a benchmark for date formatter with subseconds
dio May 24, 2018
908fc61
Merge remote-tracking branch 'upstream/master'
dio May 24, 2018
0f417da
Remove unused typedef
dio May 24, 2018
106c3e2
Use shared ptr to cache the format
dio May 24, 2018
cc6c7c9
Try to fix asan build take 1
dio May 24, 2018
4dcfeb0
Try to fix asan build take 2
dio May 24, 2018
7d53b28
Try to fix asan build take 3
dio May 25, 2018
e6cb04e
Copy the ns value
dio May 25, 2018
96eb0c6
Update release notes
dio May 25, 2018
f475839
review: simplify parsing step
dio May 29, 2018
724a203
review: reuse access log's formatter
dio May 29, 2018
24518ef
Use map to cache access log formatter
dio May 29, 2018
0f0b1bf
review: update docs
dio May 29, 2018
f0fa285
Merge remote-tracking branch 'upstream/master'
dio May 29, 2018
f78e7ad
Fix additional specifiers table format
dio May 29, 2018
7aeb767
Use [1-9] instead of {:digit}
dio May 29, 2018
6310447
Fix doc's indentations
dio May 29, 2018
8a1571f
No more find()
dio May 31, 2018
78e8430
Merge remote-tracking branch 'upstream/master'
dio May 31, 2018
325fdc5
Move subsecond offsets computation
dio Jun 2, 2018
4b9fa9e
Merge remote-tracking branch 'upstream/master'
dio Jun 2, 2018
9aba1e9
Move the offsets calc to fromTimeAndPrepareSubsecondOffsets
dio Jun 3, 2018
94e3add
Cleanup unused deps and vars
dio Jun 3, 2018
612f829
Infer seconds_length only when it is needed
dio Jun 3, 2018
96f915e
Precompute the string segments between specifiers
dio Jun 5, 2018
d5f082b
Try to fix asan on using stack after scope: digits
dio Jun 5, 2018
b85b275
Stamp per cache item.
dio Jun 5, 2018
513d4bf
Fix removing an item of a map while iterating it
dio Jun 5, 2018
9a16090
Remove const PLACEHOLDER
dio Jun 6, 2018
e5642ba
Add comments to CachedTime struct
dio Jun 6, 2018
bda6762
Remove last_segment_ and streamline replacement
dio Jun 6, 2018
ec35f6c
Add test with shorter segments
dio Jun 6, 2018
ea5e62f
Remove % since it is diff on osx vs. linux
dio Jun 6, 2018
22a94a4
Use unordered_map instead
dio Jun 6, 2018
3f01c64
Comment on the possibility to have negative offset value
dio Jun 6, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions api/envoy/config/filter/http/router/v2/router.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,15 @@ message Router {
// an upstream request. Presuming retries are configured, multiple upstream
// requests may be made for each downstream (inbound) request.
repeated envoy.config.filter.accesslog.v2.AccessLog upstream_log = 3;

// If set, the specified header will be added to upstream requests with a timestamp
// (in milliseconds) when Envoy starts decoding downstream's request headers.
//
// Example:
//
// .. code-block:: yaml
//
// start_timestamp_header: x-request-start
//
string start_timestamp_header = 4;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we reuse the existing user defined headers mechanism for this? E.g. you could configure x-request-start with value %START_TIMESTAMP%?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh yes, +1. That seems better.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@htuch thanks for the hint! Yes, I think that's the right direction (I should went to that direction in the first place, since apparently RequestInfo has that useful .startTime() 😔).

I added the corresponding changes using that approach. Thanks!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That's cleaner, thanks for suggesting!

}
3 changes: 3 additions & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ Version history
* logger: added the ability to optionally set the log format via the :option:`--log-format` option.
* logger: all :ref:`logging levels <operations_admin_interface_logging>` can be configured
at run-time: trace debug info warning error critical.
* router: added the optional
:ref:`start timestamp header<envoy_api_field_config.filter.http.router.v2.Router.start_timestamp_header>`
to send timestamp (in milliseconds) as the value of the specified header in upstream requests.
* sockets: added `IP_FREEBIND` socket option support for :ref:`listeners
<envoy_api_field_Listener.freebind>` and upstream connections via
:ref:`cluster manager wide
Expand Down
2 changes: 1 addition & 1 deletion source/common/http/async_client_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ AsyncClientImpl::AsyncClientImpl(const Upstream::ClusterInfo& cluster, Stats::St
Runtime::RandomGenerator& random,
Router::ShadowWriterPtr&& shadow_writer)
: cluster_(cluster), config_("http.async-client.", local_info, stats_store, cm, runtime, random,
std::move(shadow_writer), true, false),
std::move(shadow_writer), true, false, EMPTY_STRING),
dispatcher_(dispatcher) {}

AsyncClientImpl::~AsyncClientImpl() {
Expand Down
6 changes: 6 additions & 0 deletions source/common/router/router.cc
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ void Filter::sendLocalReply(Http::Code code, const std::string& body,
Http::FilterHeadersStatus Filter::decodeHeaders(Http::HeaderMap& headers, bool end_stream) {
downstream_headers_ = &headers;

// If defined, set the specified start timestamp header with timestamp in milliseconds.
if (!config_.start_timestamp_header_.get().empty()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

add an explanation comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added in 4c80a6d.

headers.addReferenceKey(config_.start_timestamp_header_,
std::chrono::system_clock::now().time_since_epoch().count());
}

// Only increment rq total stat if we actually decode headers here. This does not count requests
// that get handled by earlier filters.
config_.stats_.rq_total_.inc();
Expand Down
8 changes: 5 additions & 3 deletions source/common/router/router.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,20 @@ class FilterConfig {
FilterConfig(const std::string& stat_prefix, const LocalInfo::LocalInfo& local_info,
Stats::Scope& scope, Upstream::ClusterManager& cm, Runtime::Loader& runtime,
Runtime::RandomGenerator& random, ShadowWriterPtr&& shadow_writer,
bool emit_dynamic_stats, bool start_child_span)
bool emit_dynamic_stats, bool start_child_span,
const std::string& start_timestamp_header)
: scope_(scope), local_info_(local_info), cm_(cm), runtime_(runtime),
random_(random), stats_{ALL_ROUTER_STATS(POOL_COUNTER_PREFIX(scope, stat_prefix))},
emit_dynamic_stats_(emit_dynamic_stats), start_child_span_(start_child_span),
shadow_writer_(std::move(shadow_writer)) {}
start_timestamp_header_(start_timestamp_header), shadow_writer_(std::move(shadow_writer)) {}

FilterConfig(const std::string& stat_prefix, Server::Configuration::FactoryContext& context,
ShadowWriterPtr&& shadow_writer,
const envoy::config::filter::http::router::v2::Router& config)
: FilterConfig(stat_prefix, context.localInfo(), context.scope(), context.clusterManager(),
context.runtime(), context.random(), std::move(shadow_writer),
PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, dynamic_stats, true),
config.start_child_span()) {
config.start_child_span(), config.start_timestamp_header()) {
for (const auto& upstream_log : config.upstream_log()) {
upstream_logs_.push_back(AccessLog::AccessLogFactory::fromProto(upstream_log, context));
}
Expand All @@ -118,6 +119,7 @@ class FilterConfig {
const bool emit_dynamic_stats_;
const bool start_child_span_;
std::list<AccessLog::InstanceSharedPtr> upstream_logs_;
const Http::LowerCaseString start_timestamp_header_;

private:
ShadowWriterPtr shadow_writer_;
Expand Down
57 changes: 55 additions & 2 deletions test/common/router/router_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ class TestFilter : public Filter {

class RouterTestBase : public testing::Test {
public:
RouterTestBase(bool start_child_span)
RouterTestBase(bool start_child_span, const std::string& start_timestamp_header = EMPTY_STRING)
: shadow_writer_(new MockShadowWriter()),
config_("test.", local_info_, stats_store_, cm_, runtime_, random_,
ShadowWriterPtr{shadow_writer_}, true, start_child_span),
ShadowWriterPtr{shadow_writer_}, true, start_child_span, start_timestamp_header),
router_(config_) {
router_.setDecoderFilterCallbacks(callbacks_);
upstream_locality_.set_zone("to_az");
Expand Down Expand Up @@ -1628,6 +1628,28 @@ TEST_F(RouterTest, DirectResponseWithBody) {
EXPECT_EQ(1UL, config_.stats_.rq_direct_response_.value());
}

TEST_F(RouterTest, NoStartTimestampHeader) {
absl::optional<Http::Protocol> downstream_protocol{Http::Protocol::Http2};
EXPECT_CALL(*cm_.thread_local_cluster_.cluster_.info_, features())
.WillOnce(Return(Upstream::ClusterInfo::Features::USE_DOWNSTREAM_PROTOCOL));
EXPECT_CALL(callbacks_.request_info_, protocol()).WillOnce(ReturnPointee(&downstream_protocol));

EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, Http::Protocol::Http2, _));
EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_));
expectResponseTimerCreate();

Http::TestHeaderMapImpl headers;
HttpTestUtility::addDefaultHeaders(headers);
router_.decodeHeaders(headers, true);

const Http::LowerCaseString header("x-request-start");
EXPECT_EQ(nullptr, headers.get(header));

EXPECT_CALL(cancellable_, cancel());
router_.onDestroy();
EXPECT_TRUE(verifyHostUpstreamStats(0, 0));
}

TEST(RouterFilterUtilityTest, finalTimeout) {
{
NiceMock<MockRouteEntry> route;
Expand Down Expand Up @@ -2117,5 +2139,36 @@ TEST_F(RouterTestChildSpan, ResetFlow) {
encoder.stream_.resetStream(Http::StreamResetReason::RemoteReset);
}

class RouterTestStartTimestampHeader : public RouterTestBase {
public:
RouterTestStartTimestampHeader() : RouterTestBase(false, "X-Request-Start") {}
};

TEST_F(RouterTestStartTimestampHeader, WithStartTimestampHeader) {
absl::optional<Http::Protocol> downstream_protocol{Http::Protocol::Http2};
EXPECT_CALL(*cm_.thread_local_cluster_.cluster_.info_, features())
.WillOnce(Return(Upstream::ClusterInfo::Features::USE_DOWNSTREAM_PROTOCOL));
EXPECT_CALL(callbacks_.request_info_, protocol()).WillOnce(ReturnPointee(&downstream_protocol));

EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, Http::Protocol::Http2, _));
EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_));
expectResponseTimerCreate();

Http::TestHeaderMapImpl headers;
HttpTestUtility::addDefaultHeaders(headers);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

test that the x-request-start header is not present before the call to decodeHeaders

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added in 3110ce0.

const Http::LowerCaseString header("x-request-start");
EXPECT_EQ(nullptr, headers.get(header));
router_.decodeHeaders(headers, true);
EXPECT_NE(nullptr, headers.get(header));

uint64_t timestamp;
StringUtil::atoul(headers.get(header)->value().c_str(), timestamp);
EXPECT_GT(timestamp, 0);

EXPECT_CALL(cancellable_, cancel());
router_.onDestroy();
EXPECT_TRUE(verifyHostUpstreamStats(0, 0));
}

} // namespace Router
} // namespace Envoy