您的位置:首页 > 理论基础 > 计算机网络

【Web】C++ Http -- 记一次使用第三方http请求的问题解决

2016-06-15 02:10 549 查看
使用一个第三方的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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  web HTTP