Skip to content

Conversation

@imlk0
Copy link

@imlk0 imlk0 commented Aug 18, 2025

This PR introduces a new http-compat module that provides asynchronous streaming conversion between HTTP types from the http crate and BHTTP format. This enhancement allows seamless conversion between standard HTTP requests/responses and BHTTP binary format using asynchronous streams.

Note: this pull request is based on #74

Changes

  • Added BhttpEncoder struct for converting http::Request/http::Response to BHTTP data stream (futures::prelude::Stream<Item = Res<Vec<u8>>>)
  • Added BhttpDecoder struct for converting BHTTP binary data (futures::io::AsyncRead) back to http::Request/http::Response
  • Introduced http-compat feature flag that enables this feature

Testing

  • Added unit tests for request and response conversion based on examples in RFC 9292 Section 5.1 and Section 5.2
  • You can run test with cargo test --features http-compat http_compat

Usage

With this change, users can now easily convert between standard HTTP types and BHTTP format:

// Encoding example
let request = http::Request::builder()
    .method("GET")
    .uri("https://example.com")
    .body(Full::new(Bytes::from("test")))
    .unwrap();

let stream = BhttpEncoder::from_request(request);
// Stream can be consumed to get BHTTP binary data
// For encoding response using
// let stream = BhttpEncoder::from_response(response);

// Decoding example
let bhttp_async_read = get_bhttp_bytes(); // Some BHTTP binary data
let decoder = BhttpDecoder::new(bhttp_async_read);
// Decoder can be used to get back http::Request or http::Response
let request_or_response = decoder.decode_message().await?;
match request_or_response {
    HttpRequestOrResponse::Request(request) => {
        // got http::Request
    }
    HttpRequestOrResponse::Response(response) => {
        // got http::Response
    }
}

Copy link
Owner

@martinthomson martinthomson left a comment

Choose a reason for hiding this comment

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

Just some preliminary feedback. I find myself short on time today (as always), so it's not as in-depth as I'd like.

///
/// This is the final result of the decoding process, containing either
/// a fully constructed HTTP request or response with a streaming body.
pub enum HttpRequestOrResponse<R> {
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
pub enum HttpRequestOrResponse<R> {
pub enum HttpMessage<R> {

How do you imagine someone using this type? Do they simply call HttpMessage::from(...)?

Copy link
Author

Choose a reason for hiding this comment

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

The name HttpMessage seems clearer than before.

However, I don't think it makes sense to allow users to directly call HttpMessage::from(...) because that would require a BhttpBody, which should only be created through BhttpDecoder::decode_message(). To enforce this, I will make BhttpBody::new() private in a later commit.


/// Enum to wrap either an HTTP Request or Response for unified BHTTP encoding
#[derive(Debug)]
enum HttpRequestResponse<T> {
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
enum HttpRequestResponse<T> {
enum HttpMessage<T> {

So the trick here is that you wrap and then implement functions to encode those messages into the underlying type. And T is the body type.

Copy link
Author

Choose a reason for hiding this comment

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

Yes, the http crate uses a generic type T to represent the body type, and as long as T implements http_body::Body, I think this is a great design choice. Perhaps in the future we could refactor bhttp::Message into bhttp::Message<T>, which would allow us to avoid using content: Vec<u8> internally for storing content.

ohttp/bhttp/src/lib.rs

Lines 592 to 598 in c6131ac

pub struct Message {
informational: Vec<InformationalResponse>,
control: ControlData,
header: FieldSection,
content: Vec<u8>,
trailer: FieldSection,
}


#[test]
fn test_encode_rfc9292_request_example_indeterminate_length() {
// Modifyed example from Section 5.1 of RFC 9292 - Indeterminate-Length Binary Encoding of Request
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
// Modifyed example from Section 5.1 of RFC 9292 - Indeterminate-Length Binary Encoding of Request
// Modified example from Section 5.1 of RFC 9292 - Indeterminate-Length Binary Encoding of Request

How modified?

Copy link
Author

Choose a reason for hiding this comment

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

In the original bhttp example, the scheme is https, the authority is empty, and the path is /hello.txt. However, this is not allowed by the http crate (see source), which requires that if a scheme is present, the authority must also be present.

To make the test meaningful, I have modified the bhttp example to set a valid authority, ensuring it conforms to the HTTP URI specification.

I will add a comment describing the change later.

assert!(!bhttp_data.is_empty());

// The data should be longer than just the body since it includes headers and metadata
assert!(bhttp_data.len() > 9);
Copy link
Owner

Choose a reason for hiding this comment

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

This seems pretty weak in terms of its utility as a test. I suggest that you have a set of known-answer tests for these. On the one hand, http::Request, on the other a byte array. Those can live in a common test module. This code can run down the list and check that the output matches the expectation.

Copy link
Author

Choose a reason for hiding this comment

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

This is a great suggestion. It would make test expansion much more convenient. Do you have any ideas about geting more of such combinations? There are some in RFC 9292, but I've already used them all.

@martinthomson martinthomson changed the base branch from main to stream August 19, 2025 00:50
@imlk0
Copy link
Author

imlk0 commented Aug 20, 2025

Thank you for your feedback. I will update this PR later :)

imlk0 added 5 commits August 20, 2025 17:06
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
…HeaderMap

Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
…ering a trailers frame

Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
@imlk0
Copy link
Author

imlk0 commented Aug 21, 2025

Updated based on your feedback and marked most reviews as resolved. Left a few open for further discussion, would appreciate your thoughts!

Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
…onsuming

Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
…and http

Signed-off-by: Kun Lai <laikun@linux.alibaba.com>
@martinthomson martinthomson deleted the branch martinthomson:stream September 22, 2025 07:45
@martinthomson
Copy link
Owner

@imlk0, I'm sorry, but I didn't realize that merging the other PR would close this. And I can't reopen it. Can you do that? I think it needs a new pull request. (I'm still considering taking this code; it's good, but I need to do a bit more testing on my own to get more confident in it.)

@imlk0
Copy link
Author

imlk0 commented Nov 10, 2025

Hi @martinthomson

Sorry for the long delay in getting back to this. I’ve reopened the PR and rebased it onto the latest master (#87). I’ve also cleaned up and squashed some of the earlier commits for a clearer history.

Based on further testing and use in my side, I’ve incorporated several improvements to enhance stability and functionality:

  • 4bc4d63 fix(http-compat): remove default value for uri when convert between bhttp and http (Kun Lai)
  • 321f202 fix(ohttp): change nonce array length from 16 to 32 to fix panic (Kun Lai)
  • 0106d0d feat(http-compat): allow creating server response while client request is consuming (Kun Lai)

I’ve been using this branch in my own project to encrypt both HTTP requests and responses (including SSE streaming) for vLLM inference, and it’s been working well.

I’m still committed to helping move this forward. please let me know if you'd like any further adjustments or have additional feedback :).

Thanks!

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