Rust HTTP/2协议库httpbis的使用:高效构建支持HTTP/2的双向流通信服务

Rust HTTP/2协议库httpbis的使用:高效构建支持HTTP/2的双向流通信服务

项目介绍

httpbis是一个基于tokio的Rust HTTP/2客户端和服务器实现。目前它被用作grpc-rust的实现基础。

Build Status License crates.io

示例服务器

查看源代码并执行命令:

cargo run --example server

服务器将在 https://localhost:8443/ 上可用。您需要任何支持HTTP/2的现代浏览器来打开页面(例如Firefox、Safari、Chrome)。

服务器仅通过HTTP/2工作,如果浏览器不发送HTTP/2前言,服务器将关闭连接。

示例客户端

cargo run --example client https://google.com/

结果如下:

:status: 302
cache-control: private
content-type: text/html; charset=UTF-8
referrer-policy: no-referrer
location: https://www.google.ru/?gfe_rd=cr&ei=mZQ4WZfaGISDZOzOktgO
content-length: 257
date: Thu, 08 Jun 2017 00:04:41 GMT
alt-svc: quic=":443"; ma=2592000; v="38,37,36,35"

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="https://www.google.ru/?gfe_rd=cr&amp;amp;ei=mZQ4WZfaGISDZOzOktgO">here</A>.
</BODY></HTML>

完整示例

HTTP/2服务器示例代码

use httpbis::*;
use std::sync::Arc;

// 创建HTTP/2服务器
fn main() {
    // 配置服务器参数
    let mut server_conf = ServerConf::new();
    server_conf.alpn_protocols = vec![b"h2".to_vec()]; // 仅支持HTTP/2
    
    // 创建服务器
    let mut server = ServerBuilder::new()
        .conf(server_conf)
        .service(Arc::new(HelloService))
        .build()
        .expect("Failed to build server");
    
    // 添加TLS证书
    server
        .add_tls("localhost", include_bytes!("../test/cert.pem"), include_bytes!("../test/key.pem"))
        .expect("Failed to add TLS");
    
    // 绑定端口
    server.bind_port(8443).expect("Failed to bind port");
    
    println!("Server started at https://localhost:8443/");
    server.wait();
}

// 简单的HTTP服务实现
struct HelloService;

impl Service for HelloService {
    fn start_request(&self, _headers: Headers, _req: HttpStreamAfterHeaders) -> Response {
        // 返回"Hello, World!"响应
        Response::found_200_plain_text("Hello, World!")
    }
}

HTTP/2客户端示例代码

use httpbis::*;
use std::sync::Arc;

fn main() {
    // 创建客户端
    let client = ClientBuilder::new()
        .build("google.com", 443)
        .expect("Failed to create client");
    
    // 创建请求头
    let mut headers = Headers::new();
    headers.add(":method", "GET");
    headers.add(":path", "/");
    headers.add(":scheme", "https");
    headers.add(":authority", "google.com");
    headers.add("user-agent", "httpbis-example-client");
    
    // 发送请求
    let (resp_headers, resp_body) = client.start_request(headers, HttpStreamAfterHeaders::empty())
        .expect("Failed to send request");
    
    // 打印响应头
    for (name, value) in resp_headers.iter() {
        println!("{}: {}", name, value);
    }
    
    // 打印响应体
    println!("\n{}", String::from_utf8_lossy(&resp_body.collect().unwrap()));
}

安装

作为库安装:

cargo add httpbis

或者在Cargo.toml中添加:

httpbis = "0.9.1"

特性

  • 纯Rust实现的HTTP/2协议
  • 基于tokio的高性能异步IO
  • 支持TLS加密连接
  • 双向流通信支持
  • 可用于构建gRPC服务

许可证

MIT OR Apache-2.0


1 回复

Rust HTTP/2协议库httpbis的使用:高效构建支持HTTP/2的双向流通信服务

简介

httpbis 是一个纯 Rust 实现的 HTTP/2 协议库,它提供了构建支持 HTTP/2 双向流通信服务的能力。这个库专注于 HTTP/2 协议的核心实现,不包含高级 HTTP 框架功能,适合需要精细控制 HTTP/2 通信的开发者使用。

主要特性

  • 纯 Rust 实现,无 C 依赖
  • 完整的 HTTP/2 协议支持
  • 双向流通信能力
  • 支持服务器和客户端实现
  • 高效的帧处理
  • 可定制的流控制

使用方法

添加依赖

首先在 Cargo.toml 中添加依赖:

