在我們的應用程式中,我們使用 boost::asio 來使用 HTTP 和 HTTPS 進行連接。我們也可以使用 HTTP 代理。現在我需要使用代理添加對 HTTPS 服務器的支持。
我研究了很多樣本??,發現所需的步驟似乎是:
- 創建到代理的 HTTP 連接
- 發送
CONNECT myhost.com:443到代理 - 然后繼續使用連接作為 SSL 隧道
我面臨的問題在于第 3 步。我可以使用未加密的 HTTP 連接或使用 SSL/HTTPS 連接。如果我在握手(為了發送 CONNECT)之前使用 HTTPS 連接失敗以及為普通 HTTP 連接執行 SSL 握手。
此處的這篇文章包含一些片段 - 但它不包含我遺漏的步驟: 使用 boost 通過 HTTP 代理連接 SSL 服務器
任何提示我缺少什么?
示例代碼:
using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
typedef ssl::stream<tcp::socket> ssl_socket;
// Create a context that uses the default paths for
// finding CA certificates.
ssl::context ctx(ssl::context::sslv23);
ctx.set_default_verify_paths();
// Open a socket and connect it to the remote host.
boost::asio::io_context io_context;
ssl_socket socket(io_context, ctx);
boost::asio::connect(socket.lowest_layer(), resolver.resolve(query));
socket.lowest_layer().set_option(tcp::no_delay(true));
socket.set_verify_callback(ssl::host_name_verification(...));
boost::system::error_code error = boost::asio::error::host_not_found;
boost::asio::streambuf request2;
std::ostream request_stream2(&request2);
boost::asio::streambuf response2;
request_stream2 << "CONNECT " << in_server << ":443 HTTP/1.0\r\n";
request_stream2 << "Host: " << in_server << ":443 \r\n";
AddBasicUserAuthHeader(request_stream2, testUrl);
request_stream2 << "Proxy-Connection: keep-alive\r\n";
request_stream2 << "Connection: keep-alive\r\n\r\n";
// Send the request - this will fail with "write: uninitialized"
boost::asio::write(socket, request);
... wait and process response
socket.set_verify_mode(ssl::verify_none);
socket.handshake(ssl_socket::client);
此代碼在 boost::asio::write 中失敗,并顯示“write: uninitialized”。在這一點上,我無法弄清楚如何將連接用作普通的 HTTP/TCP。反過來 - 在嘗試切換到 HTTPS 時,首先創建一個普通的 HTTP 連接失敗。
uj5u.com熱心網友回復:
錢幣。這是一些參差不齊的代碼。清理丟失的東西和拼寫錯誤的東西,我注意到:
您可能需要顯式或隱式重繪 ostream(真的只是很好的做法)
你在握手前寫入 ssl 流?如果需要,只需寫入底層套接字:
boost::asio::write(socket, request);應該
boost::asio::write(socket.next_layer(), request);
也就是說,在您有代理回應之前,我可能不會構建 ssl 流。我還會使用 Beast 撰寫請求,以排除在手動處理實作細節時出現的任何意外。
現場演示
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <iostream>
using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
namespace beast = boost::beast;
namespace http = beast::http;
using ssl_socket = ssl::stream<tcp::socket>;
using Request = http::request<http::empty_body>;
using Response = http::response<http::empty_body>;
using BodyResponse = http::response<http::string_body>;
int main()
{
std::string const in_server = "example.com";
boost::asio::io_context io_context;
tcp::resolver resolver(io_context);
tcp::resolver::query query{"localhost", "8888"};
// Create a context that uses the default paths for
// finding CA certificates.
ssl::context ctx(ssl::context::sslv23);
ctx.set_default_verify_paths();
// Open a socket and connect it to the remote host.
ssl_socket socket(io_context, ctx);
boost::asio::connect(socket.lowest_layer(), resolver.resolve(query));
socket.lowest_layer().set_option(tcp::no_delay(true));
socket.set_verify_callback(ssl::host_name_verification(in_server));
{
Request request(http::verb::connect, in_server ":443", 10);
request.set(http::field::host, in_server ":443");
request.set(http::field::proxy_connection, "keep-alive");
request.set(http::field::connection, "keep-alive");
request.set(http::field::proxy_authorization, "basic aGVsbG86d29ybGQ=");
request.prepare_payload(); // no body, but still good practice
// Send the request
http::write(socket.next_layer(), request);
}
Response proxy_response;
//... wait and process response
{
http::response_parser<http::empty_body> p;
beast::flat_buffer buf;
// Only headers expected
http::read_header(socket.next_layer(), buf, p);
proxy_response = std::move(p.get());
assert(buf.size() == 0); // no excess data should be received
}
std::cout << proxy_response << "\n";
if (proxy_response.result() == http::status::ok) {
socket.set_verify_mode(ssl::verify_none);
socket.handshake(ssl_socket::client);
std::cout << "Handshake completed" << std::endl;
} else {
return 1; // TODO handle errors
}
{
Request request(http::verb::get, "/", 10);
request.set(http::field::host, in_server);
http::write(socket, request);
}
{
beast::flat_buffer buf;
BodyResponse http_res;
http::read(socket, buf, http_res);
std::cout << http_res;
}
}
在我的機器上列印
HTTP/1.0 200 Connection established
Proxy-agent: tinyproxy/1.8.4
Handshake completed
HTTP/1.0 200 OK
Age: 214958
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Mon, 25 Oct 2021 23:11:37 GMT
Etag: "3147526947 gzip ident"
Expires: Mon, 01 Nov 2021 23:11:37 GMT
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Server: ECS (bsa/EB23)
Vary: Accept-Encoding
X-Cache: HIT
Content-Length: 1256
Connection: close
<!doctype html>
<html>
<head>
<title>Example Domain</title>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #f0f0f2;
margin: 0;
padding: 0;
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
div {
width: 600px;
margin: 5em auto;
padding: 2em;
background-color: #fdfdff;
border-radius: 0.5em;
box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
}
a:link, a:visited {
color: #38488f;
text-decoration: none;
}
@media (max-width: 700px) {
div {
margin: 0 auto;
width: auto;
}
}
</style>
</head>
<body>
<div>
<h1>Example Domain</h1>
<p>This domain is for use in illustrative examples in documents. You may use this
domain in literature without prior coordination or asking for permission.</p>
<p><a href="https://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/338632.html
上一篇:將視頻浮動到我的網頁右側
