Rust-Tcpstream:读取http请求有时会丢失正文



我正在学习如何使用带有rust的tcpstream编写http服务器。

我用这个函数来读取流缓冲区。

fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
let response = "HTTP/1.1 200 OKrnrn";
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}

但我发现,有时,缓冲区会丢失尸体。

应该是:

Request: POST /echo HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: python-requests/2.26.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 13
id=1&name=Foo

但当出现错误时,

Request: POST /echo HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: python-requests/2.26.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 13

它只会失去身体的一部分。

我使用python3请求发送请求。

错误的原因是什么?

这是获取正文内容的简单方法。这段代码逐行读取头,直到得到一个空文件,这里使用的是Rust TCPStream!读取标头后,阅读器中有一个Content-Length,所以我将char转换为integer,并使用大小创建一个向量。因此由read_exact函数填充。

let mut reader = BufReader::new(stream.try_clone().unwrap());
let mut name = String::new();
loop {
let r = reader.read_line(&mut name).unwrap();
if r < 3 { //detect empty line
break;
}
}
let mut size = 0;
let linesplit = name.split("n");
for l in linesplit {
if l.starts_with("Content-Length") {
let sizeplit = l.split(":");
for s in sizeplit {
if !(s.starts_with("Content-Length")) {
size = s.trim().parse::<usize>().unwrap(); //Get Content-Length
}
}
}
}
let mut buffer = vec![0; size]; //New Vector with size of Content   
reader.read_exact(&mut buffer).unwrap(); //Get the Body Content.

我正在编写自己的rust反向代理,作为学习该语言的一种手段。我遇到了这个问题,我的解决方案如下。

根本问题是read_to_endread_to_string需要一个EOF字符,而该字符不会在HTTP请求体中发送。因此,据我所知,您需要编写自己的字节缓冲区,然后在没有数据时退出。

我相信这并没有优化,但会让你克服这个问题。请原谅我使用拆封。显然,如果这是一个生产系统,请确保您更优雅地处理这些问题。

#[tokio::main]
async fn main() {
let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap(); 
let upstream_server = env::var("UPSTREAM").unwrap();  
loop {
println!("initialize stream");
let (mut stream, _) = listener.accept().await.unwrap(); 
println!("stream readable?");
stream.readable().await.unwrap();
println!("read all from stream");
let buf = read_all(&stream).await.unwrap();
// connect to upstream
println!("connect to upstream");
let mut upstream_stream = TcpStream::connect(&upstream_server).await.unwrap();
println!("upstream writable?");
upstream_stream.writable().await.unwrap();
// write request body from client to upstream
println!("write stream request body to upstream");
upstream_stream.write_all(&buf).await.unwrap();
println!("upstream readable?");
upstream_stream.readable().await.unwrap();
// read upstream response
println!("read all from upstream");
let upstream_buf = read_all(&upstream_stream).await.unwrap(); 
println!("shutdown upstream connection");
upstream_stream.shutdown().await.unwrap();
println!("upstream writable?");
stream.writable().await.unwrap();
println!("write stream buf upstream response");
stream.write_all(&upstream_buf.as_bytes()).await.unwrap(); 
println!("shutdown stream");
stream.shutdown().await.unwrap();
}
}
async fn read_all(stream: &TcpStream) -> Result<Vec<u8>, std::io::Error> {
let mut buf: Vec<u8> = Vec::new();
loop {
// Creating the buffer **after** the `await` prevents it from
// being stored in the async task.
let mut tmp_buf = [0; 4096]; => [u8; 4096]
// Try to read data, this may still fail with `WouldBlock`
// if the readiness event is a false positive.
match stream.try_read(&mut tmp_buf) {
Ok(0) => break,
Ok(_) => {
buf.extend_from_slice(&tmp_buf)
}
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { 
break;
}
Err(e) => { => Error
return Err(e.into());
}
}
}
return Ok(std::str::from_utf8(&buf).unwrap().trim_matches(char::from(0)).as_bytes().to_vec())
}

最新更新