[dependencies]
httpbis = "0.9"

基本服务器示例

use httpbis::*;
use httpbis::server::*;
use futures::future::Future;
use std::env;

struct MyService;

impl Service for MyService {
    fn start_request(&self, _headers: Headers, _req: HttpPartStream) -> Response {
        // 创建一个响应流
        let (sender, receiver) = futures::sync::mpsc::channel(1);
        
        // 发送响应头
        let headers = Headers::ok_200();
        let resp = Response::headers_and_stream(headers, receiver);
        
        // 在另一个线程中发送响应体
        std::thread::spawn(move || {
            sender.send(DataOrTrailers::data(b"Hello, HTTP/2!".to_vec())).wait().unwrap();
            sender.send(DataOrTrailers::trailers(Headers::new())).wait().unwrap();
        });
        
        resp
    }
}

fn main() {
    // 创建服务器配置
    let mut conf = ServerConf::new();
    conf.alpn = Some(ServerAlpn::Require);
    
    // 创建并运行服务器
    let server = ServerBuilder::new
        .set_port(8080)
        .set_conf(conf)
        .service(MyService)
        .build()
        .expect("Failed to start server");
    
    println!("Server running on port 8080");
    server.wait().expect("Server failed");
}

基本客户端示例

use httpbis::*;
use httpbis::Client;
use futures::future::Future;

fn main() {
    // 创建客户端
    let client = Client::new_plain(
        "127.0.0.1",
        8080,
        Default::default(),
        Default::default(),
    ).expect("Failed to create client");
    
    // 创建请求头
    let mut headers = Headers::new();
    headers.add(":method", "GET");
    headers.add(":path", "/");
    headers.add(":scheme", "http");
    headers.add(":authority", "127.0.0.1:8080");
    
    // 发送请求
    let (header, body) = client.start_request(headers, HttpPartStream::empty()).wait().expect("Request failed");
    
    println!("Response headers: {:?}", header);
    
    // 读取响应体
    body.for_each(|chunk| {
        match chunk {
            DataOrTrailers::Data(data) => {
                println!("Received data: {:?}", String::from_utf8_lossy(&data));
                Ok(())
            },
            DataOrTrailers::Trailers(trailers) => {
                println!("Received trailers: {:?}", trailers);
                Ok(())
            }
        }
    }).wait().expect("Failed to read response");
}

双向流通信示例

use httpbis::*;
use httpbis::server::*;
use futures::future::Future;
use futures::stream::Stream;
use futures::sync::mpsc;

struct EchoService;

impl Service for EchoService {
    fn start_request(&self, _headers: Headers, req: HttpPartStream) -> Response {
        // 创建响应流
        let (sender, receiver) = mpsc::channel(1);
        
        // 处理请求流
        let process = req.for_each(move |chunk| {
            match chunk {
                DataOrTrailers::Data(data) => {
                    // 回显接收到的数据
                    sender.send(DataOrTrailers::data(data)).wait().unwrap();
                    Ok(())
                },
                DataOrTrailers::Trailers(_) => {
                    // 结束流
                    sender.send(DataOrTrailers::trailers(Headers::new())).wait().unwrap();
                    Ok(())
                }
            }
        });
        
        // 在后台处理请求流
        std::thread::spawn(move || {
            process.wait().unwrap();
        });
        
        // 返回响应
        Response::headers_and_stream(Headers::ok_200(), receiver)
    }
}

fn main() {
    let server = ServerBuilder::new()
        .set_port(8081)
        .service(EchoService)
        .build()
        .expect("Failed to start server");
    
    println!("Echo server running on port 8081");
    server.wait().expect("Server failed");
}

高级配置

TLS 配置

use httpbis::*;
use std::path::Path;

fn configure_tls() {
    let server = ServerBuilder::new()
        .set_port(8443)
        .set_tls(
            ServerTlsOption::Tls(
                // 证书链文件
                Path::new("cert.pem"),
                // 私钥文件
                Path::new("key.pem"),
            )
        )
        // ...其他配置
        .build()
        .expect("Failed to start server");
}

流控制配置

use httpbis::*;

fn configure_flow_control() {
    let mut conf = ServerConf::new();
    // 设置初始窗口大小 (默认 65535)
    conf.initial_window_size = Some(128 * 1024);
    // 设置最大帧大小 (默认 16384)
    conf.max_frame_size = Some(32768);
    
    let server = ServerBuilder::new()
        .set_port(8080)
        .set_conf(conf)
        // ...其他配置
        .build()
        .expect("Failed to start server");
}

