-
Notifications
You must be signed in to change notification settings - Fork 5.5k
http conn man: add an option to strip trailing host dot. #15568
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
7c0674d
d2e05b7
595157c
4945289
74f247f
bb62fda
c3d1057
4b9c804
468ce45
3a4f449
40a1a44
8995b52
baed449
58b3759
e910496
dca8659
fffc458
d3cd808
ef66621
c744100
ec186fd
c94357b
d117486
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -229,6 +229,29 @@ bool HeaderUtility::isEnvoyInternalRequest(const RequestHeaderMap& headers) { | |
| internal_request_header->value() == Headers::get().EnvoyInternalRequestValues.True; | ||
| } | ||
|
|
||
| void HeaderUtility::stripTrailingHostDot(RequestHeaderMap& headers) { | ||
| auto host = headers.getHostValue(); | ||
| // Find last dot and return if not found or take off end if last | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe just
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes fits well here, will update. |
||
| auto dot_index = host.rfind('.'); | ||
| if (dot_index == std::string::npos) { | ||
| return; | ||
| } else if (dot_index == (host.size() - 1)) { | ||
| host.remove_suffix(1); | ||
| headers.setHost(host); | ||
| return; | ||
| } | ||
| // Check if the dot is just before a colon, which means it must be the port | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will change. |
||
| // because although the host may contain a colon via an IPv6 bracketed | ||
| // address, and although that IPv6 address may also contain dots when | ||
| // embedding an address per RFC 4291 2.2.3, the dot will never directly | ||
| // precede the colon like it would in foo.com.:123 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe split this sentence up? It's very long
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yah, will fix it. |
||
| if (host[dot_index + 1] == ':') { | ||
| // Does a memcpy internally, but since we only have access to a string_view | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does a memcpy? If you're talking about it copying data to a new string we do that on L238 as well, so this call out seems to be a bit unnecessary
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will remove it. |
||
| // anyways, this is acceptable compared to a string::erase of a copy | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: end comments with period
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will change. |
||
| headers.setHost(absl::StrCat(host.substr(0, dot_index), host.substr(dot_index + 1))); | ||
| } | ||
| } | ||
|
|
||
| void HeaderUtility::stripPortFromHost(RequestHeaderMap& headers, | ||
| absl::optional<uint32_t> listener_port) { | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -801,6 +801,89 @@ TEST_F(HttpConnectionManagerImplTest, RouteShouldUseNormalizedHost) { | |
| filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); | ||
| } | ||
|
|
||
| // Filters observe host header w/o trailing dot in host when trailing dot removal is configured | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think dot stripping could be tested more directly? Instantiate
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, added test for this, please check and let me know. Thank you.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ultimately, I'm not sure why three tests that very closely resemble each other are needed (they all verify that a) header map is updated, and b) that filters in the filter chain see that change. Perhaps just
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, will remove
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed. |
||
| TEST_F(HttpConnectionManagerImplTest, FilterShouldUseHostWithoutTrailingDot) { | ||
| setup(false, ""); | ||
| // Enable removal of host's trailing dot | ||
| strip_trailing_host_dot_ = true; | ||
| const std::string original_host = "host."; | ||
| const std::string updated_host = "host"; | ||
|
|
||
| auto* filter = new MockStreamFilter(); | ||
|
|
||
| EXPECT_CALL(filter_factory_, createFilterChain(_)) | ||
| .WillOnce(Invoke([&](FilterChainFactoryCallbacks& callbacks) -> void { | ||
| callbacks.addStreamDecoderFilter(StreamDecoderFilterSharedPtr{filter}); | ||
| })); | ||
|
|
||
| EXPECT_CALL(*filter, decodeHeaders(_, true)) | ||
| .WillRepeatedly(Invoke([&](RequestHeaderMap& header_map, bool) -> FilterHeadersStatus { | ||
| EXPECT_EQ(updated_host, header_map.getHostValue()); | ||
| return FilterHeadersStatus::StopIteration; | ||
| })); | ||
|
|
||
| EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); | ||
|
|
||
| EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { | ||
| decoder_ = &conn_manager_->newStream(response_encoder_); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Couldn't you reduce the test to setup and lines 854-857? Then verify the contents of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it ok to verify host header after
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed. |
||
| RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ | ||
| {":authority", original_host}, {":path", "/"}, {":method", "GET"}}}; | ||
| decoder_->decodeHeaders(std::move(headers), true); | ||
| return Http::okStatus(); | ||
| })); | ||
|
|
||
| // Kick off the incoming data. | ||
| Buffer::OwnedImpl fake_input("1234"); | ||
| conn_manager_->onData(fake_input, false); | ||
|
|
||
| // Clean up. | ||
| EXPECT_CALL(*filter, onStreamComplete()); | ||
| EXPECT_CALL(*filter, onDestroy()); | ||
| filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); | ||
| } | ||
|
|
||
| // The router observes host header w/o trailing dot, not the original host, when | ||
| // remove_trailing_hot_dot is configured | ||
| TEST_F(HttpConnectionManagerImplTest, RouteShouldUseHostWithoutTrailingDot) { | ||
| setup(false, ""); | ||
| // Enable removal of host's trailing dot | ||
| strip_trailing_host_dot_ = true; | ||
| strip_port_type_ = Http::StripPortType::MatchingHost; | ||
| const std::string original_host = "host."; | ||
| const std::string updated_host = "host"; | ||
|
|
||
| EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { | ||
| decoder_ = &conn_manager_->newStream(response_encoder_); | ||
| RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ | ||
| {":authority", original_host}, {":path", "/"}, {":method", "GET"}}}; | ||
| decoder_->decodeHeaders(std::move(headers), true); | ||
| return Http::okStatus(); | ||
| })); | ||
|
|
||
| const std::string fake_cluster_name = "fake_cluster"; | ||
|
|
||
| std::shared_ptr<Upstream::MockThreadLocalCluster> fake_cluster = | ||
| std::make_shared<NiceMock<Upstream::MockThreadLocalCluster>>(); | ||
| std::shared_ptr<Router::MockRoute> route = std::make_shared<NiceMock<Router::MockRoute>>(); | ||
| EXPECT_CALL(route->route_entry_, clusterName()).WillRepeatedly(ReturnRef(fake_cluster_name)); | ||
|
|
||
| EXPECT_CALL(*route_config_provider_.route_config_, route(_, _, _, _)) | ||
| .WillOnce(Invoke([&](const Router::RouteCallback&, const Http::RequestHeaderMap& header_map, | ||
| const StreamInfo::StreamInfo&, uint64_t) { | ||
| EXPECT_EQ(updated_host, header_map.getHostValue()); | ||
| return route; | ||
| })); | ||
| EXPECT_CALL(filter_factory_, createFilterChain(_)) | ||
| .WillOnce(Invoke([&](FilterChainFactoryCallbacks&) -> void {})); | ||
|
|
||
| // Kick off the incoming data. | ||
| Buffer::OwnedImpl fake_input("1234"); | ||
| conn_manager_->onData(fake_input, false); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think you need to call onData callback (and set the expectation above), instead you should be able to do
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thank you. Updated the code with suggestion. |
||
|
|
||
| // Clean up. | ||
| filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); | ||
| } | ||
|
|
||
| TEST_F(HttpConnectionManagerImplTest, DateHeaderNotPresent) { | ||
| setup(false, ""); | ||
| setUpEncoderAndDecoder(false, false); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drive by question for mdroth, do we think it's worth moving this and strip_matching_host_port together in a host mutation proto or just and wait and see if we get more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point, there are a whole bunch of options here related to URI normalization, not just the host ones but also the path ones. I'd be open to moving all of those options to a separate
UriNormalizationOptionsmessage, but I'd also be okay with keeping them here, since they're unlikely to ever be used anywhere else.