使用Boost Asio进行同步HTTPS POST

时间:2016-07-21 21:23:27

标签: c++ sockets http boost boost-asio

使用普通套接字通过HTTP上传10 MB文件时 - 一切都按预期工作:

string filename("c:\\test.zip");
long long fileSize = boost::filesystem::file_size(filename);

//Read file into memory
FILE * filePointer;
fopen_s(&filePointer, filename.c_str(), "rb");
unique_ptr<unsigned char[]> charArray(new unsigned char[1024*1024*20]);
fseek(filePointer, 0, SEEK_SET);
fread_s(charArray.get(), 1024 * 1024 * 20, sizeof(unsigned char), fileSize, filePointer);

//Setup Socket
boost::asio::io_service io_service;
tcp::endpoint ep;
ep.port(90);
ep.address(boost::asio::ip::address_v4::from_string("127.0.0.1"));

shared_ptr<tcp::socket> httpSocket = make_shared<tcp::socket>(io_service);
httpSocket->connect(ep);

string PREFIX = "--";
//Use GUID as boundary
string BOUNDARY = boost::uuids::to_string(boost::uuids::random_generator()()); 
string NEWLINE = "\r\n";
int NEWLINE_LENGTH = NEWLINE.length();

//Calculate length of entire HTTP request - goes into header
long long lengthOfRequest = 0;
lengthOfRequest += PREFIX.length() + BOUNDARY.length() + NEWLINE_LENGTH;
lengthOfRequest += string("Content-Disposition: form-data; name=\"fmChunk\"; filename=\"test.zip\"").length();
lengthOfRequest += NEWLINE_LENGTH + NEWLINE_LENGTH;
lengthOfRequest += fileSize;
lengthOfRequest += NEWLINE_LENGTH + PREFIX.length() + BOUNDARY.length() + PREFIX.length() + NEWLINE_LENGTH;

boost::asio::streambuf request;
std::ostream request_stream(&request);

request_stream << "POST /filehandler.ashx HTTP/1.1" << NEWLINE;
request_stream << "Host: localhost" << NEWLINE; // << ":" << port << NEWLINE;
request_stream << "User-Agent: FilemailDesktop2Cpp" << NEWLINE;
request_stream << "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" << NEWLINE;
request_stream << "Accept-Language: nb,no;q=0.8,nn;q=0.6,en-us;q=0.4,en;q=0.2" << NEWLINE;
request_stream << "Accept-Encoding: gzip;q=0,deflate;q=0" << NEWLINE; //Disables compression
request_stream << "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7" << NEWLINE;
request_stream << "Connection: close" << NEWLINE;
request_stream << "Content-Length: " << lengthOfRequest << NEWLINE;
request_stream << "Content-Type: multipart/form-data; boundary=" << BOUNDARY << NEWLINE;
request_stream << NEWLINE;

request_stream << PREFIX;
request_stream << BOUNDARY;
request_stream << NEWLINE;
request_stream << "Content-Disposition: form-data; name=\"fmChunk\"; filename=\"test.zip\"";
request_stream << NEWLINE;
request_stream << NEWLINE;

auto data = request.data();
httpSocket->write_some(buffer(data));

//Send Data (Paytload)
auto bytesSent = 0;
while (bytesSent < fileSize)
{
    int bytesToSendNow = min(fileSize - bytesSent, 1024 * 100);
    httpSocket->write_some(boost::asio::buffer(charArray.get() + bytesSent, bytesToSendNow));
    bytesSent += bytesToSendNow;
}

//Close request
httpSocket->write_some(boost::asio::buffer(NEWLINE));
httpSocket->write_some(boost::asio::buffer(PREFIX));
httpSocket->write_some(boost::asio::buffer(BOUNDARY));
httpSocket->write_some(boost::asio::buffer(PREFIX));
httpSocket->write_some(boost::asio::buffer(NEWLINE));

//Read Response
boost::asio::streambuf response;
read_until(*httpSocket, response, "\r\n");
string strResponse(boost::asio::buffer_cast<const char*>(response.data()), response.size());

//Check Response
if (strResponse.find("200 OK") != string::npos){
    cout << "OK";
}
else
{
    BOOST_FAIL("Upload failed");
}

但是通过HTTPS执行相同的上传不起作用。一个不同的端口+插座部分几乎完全不同。

string filename("c:\\test.zip");
long long fileSize = boost::filesystem::file_size(filename);

//Read file into memory
FILE * filePointer;
fopen_s(&filePointer, filename.c_str(), "rb");
unique_ptr<unsigned char[]> charArray(new unsigned char[1024 * 1024 * 20]);
fseek(filePointer, 0, SEEK_SET);
fread_s(charArray.get(), 1024 * 1024 * 20, sizeof(unsigned char), fileSize, filePointer);

