Skip to content

blocking::Request::try_from(http::Request<_>) drops non-default HTTP version and discards extensions #2989

@meng-xu-cs

Description

@meng-xu-cs

Summary

impl<T> TryFrom<http::Request<T>> for reqwest::blocking::Request currently destructures only method, uri, and headers from http::request::Parts, then builds a fresh inner async request. That silently resets version to Version::default() and replaces extensions with an empty set.

The async reqwest::Request conversion preserves both fields, so the blocking implementation is inconsistent.

Reproduction

use http::{Request as HttpRequest, Version};
use reqwest::blocking::Request as BlockingRequest;
use reqwest::Request as AsyncRequest;
use std::convert::TryFrom;

fn main() {
    let src = HttpRequest::builder()
        .method("GET")
        .uri("http://localhost/")
        .version(Version::HTTP_2)
        .body("hello")
        .unwrap();

    let blocking = BlockingRequest::try_from(src).unwrap();

    // Fails today: blocking.version() is HTTP/1.1
    assert_eq!(blocking.version(), Version::HTTP_2);
}

For comparison, the async conversion preserves the version:

let src = HttpRequest::builder()
    .method("GET")
    .uri("http://localhost/")
    .version(Version::HTTP_2)
    .body("hello")
    .unwrap();

let async_req = AsyncRequest::try_from(src).unwrap();
assert_eq!(async_req.version(), Version::HTTP_2); // passes

Expected behavior

Converting http::Request<T> into reqwest::blocking::Request should preserve the source request semantics, including:

  • version
  • extensions
  • already-preserved fields like method, uri, headers, and body

Actual behavior

The blocking conversion drops version and extensions and replaces them with defaults from async_impl::Request::new(...).

Impact

  • Non-default versions (for example HTTP_2 or HTTP_10) are silently lost.
  • The loss is externally observable immediately via blocking::Request::version(), and it also affects the request that is ultimately sent because the blocking client forwards the inner async request into the async execution path.
  • extensions are also discarded, making blocking and async conversions semantically inconsistent.

Metadata

Metadata

Assignees

No one assigned

    Labels

    E-easyEffort: Easy! Start here :DE-pr-welcomeThe feature is welcome to be added, instruction should be found in the issue.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions