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

http1.1 protocol downgrade #3588

Open
zhangweibin222 opened this issue Mar 1, 2024 · 0 comments
Open

http1.1 protocol downgrade #3588

zhangweibin222 opened this issue Mar 1, 2024 · 0 comments
Labels
C-bug Category: bug. Something is wrong. This is bad!

Comments

@zhangweibin222
Copy link

Version
hyper 0.14.18

Platform
eulerosv2r7.x86_64

Description

The client sends an HTTP1.1 request to the hyper server, and the state.version of the hyper server is HTTP1.1. The hyper server sends the request to the hyper client, and the state.version of the hyper client is HTTP1.1. Finally, the hyper client sends the request to the server. If the HTTP protocol of the response returned by the server is HTTP1.0 and the response is in the keepalive state, the state.version of the hyper client is HTTP1.0. If HTTP1.1 requests are received later, the HyperClient sends the requests to the server using HTTP1.0.

Related Code:

    // If we know the remote speaks an older version, we try to fix up any messages
    // to work with our older peer.
    fn enforce_version(&mut self, head: &mut MessageHead<T::Outgoing>) {
        if let Version::HTTP_10 = self.state.version {
            // Fixes response or connection when keep-alive header is not present
            self.fix_keep_alive(head);
            // If the remote only knows HTTP/1.0, we should force ourselves
            // to do only speak HTTP/1.0 as well.
            head.version = Version::HTTP_10;
        }
        // If the remote speaks HTTP/1.1, then it *should* be fine with
        // both HTTP/1.0 and HTTP/1.1 from us. So again, we just let
        // the user's headers be.
    }
pub(super) fn poll_read_head() {
        ...
        self.state.keep_alive &= msg.keep_alive;
        self.state.version = msg.head.version;
        ...
}

demo:
image

demo code:
client microservice

use std::time::Duration;
use hyper::{Body, Request, Version};
use hyper::header::CONNECTION;

#[tokio::main]
async fn main() {
    let mut i = 0;
    loop {
        let client = hyper::client::Client::new();
        i += 1;
        let mut builder = Request::builder().header(CONNECTION, "keep-alive");
        let req = if i % 2 == 0 {
            builder.uri("http://127.0.0.1:8081/yyyyyyyyyyyyyyyyyyyyy").version(Version::HTTP_10).body(Body::empty()).unwrap()
        } else {
            builder.uri("http://127.0.0.1:8081/xxxxxxxxxxxxxxxxxxxxx").version(Version::HTTP_11).body(Body::empty()).unwrap()
        };
        println!("req: {:?}", req);
        let response = client.request(req).await.unwrap();
        println!("response: {:?}", response);
        println!("------------------------------");
        tokio::time::sleep(Duration::from_secs(5)).await;
    }
}

hyper server

use cbb::proxy;

#[tokio::main]
async fn main() {
    println!("BE");
    let listen = "0.0.0.0:8082".parse().unwrap();
    let client = "127.0.0.1:8083".parse().unwrap();
    proxy(listen, client).await;
}

hyper client

use cbb::proxy;

#[tokio::main]
async fn main() {
    println!("BE");
    let listen = "0.0.0.0:8082".parse().unwrap();
    let client = "127.0.0.1:8083".parse().unwrap();
    proxy(listen, client).await;
}

server microservice

use std::net::SocketAddr;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;
use cbb::simple_listener;

#[tokio::main]
async fn main() {
    println!("B");
    let listen: SocketAddr = "127.0.0.1:8083".parse().unwrap();
    // simple_listener(listen).await;
    let listener = TcpListener::bind(listen).await.unwrap();
    loop {
        let (mut stream, _) = listener.accept().await.unwrap();
        println!("new stream");
        tokio::spawn(async move {
            let mut data = [0u8; 1024 * 32];
            while let Ok(size) = stream.read(&mut data).await {
                if size == 0 {
                    continue
                }
                let data = String::from_utf8_lossy(&data[..size]);
                println!("[{}]", data);
                if data.contains("1_0") {
                    stream.write_all(b"HTTP/1.0 200 OK\r\nconnection: keep-alive\r\nContent-Length: 0\r\n\r\n").await.unwrap();
                } else {
                    stream.write_all(b"HTTP/1.1 200 OK\r\nconnection: keep-alive\r\nContent-Length: 0\r\n\r\n").await.unwrap();
                }
            }
        });
    }
}

lib

use std::net::SocketAddr;
use hyper::{Body, Response};
use hyper::server::conn::AddrStream;

pub async fn proxy(listen: SocketAddr, c_addr: SocketAddr) {
    use hyper::Error;
    use hyper::service::{make_service_fn, service_fn};
// And a MakeService to handle each connection...
    let client = hyper::client::Client::new();
    let make_svc = make_service_fn(|io: &AddrStream| {
        println!("new conn: {:?}", io.remote_addr());
        let client = client.clone();
        async move {
            let client = client.clone();
            Ok::<_, Error>(service_fn(move |mut req| {
                let client = client.clone();
                async move {
                    println!("request: {:?}", req);
                    let url = format!("http://{}{}", c_addr, req.uri().path());
                    *req.uri_mut() = url.parse().unwrap();
                    let result = client.request(req).await;
                    println!("response: {:?}", result);
                    println!();
                    result
                }
            }))
        }
    });
    hyper::server::Server::bind(&listen).serve(make_svc).await.unwrap();
}


pub async fn simple_listener(listen: SocketAddr) {
    use hyper::Error;
    use hyper::service::{make_service_fn, service_fn};
// And a MakeService to handle each connection...
    let make_svc = make_service_fn(|_| {
        async move {
            Ok::<_, Error>(service_fn(move | req| {
                async move {
                    println!("{:?}",req);
                    Ok::<_, Error>(Response::new(Body::from("hello")))
                }
            }))
        }
    });
    hyper::server::Server::bind(&listen).serve(make_svc).await.unwrap();
}

When the client microservice sends HTTP1.0 and HTTP1.1 requests in turn, the Hyper Server sends the requests to the Hyper Client. The HTTP protocol of the Hyper Client is gradually set to HTTP1.0. As a result, the HTTP protocol of subsequent requests is changed to HTTP1.0
That doesn't seem to be the outcome we want.

@zhangweibin222 zhangweibin222 added the C-bug Category: bug. Something is wrong. This is bad! label Mar 1, 2024
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!
Projects
None yet
Development

No branches or pull requests

1 participant