9 个版本 (4 个破坏性版本)

使用旧的Rust 2015

0.4.4 2015年9月7日
0.4.3 2015年8月31日
0.4.0 2015年6月26日
0.3.0 2015年6月3日
0.0.1 2015年5月4日

#1662 in 网络编程

Download history 963/week @ 2023-10-20 966/week @ 2023-10-27 939/week @ 2023-11-03 736/week @ 2023-11-10 1200/week @ 2023-11-17 817/week @ 2023-11-24 466/week @ 2023-12-01 741/week @ 2023-12-08 914/week @ 2023-12-15 700/week @ 2023-12-22 573/week @ 2023-12-29 736/week @ 2024-01-05 902/week @ 2024-01-12 769/week @ 2024-01-19 692/week @ 2024-01-26 370/week @ 2024-02-02

2,854 每月下载量

MIT 许可证

280KB
4.5K SLoC

solicit

Travis Build Status AppVeyor Build Status Crates.io

Rust中的HTTP/2实现。

API文档

目标

项目的主要目标是提供一个低级别的HTTP/2协议实现,并以一种方式将其公开,使得高级库可以使用它。例如,应该能够为高级库编写一个简单的适配器,以与HTTP/1.1连接中获取的响应相同的方式暴露通过HTTP/2连接获取的响应。

API应该允许在任何级别使用HTTP/2连接——从发送和管理单个HTTP/2帧到仅操作请求和响应——取决于库的最终用户的需要。

库的核心应与底层IO的特定细节解耦——应能够在事件驱动或阻塞的IO上使用相同的API。同时,库提供了方便的适配器,允许直接使用Rust的标准库套接字IO作为传输层。

广泛的测试覆盖也是主要目标之一。没有代码提交而没有相应的测试。

在这个阶段,性能不是主要目标之一。

示例

如目标部分所述,此库不旨在提供完整的高级客户端或服务器实现(无缓存、无自动重定向、无强类型头等)。

然而,为了展示如何构建此类组件或为较不复杂版本提供基线,该包中包含了对有限客户端和服务器实现的实现(分别对应于模块 solicit::clientsolicit::server)。

简单客户端

简单客户端实现允许用户在阻塞读取一个响应之前发出多个请求。接收到响应后,可以发送更多的请求通过相同的连接;然而,在读取响应时不能排队请求。

在某种程度上,这与带有keep-alive(和流水线)的HTTP/1.1连接类似。

示例