注意事项

  1. httpbis 是一个底层库,不提供高级 HTTP 框架功能如路由、中间件等
  2. 对于生产环境,建议使用更成熟的 HTTP/2 实现如 hyper,除非有特殊需求
  3. HTTP/2 的某些高级特性如服务器推送需要手动实现
  4. 错误处理需要仔细考虑,特别是在流操作中

性能建议

  1. 重用客户端连接以避免握手开销
  2. 合理设置流控制窗口大小以平衡内存使用和吞吐量
  3. 对于大量小消息,考虑批处理以减少帧开销
  4. 使用适当的线程模型处理并发请求

完整示例:HTTP/2文件上传与下载服务

下面是一个完整的HTTP/2文件服务示例,包含文件上传和下载功能:

use httpbis::*;
use httpbis::server::*;
use futures::future::Future;
use futures::stream::Stream;
use futures::sync::mpsc;
use std::fs::File;
use std::io::Read;
use std::path::Path;

// 文件服务处理器
struct FileService {
    upload_dir: String,
    download_dir: String,
}

impl Service for FileService {
    fn start_request(&self, headers: Headers, req: HttpPartStream) -> Response {
        // 获取请求路径
        let path = headers.get(":path").unwrap();
        
        if path.starts_with("/upload") {
            // 文件上传处理
            self.handle_upload(req)
        } else if path.starts_with("/download") {
            // 文件下载处理
            let filename = path.trim_start_matches("/download/");
            self.handle_download(filename)
        } else {
            // 返回404响应
            Response::not_found_404()
        }
    }
}

impl FileService {
    // 处理文件上传
    fn handle_upload(&self, req: HttpPartStream) -> Response {
        let (sender, receiver) = mpsc::channel(1);
        
        // 创建上传文件
        let upload_path = format!("{}/uploaded_file_{}", self.upload_dir, std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs());
        
        let mut file = File::create(&upload_path).unwrap();
        
        // 处理上传流
        let process = req.for_each(move |chunk| {
            match chunk {
                DataOrTrailers::Data(data) => {
                    // 写入文件
                    file.write_all(&data).unwrap();
                    Ok(())
                },
                DataOrTrailers::Trailers(_) => {
                    // 上传完成
                    sender.send(DataOrTrailers::trailers(Headers::new())).wait().unwrap();
                    Ok(())
                }
            }
        });
        
        // 在后台处理上传
        std::thread::spawn(move || {
            process.wait().unwrap();
        });
        
        // 返回响应
        Response::headers_and_stream(Headers::ok_200(), receiver)
    }
    
    // 处理文件下载
    fn handle_download(&self, filename: &str) -> Response {
        let (sender, receiver) = mpsc::channel(1);
        
        let file_path = format!("{}/{}", self.download_dir, filename);
        
        // 在另一个线程中读取并发送文件
        std::thread::spawn(move || {
            let mut file = File::open(&file_path).unwrap();
            let mut buffer = vec![0; 1024 * 8]; // 8KB缓冲区
            
            loop {
                let bytes_read = file.read(&mut buffer).unwrap();
                if bytes_read == 0 {
                    break;
                }
                
                sender.send(DataOrTrailers::data(buffer[..bytes_read].to_vec()))
                    .wait()
                    .unwrap();
            }
            
            // 发送结束标记
            sender.send(DataOrTrailers::trailers(Headers::new())).wait().unwrap();
        });
        
        // 返回响应
        Response::headers_and_stream(Headers::ok_200(), receiver)
    }
}

fn main() {
    // 创建文件服务
    let file_service = FileService {
        upload_dir: "uploads".to_string(),
        download_dir: "downloads".to_string(),
    };
    
    // 创建并启动服务器
    let server = ServerBuilder::new()
        .set_port(8080)
        .service(file_service)
        .build()
        .expect("Failed to start server");
    
    println!("HTTP/2文件服务运行在端口8080");
    server.wait().expect("服务器运行失败");
}

这个完整示例展示了如何使用httpbis构建一个支持HTTP/2的文件上传下载服务,包含了以下功能:

  1. 处理文件上传请求,将上传的文件保存到指定目录
  2. 处理文件下载请求,将文件分块发送给客户端
  3. 使用HTTP/2的流特性高效传输大文件
  4. 多线程处理避免阻塞主线程

要使用这个服务,客户端可以发送HTTP/2请求到:

  • /upload路径上传文件
  • /download/{filename}路径下载文件
回到顶部