Skip to content

http: Simplify stacks and target types#656

Merged
olix0r merged 24 commits intomainfrom
ver/orig-proto
Sep 16, 2020
Merged

http: Simplify stacks and target types#656
olix0r merged 24 commits intomainfrom
ver/orig-proto

Conversation

@olix0r
Copy link
Member

@olix0r olix0r commented Sep 15, 2020

Currently, various properties of HTTP requests are exposed into the
stack target type, meaning that stacks (including their resolutions)
cannot be shared across requests that vary by any of these settings and,
worse, that per-request determinations must be made to determine which
stack to use.

In order to bind stacks according to connection-level metadata,
where these request-level parameters cannot be known, we need to remove
this metadata from the stack target.

The HTTP server now applies URI normalization as an internal
implementation detail (because it's a detail of how hyper clients and
servers differ). This eliminates the ShouldNormalizeUri trait, as that
determination can be made entirely based on the properties of the
request without any additional stack-specific metadata.

The HTTP client now takes an updated Settings type that explicitly
handles the orig-proto upgrade logic (removing this from the client
stack).

Stack targets now hold an http::Version on the accept-side, and map
that to a per-endpoint http::client::Settings for use by the http
client.

Currently, various properties of HTTP requests are exposed into the
stack target type, meaning that stacks (including their resolutions)
cannot be shared across requests that vary by any of these settings and,
worse, that per-request determinations must be made to determine which
stack to use.

In order to perform bind stacks according to connection-level metadata,
where these request-level parameters cannot be known, we need to remove
this metadata from the stack target.

The HTTP server now applies URI normalization as an internal
implementation detail (because it's a detail of how hyper clients and
servers differ). This eliminates the `ShouldNormalizeUri` trait, as that
determination can be made entirely based on the properties of the
request without any additional stack-specific metadata.

The HTTP client now takes an updated `Settings` type that explicitly
handles the orig-proto upgrade logic (removing this from the client
stack).

Stack targets now hold an `http::Version` on the accept-side, and map
that to a per-endpoint `http::client::Settings` for use by the http
client.
@olix0r olix0r requested a review from a team September 15, 2020 01:41
Copy link
Contributor

@hawkw hawkw left a comment

Choose a reason for hiding this comment

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

the new factoring makes a lot of sense to me --- this feels simpler and easier to understand. i had some style nits and suggestions, but didn't notice any problems.