明文(http://)连接。

extern crate solicit;
use solicit::http::client::CleartextConnector;
use solicit::client::SimpleClient;
use std::str;

fn main() {
  // Connect to an HTTP/2 aware server
  let connector = CleartextConnector::new("http2bin.org");
  let mut client = SimpleClient::with_connector(connector).unwrap();
  let response = client.get(b"/get", &[]).unwrap();
  assert_eq!(response.stream_id, 1);
  assert_eq!(response.status_code().unwrap(), 200);
  // Dump the headers and the response body to stdout.
  // They are returned as raw bytes for the user to do as they please.
  // (Note: in general directly decoding assuming a utf8 encoding might not
  // always work -- this is meant as a simple example that shows that the
  // response is well formed.)
  for header in response.headers.iter() {
    println!("{}: {}",
        str::from_utf8(&header.0).unwrap(),
        str::from_utf8(&header.1).unwrap());
  }
  println!("{}", str::from_utf8(&response.body).unwrap());
  // We can issue more requests after reading this one...
  // These calls block until the request itself is sent, but do not wait
  // for a response.
  let req_id1 = client.request(b"GET", b"/get?hi=hello", &[], None).unwrap();
  let req_id2 = client.request(b"GET", b"/asdf", &[], None).unwrap();
  // Now we get a response for both requests... This does block.
  let (resp1, resp2) = (
      client.get_response(req_id1).unwrap(),
      client.get_response(req_id2).unwrap(),
  );
  assert_eq!(resp1.status_code().unwrap(), 200);
  assert_eq!(resp2.status_code().unwrap(), 404);
}

一个受TLS保护(并协商)的https://连接。唯一的区别在于提供给SimpleClient的连接器类型。

需要该crate的tls功能。

extern crate solicit;
use solicit::http::client::tls::TlsConnector;
use solicit::client::SimpleClient;
use std::str;

fn main() {
  // Connect to an HTTP/2 aware server
  let path = "/path/to/certs.pem";
  let connector = TlsConnector::new("http2bin.org", &path);
  let mut client = SimpleClient::with_connector(connector).unwrap();
  let response = client.get(b"/get", &[]).unwrap();
  assert_eq!(response.stream_id, 1);
  assert_eq!(response.status_code().unwrap(), 200);
  // Dump the headers and the response body to stdout.
  // They are returned as raw bytes for the user to do as they please.
  // (Note: in general directly decoding assuming a utf8 encoding might not
  // always work -- this is meant as a simple example that shows that the
  // response is well formed.)
  for header in response.headers.iter() {
    println!("{}: {}",
        str::from_utf8(&header.0).unwrap(),
        str::from_utf8(&header.1).unwrap());
  }
  println!("{}", str::from_utf8(&response.body).unwrap());
}

有关它是如何利用solicit::http API来实现其功能的,请查看solicit::client::simple模块。

异步客户端

与HTTP/1.1相比,异步客户端利用了更多针对HTTP/2的特定功能。

它允许多个客户端并发地向同一底层连接发送请求。响应以Future的形式返回给客户端,使他们只有在没有其他事情可做时(这可能在发送请求后立即发生)才需要等待响应。

此客户端为每个HTTP/2连接启动一个后台线程,一旦没有更多客户端连接到它(因此不能再发送更多请求)或HTTP/2连接返回错误,线程将优雅退出。

此客户端实现只是使用solicit::htp API可以实现的功能的一个示例--请参阅:solicit::client::async

示例

extern crate solicit;

use solicit::client::Client;
use solicit::http::client::CleartextConnector;
use std::thread;
use std::str;

fn main() {
  // Connect to a server that supports HTTP/2
  let connector = CleartextConnector::new(host: "http2bin.org");
  let client = Client::with_connector(connector).unwrap();

  // Issue 5 requests from 5 different threads concurrently and wait for all
  // threads to receive their response.
  let threads: Vec<_> = (0..5).map(|i| {
      let this = client.clone();
      thread::spawn(move || {
          let resp = this.get(b"/get", &[(b"x-thread".to_vec(), vec![b'0' + i])]).unwrap();
          let response = resp.recv().unwrap();

          println!("Thread {} got response ... {}", i, response.status_code().ok().unwrap());

          response
      })
      }).collect();

  let responses: Vec<_> = threads.into_iter().map(|thread| thread.join())
                                             .collect();

  println!("All threads joined. Full responses are:");
  for response in responses.into_iter() {
      let response = response.unwrap();
      println!("The response contains the following headers:");
      for header in response.headers.iter() {
          println!("  {}: {}",
                   str::from_utf8(&header.0).unwrap(),
                   str::from_utf8(&header.1).unwrap());
      }
      println!("{}", str::from_utf8(&response.body).unwrap());
  }
}

简单服务器

简单服务器的实现与SimpleClient类似;此服务器实现是完全单线程的:在读取请求时不能写入响应(反之亦然)。尽管如此,它确实展示了如何使用API实现HTTP/2服务器。

solicit::server模块提供。

示例

该服务器会回显它接收到的每个请求的正文。

extern crate solicit;
use std::str;
use std::net::{TcpListener, TcpStream};
use std::thread;

use solicit::http::Response;
use solicit::server::SimpleServer;

fn main() {
    fn handle_client(stream: TcpStream) {
        let mut server = SimpleServer::new(stream, |req| {
            println!("Received request:");
            for header in req.headers.iter() {
                println!("  {}: {}",
                str::from_utf8(&header.0).unwrap(),
                str::from_utf8(&header.1).unwrap());
            }
            println!("Body:\n{}", str::from_utf8(&req.body).unwrap());

            // Return a dummy response for every request
            Response {
                headers: vec![
                    (b":status".to_vec(), b"200".to_vec()),
                    (b"x-solicit".to_vec(), b"Hello, World!".to_vec()),
                ],
                body: req.body.to_vec(),
                stream_id: req.stream_id,
           }
        }).unwrap();
        while let Ok(_) = server.handle_next() {}
        println!("Server done (client disconnected)");
    }

    let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
    for stream in listener.incoming() {
        let stream = stream.unwrap();
        thread::spawn(move || {
            handle_client(stream)
        });
    }
}

许可证

该项目根据MIT许可证发布。

依赖关系

~270–750KB
~16K SLoC