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 网络编程
2,854 每月下载量
280KB
4.5K SLoC
solicit
Rust中的HTTP/2实现。
目标
项目的主要目标是提供一个低级别的HTTP/2协议实现,并以一种方式将其公开,使得高级库可以使用它。例如,应该能够为高级库编写一个简单的适配器,以与HTTP/1.1连接中获取的响应相同的方式暴露通过HTTP/2连接获取的响应。
API应该允许在任何级别使用HTTP/2连接——从发送和管理单个HTTP/2帧到仅操作请求和响应——取决于库的最终用户的需要。
库的核心应与底层IO的特定细节解耦——应能够在事件驱动或阻塞的IO上使用相同的API。同时,库提供了方便的适配器,允许直接使用Rust的标准库套接字IO作为传输层。
广泛的测试覆盖也是主要目标之一。没有代码提交而没有相应的测试。
在这个阶段,性能不是主要目标之一。
示例
如目标部分所述,此库不旨在提供完整的高级客户端或服务器实现(无缓存、无自动重定向、无强类型头等)。
然而,为了展示如何构建此类组件或为较不复杂版本提供基线,该包中包含了对有限客户端和服务器实现的实现(分别对应于模块 solicit::client
和 solicit::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