使用Boost Beast通过SSL连接HTTP传文件遇401未授权问题求助
我尝试使用BOOST BEAST库通过带SSL连接的HTTP请求向服务器传输文件,以下是我的代码片段:
using boost::system::error_code; using boost::system::system_error; using net::ip::TCP; using stream = ssl::stream<tcp::socket>; typedef ssl::stream<tcp::socket> ssl_socket; boost::asio::io_context io_service; // 创建使用默认CA证书路径的上下文 ssl::context ctx(ssl::context::sslv23); ctx.set_default_verify_paths(); // 获取对应服务器名称的端点列表 tcp::resolver resolver(io_service); tcp::resolver::query query(host "https"); tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); // 尝试每个端点直到成功建立连接 ssl_socket socket(io_service, ctx); boost::asio::connect(socket.lowest_layer(), endpoint_iterator); socket.lowest_layer().set_option(tcp::no_delay(true)); // 执行SSL握手并验证远程主机证书 socket.set_verify_mode(ssl::verify_peer); // socket.set_verify_callback(ssl::rfc2818_verification(host)); socket.handshake(ssl_socket::client); std::cout<<"sending the request\n"; boost::asio::streambuf request; std::ostream request_stream(&request); std::ifstream source_file( sendFilePath, std::ios::binary | std::ios::ate ); size_t file_size = source_file.tellg(); std::cout<<"THE PATH: "<<path<<"\n\n"; std::stringstream buffer; buffer << source_file.rdbuf(); request_stream << "PUT "<< path <<" HTTP/1.1\r\n"; request_stream << "Host: " << host << "\r\n"; request_stream << "User-Agent: "<<BOOST_BEAST_VERSION_STRING<< "\r\n"; request_stream << "Content-Type: application/zip \r\n"; request_stream << "Accept: */*\r\n"; request_stream << "Content-Length: " << std::to_string((int)file_size) << "\r\n"; request_stream << "path: "<< path <<"\r\n"; request_stream << "Expect: 100-continue"<< "\r\n\r\n"; // request_stream << "Connection: close\r\n\r\n"; // 注意双换行 request_stream << buffer.str(); boost::asio::write(socket, request); boost::asio::streambuf response; boost::asio::read_until(socket, response, "\r\n"); std::string s( (std::istreambuf_iterator<char>(&response)), std::istreambuf_iterator<char>() ); std::cout<<"THE RESPONSE: "<<s<<"\n";
执行代码后,服务器返回如下响应:
HTTP/1.1 401 Unauthorized Date: Fri, 12 Jan 2024 16:51:49 GMT Server: Apache WWW-Authenticate: Basic realm="Authentication Required" Content-Length: 381 Content-Type: text/html; charset=iso-8859-1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>401 Unauthorized</title> </head><body> <h1>Unauthorized</h1> <p>This server could not verify that you are authorized to access the document requested. Either you supplied the wrong credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required.</p> </body></html>
但使用curl命令(示例:curl -v -T ./Settings.csv https://<hostname>/filetransfer/upload/104/18cfe8ef810-683/setting.csv)无需凭证即可成功上传文件,curl输出如下:
* Trying <host_ip>:5999... * Connected to <host> port 5999 (#0) * ALPN: offers h2 * ALPN: offers http/1.1 * CAfile: /etc/ssl/certs/ca-certificates.crt * CApath: /etc/ssl/certs * TLSv1.0 (OUT), TLS header, Certificate Status (22): * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.2 (IN), TLS header, Certificate Status (22): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.2 (IN), TLS header, Finished (20): * TLSv1.2 (IN), TLS header, Supplemental data (23): * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): * TLSv1.2 (IN), TLS header, Supplemental data (23): * TLSv1.3 (IN), TLS handshake, Certificate (11): * TLSv1.2 (IN), TLS header, Supplemental data (23): * TLSv1.3 (IN), TLS handshake, CERT verify (15): * TLSv1.2 (IN), TLS header, Supplemental data (23): * TLSv1.3 (IN), TLS handshake, Finished (20): * TLSv1.2 (OUT), TLS header, Finished (20): * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.2 (OUT), TLS header, Supplemental data (23): * TLSv1.3 (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN: server accepted http/1.1 * Server certificate: * subject: CN=<host> * start date: Nov 24 18:34:50 2023 GMT * expire date: Feb 22 18:34:49 2024 GMT * subjectAltName: host "<host>" matched cert's <host> * issuer: C=US; O=Let's Encrypt; CN=R3 * SSL certificate verify ok. * TLSv1.2 (OUT), TLS header, Supplemental data (23): > PUT /filetransfer/upload/104/18cfe8ef810-683/settignsss.zip HTTP/1.1 > Host: <host>:5999 > User-Agent: curl/7.85.0 > Accept: */* > Content-Length: 1397 > Expect: 100-continue > * TLSv1.2 (IN), TLS header, Supplemental data (23): * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * TLSv1.2 (IN), TLS header, Supplemental data (23): * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * old SSL session ID is stale, removing * TLSv1.2 (IN), TLS header, Supplemental data (23): * Mark bundle as not supporting multiuse < HTTP/1.1 100 Continue * TLSv1.2 (OUT), TLS header, Supplemental data (23): * We are completely uploaded and fine * TLSv1.2 (IN), TLS header, Supplemental data (23): * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Date: Fri, 12 Jan 2024 17:23:00 GMT < Server: Apache < X-Powered-By: Express < Set-Cookie: connect.sid=s%3A6aLxOXdCiLoK2ZEj9KpqS3ONuG-avYBF.BCqcr9WcsfJkn%2BkWiNSge4gSOnDSFOEX38ACvLWg0cA; Path=/; HttpOnly < Content-Length: 0 < Content-Type: application/zip < * Connection #0 to host <host> left intact
*已隐藏主机实际名称
请问我的C++代码中需要添加或调整哪些参数以解决401未授权问题?
对比你的代码请求和curl的请求,能发现几个关键差异,调整这些点即可解决问题:
移除自定义
path请求头:你的代码里额外添加了path: <path>头,但curl请求中没有这个字段。服务器可能将这个额外头视为非法请求标识,触发401校验。直接删除这一行:request_stream << "path: "<< path <<"\r\n";修正
Host头格式:curl的Host头包含端口号(Host: <host>:5999),但你的代码仅使用host变量,未携带端口。若服务器基于Host头做权限校验,缺失端口会导致校验失败。修改Host头为:request_stream << "Host: " << host << ":5999\r\n";注意端口号需与实际连接端口一致,即curl中的5999。
修复文件读取逻辑:你打开文件后用
source_file.tellg()获取大小,但此时文件指针位于末尾,直接用buffer << source_file.rdbuf()会读取不到内容。需先将文件指针移至开头:std::ifstream source_file(sendFilePath, std::ios::binary | std::ios::ate); size_t file_size = source_file.tellg(); source_file.seekg(0, std::ios::beg); // 添加此行,将指针移到文件开头 std::stringstream buffer; buffer << source_file.rdbuf();这个问题会导致发送的文件内容为空,服务器可能因此返回401(表面是权限问题,实际是请求内容异常)。
修正
Content-Type头的多余空格:你的代码中Content-Type: application/zip末尾多了一个空格,虽影响不大,但建议和curl保持一致,改为:request_stream << "Content-Type: application/zip\r\n";正确处理
Expect: 100-continue交互:curl发送该头后,会等待服务器返回100 Continue再发送文件内容,但你的代码直接将请求头和文件内容一起发送。部分服务器会因未正确处理此流程拒绝请求,你可以:- 先发送请求头(到
\r\n\r\n为止),读取服务器的100 Continue响应后,再发送文件内容; - 或直接移除
Expect: 100-continue头,让服务器直接接收完整请求。
- 先发送请求头(到
调整完这些点后,重新测试即可正常上传文件。
内容的提问来源于stack exchange,提问作者newww