//Setup Socket
boost::asio::io_service io_service;
tcp::endpoint ep;
ep.port(443);
ep.address(boost::asio::ip::address_v4::from_string("127.0.0.1"));

boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
ssl_socket sslSocket(io_service, ctx);
sslSocket.lowest_layer().connect(ep);
sslSocket.set_verify_mode(boost::asio::ssl::verify_none);
sslSocket.handshake(ssl_socket::client); 


string PREFIX = "--";
//Use GUID as boundary
string BOUNDARY = boost::uuids::to_string(boost::uuids::random_generator()());
string NEWLINE = "\r\n";
int NEWLINE_LENGTH = NEWLINE.length();

//Calculate length of entire HTTP request - goes into header
long long lengthOfRequest = 0;
lengthOfRequest += PREFIX.length() + BOUNDARY.length() + NEWLINE_LENGTH;
lengthOfRequest += string("Content-Disposition: form-data; name=\"fmChunk\"; filename=\"test.zip\"").length();
lengthOfRequest += NEWLINE_LENGTH + NEWLINE_LENGTH;
lengthOfRequest += fileSize;
lengthOfRequest += NEWLINE_LENGTH + PREFIX.length() + BOUNDARY.length() + PREFIX.length() + NEWLINE_LENGTH;

boost::asio::streambuf request;
std::ostream request_stream(&request);

request_stream << "POST /filehandler.ashx HTTP/1.1" << NEWLINE;
request_stream << "Host: localhost" << NEWLINE; // << ":" << port << NEWLINE;
request_stream << "User-Agent: FilemailDesktop2Cpp" << NEWLINE;
request_stream << "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" << NEWLINE;
request_stream << "Accept-Language: nb,no;q=0.8,nn;q=0.6,en-us;q=0.4,en;q=0.2" << NEWLINE;
request_stream << "Accept-Encoding: gzip;q=0,deflate;q=0" << NEWLINE; //Disables compression
request_stream << "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7" << NEWLINE;
request_stream << "Connection: close" << NEWLINE;
request_stream << "Content-Length: " << lengthOfRequest << NEWLINE;
request_stream << "Content-Type: multipart/form-data; boundary=" << BOUNDARY << NEWLINE;
request_stream << NEWLINE;

request_stream << PREFIX;
request_stream << BOUNDARY;
request_stream << NEWLINE;
request_stream << "Content-Disposition: form-data; name=\"fmChunk\"; filename=\"test.zip\"";
request_stream << NEWLINE;
request_stream << NEWLINE;

auto data = request.data();
sslSocket.write_some(buffer(data));

//Send Data (Paytload)
auto bytesSent = 0;
while (bytesSent < fileSize)
{
    int bytesToSendNow = min(fileSize - bytesSent, 1024 * 100); 
    sslSocket.write_some(boost::asio::buffer(charArray.get() + bytesSent, bytesToSendNow));
    bytesSent += bytesToSendNow;
}

//Close request
sslSocket.write_some(boost::asio::buffer(NEWLINE));
sslSocket.write_some(boost::asio::buffer(PREFIX));
sslSocket.write_some(boost::asio::buffer(BOUNDARY));
sslSocket.write_some(boost::asio::buffer(PREFIX));
sslSocket.write_some(boost::asio::buffer(NEWLINE));


//Read Response
boost::asio::streambuf response;
read_until(sslSocket, response, "\r\n");
string strResponse(boost::asio::buffer_cast<const char*>(response.data()), response.size());

//Check Response
if (strResponse.find("200 OK") != string::npos){
    cout << "OK";
}
else
{
    BOOST_FAIL("Upload failed");
}

运行此代码时,上传会在

处停止
read_until(*sslSocket, response, "\r\n");

如果我然后终止进程 - 套接字被关闭 - IIS Express网络服务器(我还附加了一个调试器)接收请求并处理它。服务器接收的上载文件的长度始终为1772261字节。

当尝试上传1MB文件时 - 同样的情况发生,服务器只接收180224个字节。

我能够使用类似的sslSocket代码发出HTTPS GET请求 - 而普通的HTTP POST也可以正常工作 - 所以它似乎是SSL和POST的组合,这给我带来了一些问题。

Boost中的sslSockets是否存在内部缓冲区/流限制?

如果有人能够对此有所了解,我们将不胜感激。

1 个答案:

答案 0 :(得分:1)

首先 - 抱歉复制/粘贴错误。它现在已得到纠正。

无论如何 - 事实证明问题在于我每次都在写入(1024 * 100)字节到套接字 - 这对于sslSocket来说太多了 - 虽然它对于httpSocket工作得很好。将它减少到只有1024就解决了问题。

我真的应该检查sslSocket.write_some方法的返回值,以确保写入预期的字节数。

enter image description here