Skip to content
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

feat(client): introduce lower-level Connection API #1454

Merged
merged 1 commit into from
Mar 8, 2018

Conversation

seanmonstar
Copy link
Member

Adds the API described in #1449.

Closes #1449

/// Polls to determine whether this sender can be used yet for a request.
///
/// If the associated connection is closed, this returns an Error.
pub fn poll_ready(&mut self) -> Poll<(), ::Error> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the intent for this that you'd call it when pulling a connection out of a pool?

Copy link
Member Author

Choose a reason for hiding this comment

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

That is a fine time to do so, or even if you're not using a pool, and just have a single connection, it lets you know if the connection could process it right now. I think I need to alter this PR slightly so that it returns NotReady for h1 connections that aren't pipelining. It would also return NotReady if this were an h2 connection and you'd reached SETTINGS_MAX_CONCURRENT_STREAMS .

Copy link
Contributor

Choose a reason for hiding this comment

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

If you did try to send a request, would that just be queued up on the connection becoming ready, or would it be an error?

Copy link
Member Author

Choose a reason for hiding this comment

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

If the connection weren't ready, you'd get an error. We start using this poll_ready -> send pattern in the h2 crate, and it felt very natural, and it's what Sink is changing to in futures 0.2.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah cool, makes sense. I was thinking it wouldn't be an error, so I was a bit confused :)

/// upgrade. Once the upgrade is completed, the connection would be "done",
/// but it is not desired to actally shutdown the IO object. Instead you
/// would take it back using `into_parts`.
pub fn poll_without_shutdown(&mut self) -> Poll<(), ::Error> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Couldn't poll know that the connection's upgraded and not shut it down?

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 could, but that's probably a trap, since you may not choose to do anything special with a Connection, and just call exe.spawn(conn.map_err(|e| debug!("conn error: {}", e)). If you did that, and poll did what poll_with_shutdown does in this PR, then the transport would never have shutdown called.

If Connection::Item were Parts, then that could probably be easier, since then you'd need to handle the Parts in some way (even if just dropping) when spawning the future. But, in order to do that, Connection needs to hold the internal state in an Option, so it can remove it, and thus every poll requires a self.inner.as_mut().unwrap(). I figured by providing poll_without_shutdown, Connection::poll isn't bothered, and anyone could wrap poll_with_shutdown and an Option if they wanted that kind of API.

Does that reasoning seem convincing?

Copy link
Contributor

Choose a reason for hiding this comment

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

FWIW storing an Option and .as_mut().unwrap()'ing it is super common in futures/stream combinators so it wouldn't be that crazy. Seems fine to have two methods though.

///
/// Default is true.
#[inline]
pub fn keep_alive(&mut self, enabled: bool) -> &mut Builder {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this inject a Connection: close or just cause poll to return ready after the first response comes in?

Copy link
Member Author

Choose a reason for hiding this comment

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

Currently it tells the internal h1::Conn that after the first response, it cannot be used for a new request, and sets its state as Closed. This is the same as disabling keep_alive on the Client as well.

I've sometimes thought that it should probably set Connection: close, to be nice, but then, even if the header isn't set, once a response has been read, it's up to the client to use it again, or close the connection.

Interestingly, I think this option would do nothing if the connection were HTTP2. Or maybe it could send a GOAWAY after the request...

Copy link
Contributor

Choose a reason for hiding this comment

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

Given that it wouldn't do anything for h2, would it make more sense to not have this method at all and have users set Connection: close to manage keep alive? Hyper does pay attention if that header is set on outbound requests I think, right?

@seanmonstar seanmonstar force-pushed the client-connection branch 12 times, most recently from 55c1d17 to 030ddd8 Compare March 7, 2018 21:00
@seanmonstar seanmonstar merged commit eb15c66 into master Mar 8, 2018
@seanmonstar seanmonstar deleted the client-connection branch March 8, 2018 00:31
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.

2 participants