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

100-continue response with BodyStream impossible #3827

Open
fungs opened this issue Jan 13, 2025 · 3 comments
Open

100-continue response with BodyStream impossible #3827

fungs opened this issue Jan 13, 2025 · 3 comments
Labels
C-bug Category: bug. Something is wrong. This is bad! S-waiting-on-author Status: waiting on the author to provide more info, or make changes.

Comments

@fungs
Copy link

fungs commented Jan 13, 2025

Version
Hyper 1.5 server (HTTP1 mode)

Description
Hey there, Hyper team! I've run into a bit of a pickle with Hyper 1.5 and the handling of 100-continue responses. Let me break it down:

  1. The newer streaming API (BodyStream) doesn't trigger an automatic 100-continue informational response when a data frame is polled. This is different from the older Body API, which sends the 100-continue when the body is first polled.

  2. Trying to send a 100-continue response manually in the application using return Ok(Response::new(Body::empty())) results in a runtime error: UnsupportedStatusCode. This leaves us in a bit of a bind.

  3. I've seen suggestions to inject the response at the TCP layer, but that's pretty awkward, especially when using TLS encryption. It feels like we're working against the library rather than with it.

I'm curious why the logic that works for Body and the older API (as described in PR #2119, which added automatic 100-continue support for Body) doesn't work with BodyStream. That PR essentially made Hyper automatically send a 100-continue response when the body is first polled if the request included an Expect: 100-continue header.

Would it be possible to implement support for the 100-continue response using the body streaming interface in the same way as it's done for Body? It seems like this would provide a consistent experience across both APIs.

I noticed PR #3815, which adds support for sending informational responses. Will this solve the problem? It looks like it adds a send_informational method to the response, but I'm not sure if this will integrate with the automatic 100-continue behavior.

Thanks for your time, and I'm looking forward to hearing your thoughts on this!

Related

@fungs fungs added the C-bug Category: bug. Something is wrong. This is bad! label Jan 13, 2025
@sfackler
Copy link
Contributor

Doesn't this test imply this handling still exists?

hyper/tests/server.rs

Lines 876 to 907 in e981a91

#[test]
fn expect_continue_sends_100() {
let server = serve();
let mut req = connect(server.addr());
server.reply();
req.write_all(
b"\
POST /foo HTTP/1.1\r\n\
Host: example.domain\r\n\
Expect: 100-continue\r\n\
Content-Length: 5\r\n\
Connection: Close\r\n\
\r\n\
",
)
.expect("write 1");
let msg = b"HTTP/1.1 100 Continue\r\n\r\n";
let mut buf = vec![0; msg.len()];
req.read_exact(&mut buf).expect("read 1");
assert_eq!(buf, msg);
let msg = b"hello";
req.write_all(msg).expect("write 2");
let mut body = String::new();
req.read_to_string(&mut body).expect("read 2");
let body = server.body();
assert_eq!(body, msg);
}

@seanmonstar seanmonstar added the S-waiting-on-author Status: waiting on the author to provide more info, or make changes. label Jan 13, 2025
@fungs
Copy link
Author

fungs commented Jan 14, 2025

Doesn't this test imply this handling still exists?

hyper/tests/server.rs

Lines 876 to 907 in e981a91

#[test]
fn expect_continue_sends_100() {
let server = serve();
let mut req = connect(server.addr());
server.reply();
req.write_all(
b"\
POST /foo HTTP/1.1\r\n\
Host: example.domain\r\n\
Expect: 100-continue\r\n\
Content-Length: 5\r\n\
Connection: Close\r\n\
\r\n\
",
)
.expect("write 1");
let msg = b"HTTP/1.1 100 Continue\r\n\r\n";
let mut buf = vec![0; msg.len()];
req.read_exact(&mut buf).expect("read 1");
assert_eq!(buf, msg);
let msg = b"hello";
req.write_all(msg).expect("write 2");
let mut body = String::new();
req.read_to_string(&mut body).expect("read 2");
let body = server.body();
assert_eq!(body, msg);
}

I believe this handling is only triggered when it is using the function poll() at the lower level, but the BodyStream interface seems to use the function poll_frame() instead.

@sfackler
Copy link
Contributor

Which poll function? Are you sure that poll_frame doesn't call down into that poll function? Could you put together a self contained example that shows the issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: bug. Something is wrong. This is bad! S-waiting-on-author Status: waiting on the author to provide more info, or make changes.
Projects
None yet
Development

No branches or pull requests

3 participants