使用一個第三方的http請求,在VS裏打印如下,而作者在處理時,以\r\n\r\n處理獲取頭部,剩餘部分全部作爲了body,害的我追了幾個小時,wireShark抓包如下。
http分四部分:頭部、頭部分隔符、body長度,body
(頭部)
\r\n\r\n
(bodylength)\r\n
body
HTTP/1.1 200 OK\r\n
Date: Tue, 14 Jun 2016 17:42:00 GMT\r\n
Server: Apache/2.4.7 (Ubuntu)\r\n
Vary: Accept-Encoding\r\n
Connection: close\r\n
Transfer-Encoding: chunked\r\n
Content-Type: text/plain;charset=UTF-8\r\n
\r\n\r\n
71\r\n
00rtmp://video-center.alivecdn.com/show/3-1?vhost=live.abcdedo.com\r\n
閫夋墜鐜嬪皬涓洿鎾?\r\n
admin\r\n
2eCuw0F\r\n
S0000007qv\r\n
看到上述 71\r\n,該被處理掉,此爲body的長度(後面帶了一個\r\n作爲結束符)
附上wireShark的抓包圖:
122bytes的數據段和VS打印的相符
細心的讀者獲取會看到:圖中出現了(gzip):132 bytes -> 122 bytes
還沒有搞懂,在 HTTP chunked response 裏爲什麼兩個 Data chunk,,分別爲122 + 10,
而gizp又是怎麼使用的呢??有機會繼續追蹤。。
附HTTP:找不到原作者是誰了,和下載地址了,非常感謝原作者的貢獻:
Http.h
#pragma once
#include <string>
typedef bool (*httpResponseCB)(const char* data, int len, int offset, int totalsize, bool bFin, void* param) ;
struct HttpResponse
{
public:
explicit HttpResponse(){ clear();}
std::string http_version; // 版本
unsigned int status_code; // 狀態碼
std::string status_message; // 狀態
std::string header; // HTTP包頭
std::string body; // HTTP返回的內容
std::string content_type;
std::string modify_time;
unsigned int content_length;
unsigned int total_length;
unsigned int offset;
httpResponseCB funcResponseCB;
void* param;
void clear()
{
http_version.clear();
status_code = -1;
status_message.clear();
header.clear();
content_type.clear();
modify_time.clear();
content_length = 0;
total_length = 0;
offset = 0;
body.clear();
funcResponseCB = NULL;
param = NULL;
}
};
class CHttpPrivate; // 封裝ASIO操作
/*
* @brief 發送http請求並接收結果, 單線程, 同步
*/
class CHttp
{
public:
CHttp(void);
virtual ~CHttp(void);
//************************************
// Method: PostRequest
// FullName: CHttp::PostRequest
// Access: public
// Returns: bool
// Qualifier:
// Parameter: const std::string & szUrl
// Parameter: CHttpResponse & result HTTP請求返回的結果
// Parameter: const std::string & data 默認爲空, 使用GET http請求, 若不爲空, 則POST HTTP請求和data
//************************************
bool PostRequest(const std::string &szUrl, HttpResponse &result, const std::string &data = std::string(), int rangeStart = 0); // post
bool PostRequest(const std::string &szUrl, std::string &szResult, const std::string &data = std::string()); // post
//************************************
// Method: ParseUrl
// FullName: CHttp::ParseUrl
// Access: public
// Returns: bool return false indicate invalid URL
// Qualifier:
// Parameter: std::string szUrl 傳入URL
// Parameter: std::string & szHost 解析後的host
// Parameter: std::string & szParam 解析後的host參數
//************************************
bool ParseUrl(std::string szUrl, std::string &szHost, std::string &szParam);
enum{
SUCCESS_OK = 0,
ERR_BADURL,
ERR_NETWORK,
ERR_HTTPRESP,
ERR_UNKNOWN
};
int GetLastErr() { return m_lasterr;};
private:
int m_lasterr;
CHttpPrivate *p;
friend class CHttpPrivate;
};
Http.cpp
#include "stdafx.h"
#include "Http.h"
#include <vector>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <boost/asio.hpp>
#include <boost/algorithm/string.hpp>
#include "CStringUtilUnicode.h"
using boost::asio::ip::tcp;
class CHttpPrivate
{
private:
bool ParseHeader(const std::string &str, HttpResponse &result);
CHttp* _d;
public:
CHttpPrivate(CHttp* d);
enum HTTP_METHOD{ GET, POST};
bool RecvResponse(HttpResponse &result);
bool connect(const std::string &szHost);
void FillRequest(
boost::asio::streambuf &request,
HTTP_METHOD method ,
const std::string &szHost,
const std::string &szParam,
const std::string &data = std::string(),
const int rangeStart = 0);
boost::asio::io_service m_io_service;
tcp::resolver m_resolver;
tcp::socket m_socket;
};
CHttpPrivate::CHttpPrivate( CHttp* d )
: m_resolver(m_io_service)
, m_socket(m_io_service)
{
_d = d;
}
bool CHttpPrivate::ParseHeader(const std::string &strHeader, HttpResponse &result)
{
// Check that response is OK.
std::istringstream response_stream(strHeader);
response_stream >> result.http_version;
response_stream >> result.status_code;
std::string strLine;
std::getline(response_stream, strLine);
while (!strLine.empty())
{
if (strLine.find("Content-Type:") != std::string::npos)
{
result.content_type = strLine.substr(strlen("Content-Type:"));
result.content_type.erase(0, result.content_type.find_first_not_of(" "));
}
if (strLine.find("Content-Length:") != std::string::npos)
{
result.content_length = atoi(strLine.substr(strlen("Content-Length:")).c_str());
result.total_length = result.content_length;
}
if (strLine.find("Last-Modified:") != std::string::npos)
{
result.modify_time = strLine.substr(strlen("Last-Modified:"));
result.modify_time.erase(0, result.modify_time.find_first_not_of(" "));
}
if (strLine.find("Content-Range: bytes") != std::string::npos)
{
std::string tmp = strLine.substr(strlen("Content-Range: bytes"));
result.offset = atoi(tmp.substr(0, tmp.find('-')).c_str());
int ipos = tmp.find('/');
int ivalue = 0;
if (ipos != std::string::npos)
{
ivalue = atoi(tmp.substr(ipos+1).c_str());
}
if (ivalue)
result.total_length = ivalue;
}
strLine.clear();
std::getline(response_stream, strLine);
}
/*
if (result.offset < 0 && result.content_length+result.offset>0 )
result.offset += result.content_length;
*/
if ( result.http_version.substr(0, 5) != "HTTP/")
{
_d->m_lasterr = CHttp::ERR_HTTPRESP;
std::cout << "Invalid response\n";
return false;
}
if (result.status_code != 200 && result.status_code != 206)
{
_d->m_lasterr = CHttp::ERR_HTTPRESP;
std::cout << "Response returned with status code "
<< result.status_code << "\n";
return false;
}
return true;
}
bool CHttpPrivate::RecvResponse( HttpResponse &result )
{
boost::asio::streambuf response;
std::ostringstream packetStream;
try
{
_d->m_lasterr = CHttp::SUCCESS_OK;
// Read until EOF, writing data to output as we go.
bool hasReadHeader = false;
boost::system::error_code error;
result.body.clear();
while (boost::asio::read(m_socket, response, boost::asio::transfer_at_least(1), error))
{
packetStream.str("");
packetStream << &response;
std::string packetString = packetStream.str();
std::wstring log = CUtility::Ansi2Wchar(packetString);
OutputDebugString(log.c_str());
if (!hasReadHeader)
{
// 取出http header
size_t nEndHeader = packetString.find("\r\n\r\n");
if(nEndHeader == std::string::npos)
continue;
hasReadHeader = true;
result.header = packetString.substr(0, nEndHeader);
if (!ParseHeader(result.header, result))
return false;
packetString.erase(0, nEndHeader + 4);
int bodyLenPos = packetString.find("\r\n");
packetString.erase(0, bodyLenPos + 2);
int bodyEndPos = packetString.find("\r\n");
packetString = packetString.substr(0, bodyEndPos);
std::wstring log = CUtility::Ansi2Wchar(packetString);
OutputDebugString(log.c_str());
}
// response.consume(response.size());
if (result.funcResponseCB)
{
if (!result.param)
{
_d->m_lasterr = CHttp::ERR_UNKNOWN;
return false;
}
if (!result.funcResponseCB(packetString.c_str(), packetString.length(), result.offset, result.total_length, false, result.param))
{
_d->m_lasterr = CHttp::ERR_UNKNOWN;
return false;
}
}
else
{
result.body.append(packetString);
}
}
if (result.funcResponseCB)
{
result.funcResponseCB(NULL, 0, 0, result.content_length, true, result.param);
}
if (error != boost::asio::error::eof)
{
_d->m_lasterr = CHttp::ERR_UNKNOWN;
throw boost::system::system_error(error);
}
}
catch (std::exception& e)
{
std::cout << "Exception: " << e.what() << "\n";
if (_d->m_lasterr == CHttp::SUCCESS_OK)
_d->m_lasterr = CHttp::ERR_NETWORK;
return false;
}
return true;
}
void CHttpPrivate::FillRequest( boost::asio::streambuf &request, HTTP_METHOD method , const std::string &szHost, const std::string &szParam, const std::string &data /*= std::string()*/, const int rangeStart)
{
// Form the request. We specify the "Connection: close" header so that the
// server will close the socket after transmitting the response. This will
// allow us to treat all data up until the EOF as the content.
std::ostream request_stream(&request);
switch(method)
{
case GET:
request_stream << "GET " ;
request_stream << szParam << " HTTP/1.1\r\n";
request_stream << "Host: " << szHost << "\r\n";
break;
case POST:
request_stream << "POST ";
request_stream << szParam << " HTTP/1.1\r\n";
request_stream << "Host: " << szHost << "\r\n";
request_stream << "Content-Length:" << data.size();
break;
}
request_stream << "Accept: */*\r\n";
request_stream << "Pragma: no-cache\r\n";
request_stream << "Cache-Control: no-cache\r\n";
request_stream << "Connection: close\r\n";
if (rangeStart)
{
request_stream << "Range: bytes=" << rangeStart << "- \r\n";
}
request_stream << "\r\n";
}
bool CHttpPrivate::connect( const std::string &szHost )
{
// Get a list of endpoints corresponding to the server name.
std::string szService ("http");
std::string szIp = szHost;
int i = szHost.find(":") ;
if (i != -1)
{
szService = szHost.substr(i+1);
szIp = szHost.substr(0, i);
}
tcp::resolver::query query(szIp, szService);
tcp::resolver::iterator endpoint_iterator = m_resolver.resolve(query), end_it;
// Try each endpoint until we successfully establish a connection.
tcp::resolver::iterator it = boost::asio::connect(m_socket, endpoint_iterator);
if(it == end_it)
return false;
return true;
}
CHttp::CHttp(void)
{
p = new CHttpPrivate(this);
m_lasterr = SUCCESS_OK;
}
CHttp::~CHttp(void)
{
delete p;
}
bool CHttp::ParseUrl(std::string szUrl, std::string &szHost, std::string &szParam)
{
do
{
if(szUrl.empty())
break;
boost::trim(szUrl);
std::string szProtocol = szUrl.substr(0, 7);
boost::to_upper(szProtocol);
size_t pos;
if(szProtocol.compare("HTTP://") != 0 )
break;
pos = szUrl.find_first_of("/", 7);
if(std::string::npos == pos)
break;
szHost = szUrl.substr(7, pos - 7);
szParam = szUrl.substr(pos, std::string::npos);
return true;
} while (0);
return false;
}
bool CHttp::PostRequest( const std::string &szUrl, HttpResponse &result, const std::string &data /*= std::string()*/ , int rangeStart/* =0 */)
{
try
{
std::string szHost;
std::string szParam;
if(!ParseUrl(szUrl, szHost, szParam))
{
m_lasterr = ERR_BADURL;
std::cerr << "Fail in parsing url " << std::endl;
return false;
}
if(!p->connect(szHost))
{
m_lasterr = ERR_NETWORK;
return false;
}
boost::asio::streambuf request;
if(data.empty())
{
p->FillRequest(request, CHttpPrivate::GET, szHost, szParam, std::string(), rangeStart);
boost::asio::write(p->m_socket, request);
}
else
{
p->FillRequest(request, CHttpPrivate::POST, szHost, szParam, std::string(), rangeStart);
boost::asio::write(p->m_socket, request);
boost::asio::write(p->m_socket, boost::asio::buffer(data));
}
if(!p->RecvResponse(result))
return false;
}
catch (std::exception& e)
{
m_lasterr = ERR_UNKNOWN;
std::cout << "Exception: " << e.what() << "\n";
return false;
}
return true;
}
bool CHttp::PostRequest( const std::string &szUrl, std::string &szResult, const std::string &data /*= std::string()*/ )
{
HttpResponse result;
if(!this->PostRequest(szUrl, result))
return false;
szResult = result.body;
return true;
}