Comment on lines +96 to +102
if orig_proto == "HTTP/1.1" {
Some(http::Version::HTTP_11)
} else if orig_proto == "HTTP/1.0" {
Some(http::Version::HTTP_10)
} else {
None
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit/tioli: could be

Suggested change
if orig_proto == "HTTP/1.1" {
Some(http::Version::HTTP_11)
} else if orig_proto == "HTTP/1.0" {
Some(http::Version::HTTP_10)
} else {
None
}
match orig_proto {
"HTTP/1.1" => Some(http::Version::HTTP_11),
"HTTP/1.0" => Some(http::Version::HTTP_10),
_ => None,
}

...unless i'm missing something, of course?

Copy link
Member Author

Choose a reason for hiding this comment

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

orig_proto is a HeaderValue and not a string, so the matching form doesn't work

Comment on lines 102 to 109
if client.is_none() {
debug!("Caching new client");
*client = Some(hyper::Client::builder().set_host(absolute_form).build(
HyperConnect::new(self.connect.clone(), self.target.clone(), absolute_form),
));
}

client.as_ref().unwrap().request(req)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit/TIOLI: there's actually an [Option::get_or_insert_with] method that basically implements this logic and avoids the unwrap:

Suggested change
if client.is_none() {
debug!("Caching new client");
*client = Some(hyper::Client::builder().set_host(absolute_form).build(
HyperConnect::new(self.connect.clone(), self.target.clone(), absolute_form),
));
}
client.as_ref().unwrap().request(req)
client
.get_or_insert_with(|| {
debug!("Caching new client");
hyper::Client::builder().set_host(absolute_form).build(
HyperConnect::new(self.connect.clone(), self.target.clone(), absolute_form),
)
})
.request(req)

Copy link
Member Author

Choose a reason for hiding this comment

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

Unfortunately, this doesn't play with the borrow checker and self -- because we're holding a mutable ref to the client, we can't pass a reference to self into the closure.

Copy link
Contributor

@kleimkuhler kleimkuhler left a comment

Choose a reason for hiding this comment

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

This looks good! I have a question about a change in Gateway, but otherwise this makes sense to me.

.map(|n| NameAddr::from_str(n).unwrap().into())
.unwrap_or_else(|| socket_addr.into()),
http_settings: http::Settings::Http2,
http_version: http::Version::Http1,
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this now Http1 because we know it should be upgraded to H2? If not, why this change?

Copy link
Member Author

Choose a reason for hiding this comment

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

it's a test, so it's not actually used

@olix0r
Copy link
Member Author

olix0r commented Sep 15, 2020

So, this branch seems to impact CI time quite a bit.

Locally, I see:

main

#15 240.5     Finished release [optimized] target(s) in 4m 00s
#15 240.6       User time (seconds): 2043.05
#15 240.6       System time (seconds): 31.72
#15 240.6       Percent of CPU this job got: 863%
#15 240.6       Elapsed (wall clock) time (h:mm:ss or m:ss): 4:00.35
#15 240.6       Maximum resident set size (kbytes): 4573996

ver/orig-proto

#15 389.1     Finished release [optimized] target(s) in 6m 28s
#15 389.1       User time (seconds): 2143.76
#15 389.1       System time (seconds): 31.86
#15 389.1       Percent of CPU this job got: 559%
#15 389.1       Elapsed (wall clock) time (h:mm:ss or m:ss): 6:28.91
#15 389.1       Maximum resident set size (kbytes): 4328072

I'm not sure how this could have made it 30-40% worse... i would have expected the opposite.

Helps compile times for some reason?!
@olix0r olix0r merged commit b59172a into main Sep 16, 2020
@olix0r olix0r deleted the ver/orig-proto branch September 16, 2020 01:20
olix0r added a commit to linkerd/linkerd2 that referenced this pull request Sep 18, 2020
This release fixes a recent regression in multicluster gateway
configurations that would forbid inbound gateway traffic. It also fixes
URI normalization for orig-proto-upgrade requests that do not include a
`Host` header.

---

* http: Simplify stacks and target types (linkerd/linkerd2-proxy#656)
* Make SkipDetect more generic as stack::MakeSwitch (linkerd/linkerd2-proxy#657)
* introduce tests for isolated services (linkerd/linkerd2-proxy#655)
* http: Put normalize_uri back on the stack (linkerd/linkerd2-proxy#659)
* inbound: Apply loop detection on the connect stack (linkerd/linkerd2-proxy#660)
* tracing: Elide redundant info in tracing contexts (linkerd/linkerd2-proxy#661)
* outbound: Reorganize outbound stacks (linkerd/linkerd2-proxy#662)
* app: Decouple stacks from listeners (linkerd/linkerd2-proxy#663)
* inbound: Split HTTP detection stack from TLS (linkerd/linkerd2-proxy#664)
* integration: Bundle tests in src (linkerd/linkerd2-proxy#665)
olix0r added a commit to linkerd/linkerd2 that referenced this pull request Sep 19, 2020
This release fixes a recent regression in multicluster gateway
configurations that would forbid inbound gateway traffic. It also fixes
URI normalization for orig-proto-upgrade requests that do not include a
`Host` header.

---

* http: Simplify stacks and target types (linkerd/linkerd2-proxy#656)
* Make SkipDetect more generic as stack::MakeSwitch (linkerd/linkerd2-proxy#657)
* introduce tests for isolated services (linkerd/linkerd2-proxy#655)
* http: Put normalize_uri back on the stack (linkerd/linkerd2-proxy#659)
* inbound: Apply loop detection on the connect stack (linkerd/linkerd2-proxy#660)
* tracing: Elide redundant info in tracing contexts (linkerd/linkerd2-proxy#661)
* outbound: Reorganize outbound stacks (linkerd/linkerd2-proxy#662)
* app: Decouple stacks from listeners (linkerd/linkerd2-proxy#663)
* inbound: Split HTTP detection stack from TLS (linkerd/linkerd2-proxy#664)
* integration: Bundle tests in src (linkerd/linkerd2-proxy#665)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants