如何在Rust中通过发送原始数据发起HTTPS请求
问题(翻译后)
我希望通过发送原始数据来发起HTTPS请求,原理类似用Rust标准库发起HTTP请求的实现,但需要基于SSL完成。我不想用reqwest这类通过不同方法构建请求的库,而是希望直接使用如下示例格式的原始数据,通过HTTPS发送给服务器:
GET请求示例:
GET /index.php HTTP/1.1 Host: www.server.com
POST请求示例:
POST /index.php HTTP/1.1 Host: www.server.com Content-Length:14 somepostdata=1
补充说明:我显然不想自行实现SSL,而是寻求一个简单的API,允许我编写上述格式的请求并通过HTTPS发送给Web服务器(就像用telnet处理HTTP请求那样)。
解决方案
你可以使用native-tls(调用系统原生TLS库)或rustls(纯Rust实现TLS)来封装TCP流,直接发送原始HTTP请求文本,完全符合你要的"telnet式"操作逻辑。
方案1:使用native-tls(推荐,依赖系统原生TLS)
这个库封装了Windows Schannel、macOS Secure Transport、Linux OpenSSL等系统原生TLS实现,无需自行处理SSL握手和证书验证,API极简。
- 添加依赖到
Cargo.toml:
[dependencies] native-tls = "0.2"
- GET请求示例代码:
use std::io::{Read, Write}; use std::net::TcpStream; use native_tls::TlsConnector; fn main() -> Result<(), Box<dyn std::error::Error>> { // 建立TCP连接到目标服务器443端口 let mut stream = TcpStream::connect("www.server.com:443")?; // 创建TLS连接器并包装TCP流为加密流 let connector = TlsConnector::new()?; let mut tls_stream = connector.connect("www.server.com", stream)?; // 原始HTTP请求(必须用\r\n换行,HTTP协议要求CRLF) let request = "GET /index.php HTTP/1.1\r\nHost: www.server.com\r\n\r\n"; // 发送请求 tls_stream.write_all(request.as_bytes())?; // 读取服务器响应 let mut response = String::new(); tls_stream.read_to_string(&mut response)?; println!("{}", response); Ok(()) }
- POST请求示例代码:
use std::io::{Read, Write}; use std::net::TcpStream; use native_tls::TlsConnector; fn main() -> Result<(), Box<dyn std::error::Error>> { let mut stream = TcpStream::connect("www.server.com:443")?; let connector = TlsConnector::new()?; let mut tls_stream = connector.connect("www.server.com", stream)?; // 原始POST请求,注意Content-Length必须与POST数据长度一致,换行用\r\n let request = "POST /index.php HTTP/1.1\r\nHost: www.server.com\r\nContent-Length: 14\r\n\r\nsomepostdata=1"; tls_stream.write_all(request.as_bytes())?; let mut response = String::new(); tls_stream.read_to_string(&mut response)?; println!("{}", response); Ok(()) }
方案2:使用rustls(纯Rust实现,无系统依赖)
如果不想依赖系统TLS库,可以用纯Rust的rustls,配合webpki-roots提供根证书。
- 添加依赖到
Cargo.toml:
[dependencies] rustls = "0.21" webpki-roots = "0.23"
- 示例代码:
use std::io::{Read, Write}; use std::net::TcpStream; use rustls::{ClientConfig, ClientConnection, Stream, ServerName}; use webpki_roots::TLS_SERVER_ROOTS; fn main() -> Result<(), Box<dyn std::error::Error>> { let stream = TcpStream::connect("www.server.com:443")?; // 配置TLS客户端,加载根证书 let config = ClientConfig::builder() .with_root_certificates(TLS_SERVER_ROOTS) .with_no_client_auth(); let server_name = ServerName::try_from("www.server.com")?; let conn = ClientConnection::new(config, server_name)?; // 创建加密流 let mut tls_stream = Stream::new(conn, stream); // 发送原始请求 let request = "GET /index.php HTTP/1.1\r\nHost: www.server.com\r\n\r\n"; tls_stream.write_all(request.as_bytes())?; // 读取响应 let mut response = String::new(); tls_stream.read_to_string(&mut response)?; println!("{}", response); Ok(()) }
关键注意事项
- 换行必须用CRLF:HTTP协议要求请求头和正文的换行是
\r\n,不能只用\n,否则多数服务器会拒绝处理。 - 域名匹配:TLS连接时传入的域名必须与请求头的
Host字段一致,且匹配服务器SSL证书的域名,否则会触发证书验证错误。 - 测试用证书跳过:仅用于测试环境,绝对不要在生产中使用,可以通过
TlsConnector::builder().danger_accept_invalid_certs(true)(native-tls)或修改rustls配置来跳过验证。
内容的提问来源于stack exchange,提问作者0x4ndy
相关产品推荐
相关产品推荐

