forked from alphanu1/EmulationStation
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added HttpReq class based on Boost.Asio.
- Loading branch information
Showing
4 changed files
with
285 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
#include <iostream> | ||
#include "HttpReq.h" | ||
#include <boost/bind.hpp> | ||
#include "Log.h" | ||
|
||
boost::asio::io_service HttpReq::io_service; | ||
|
||
HttpReq::HttpReq(const std::string& server, const std::string& path) | ||
: mResolver(io_service), mSocket(io_service), mStatus(REQ_IN_PROGRESS) | ||
{ | ||
std::ostream req_str(&mRequest); | ||
req_str << "GET " << path << " HTTP/1.0\r\n"; | ||
req_str << "Host: " << server << "\r\n"; | ||
req_str << "Accept: */*\r\n"; | ||
req_str << "Connection: close\r\n\r\n"; | ||
|
||
tcp::resolver::query query(server, "http"); | ||
mResolver.async_resolve(query, | ||
boost::bind(&HttpReq::handleResolve, this, | ||
boost::asio::placeholders::error, | ||
boost::asio::placeholders::iterator)); | ||
} | ||
|
||
|
||
void HttpReq::handleResolve(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator) | ||
{ | ||
if (!err) | ||
{ | ||
// Attempt a connection to each endpoint in the list until we | ||
// successfully establish a connection. | ||
boost::asio::async_connect(mSocket, endpoint_iterator, | ||
boost::bind(&HttpReq::handleConnect, this, | ||
boost::asio::placeholders::error)); | ||
} | ||
else | ||
{ | ||
onError(err); | ||
} | ||
} | ||
|
||
void HttpReq::handleConnect(const boost::system::error_code& err) | ||
{ | ||
if (!err) | ||
{ | ||
// The connection was successful. Send the request. | ||
boost::asio::async_write(mSocket, mRequest, | ||
boost::bind(&HttpReq::handleWriteRequest, this, | ||
boost::asio::placeholders::error)); | ||
} | ||
else | ||
{ | ||
onError(err); | ||
} | ||
} | ||
|
||
void HttpReq::handleWriteRequest(const boost::system::error_code& err) | ||
{ | ||
if (!err) | ||
{ | ||
// Read the response status line. The response_ streambuf will | ||
// automatically grow to accommodate the entire line. The growth may be | ||
// limited by passing a maximum size to the streambuf constructor. | ||
boost::asio::async_read_until(mSocket, mResponse, "\r\n", | ||
boost::bind(&HttpReq::handleReadStatusLine, this, | ||
boost::asio::placeholders::error)); | ||
} | ||
else | ||
{ | ||
onError(err); | ||
} | ||
} | ||
|
||
void HttpReq::handleReadStatusLine(const boost::system::error_code& err) | ||
{ | ||
if (!err) | ||
{ | ||
// Check that response is OK. | ||
std::istream response_stream(&mResponse); | ||
std::string http_version; | ||
response_stream >> http_version; | ||
response_stream >> mResponseStatusCode; | ||
std::string status_message; | ||
std::getline(response_stream, status_message); | ||
if(!response_stream || http_version.substr(0, 5) != "HTTP/") | ||
{ | ||
mStatus = REQ_INVALID_RESPONSE; | ||
return; | ||
} | ||
if(mResponseStatusCode != 200) | ||
{ | ||
mStatus = REQ_BAD_STATUS_CODE; | ||
return; | ||
} | ||
|
||
// Read the response headers, which are terminated by a blank line. | ||
boost::asio::async_read_until(mSocket, mResponse, "\r\n\r\n", | ||
boost::bind(&HttpReq::handleReadHeaders, this, | ||
boost::asio::placeholders::error)); | ||
} | ||
else | ||
{ | ||
onError(err); | ||
} | ||
} | ||
|
||
void HttpReq::handleReadHeaders(const boost::system::error_code& err) | ||
{ | ||
if (!err) | ||
{ | ||
// Process the response headers. | ||
std::istream response_stream(&mResponse); | ||
std::string header; | ||
while (std::getline(response_stream, header) && header != "\r"); //and by process we mean ignore | ||
|
||
// Write whatever content we already have to output. | ||
if (mResponse.size() > 0) | ||
mContent << &mResponse; | ||
|
||
// Start reading remaining data until EOF. | ||
boost::asio::async_read(mSocket, mResponse, | ||
boost::asio::transfer_at_least(1), | ||
boost::bind(&HttpReq::handleReadContent, this, | ||
boost::asio::placeholders::error)); | ||
} | ||
else | ||
{ | ||
onError(err); | ||
} | ||
} | ||
|
||
void HttpReq::handleReadContent(const boost::system::error_code& err) | ||
{ | ||
if (!err) | ||
{ | ||
// Write all of the data that has been read so far. | ||
mContent << &mResponse; | ||
|
||
// Continue reading remaining data until EOF. | ||
boost::asio::async_read(mSocket, mResponse, | ||
boost::asio::transfer_at_least(1), | ||
boost::bind(&HttpReq::handleReadContent, this, | ||
boost::asio::placeholders::error)); | ||
}else{ | ||
if (err != boost::asio::error::eof) | ||
{ | ||
onError(err); | ||
}else{ | ||
mStatus = REQ_SUCCESS; | ||
} | ||
} | ||
} | ||
|
||
HttpReq::Status HttpReq::status() | ||
{ | ||
io_service.poll(); | ||
return mStatus; | ||
} | ||
|
||
std::string HttpReq::getContent() | ||
{ | ||
if(mStatus != REQ_SUCCESS) | ||
{ | ||
LOG(LogError) << "Called getContent() on an unsuccessful HttpReq!"; | ||
return ""; | ||
} | ||
|
||
return mContent.str(); | ||
} | ||
|
||
//only called for boost-level errors (REQ_IO_ERROR) | ||
void HttpReq::onError(const boost::system::error_code& err) | ||
{ | ||
mError = err; | ||
mStatus = REQ_IO_ERROR; | ||
} | ||
|
||
std::string HttpReq::getErrorMsg() | ||
{ | ||
switch(mStatus) | ||
{ | ||
case REQ_BAD_STATUS_CODE: | ||
return "Bad status code"; | ||
case REQ_INVALID_RESPONSE: | ||
return "Invalid response from server"; | ||
case REQ_IO_ERROR: | ||
return mError.message(); | ||
case REQ_IN_PROGRESS: | ||
return "Not done yet"; | ||
case REQ_SUCCESS: | ||
return "No error"; | ||
default: | ||
return "???"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
#pragma once | ||
|
||
#include <boost/asio.hpp> | ||
|
||
using boost::asio::ip::tcp; | ||
|
||
//Based on: http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/example/http/client/async_client.cpp | ||
|
||
/* Usage: | ||
* HttpReq myRequest("www.google.com", "/index.html"); | ||
* //for blocking behavior: while(myRequest.status() == HttpReq::REQ_IN_PROGRESS); | ||
* //for non-blocking behavior: check if(myRequest.status() != HttpReq::REQ_IN_PROGRESS) in some sort of update method | ||
* | ||
* //once one of those completes, the request is ready | ||
* if(myRequest.status() != REQ_SUCCESS) | ||
* { | ||
* //an error occured | ||
* LOG(LogError) << "HTTP request error - " << myRequest.getErrorMessage(); | ||
* return; | ||
* } | ||
* | ||
* std::string content = myRequest.getContent(); | ||
* //process contents... | ||
*/ | ||
|
||
class HttpReq | ||
{ | ||
public: | ||
HttpReq(const std::string& server, const std::string& path); | ||
|
||
enum Status | ||
{ | ||
REQ_IN_PROGRESS, //request is in progress | ||
REQ_SUCCESS, //request completed successfully, get it with getContent() | ||
|
||
REQ_IO_ERROR, //some boost::asio error happened, get it with getErrorMsg() | ||
REQ_BAD_STATUS_CODE, //some invalid HTTP response status code happened (non-200) | ||
REQ_INVALID_RESPONSE //the HTTP response was invalid | ||
}; | ||
|
||
Status status(); //process any received data and return the status afterwards | ||
|
||
std::string getErrorMsg(); | ||
|
||
std::string getContent(); | ||
|
||
private: | ||
static boost::asio::io_service io_service; | ||
|
||
void handleResolve(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator); | ||
void handleConnect(const boost::system::error_code& err); | ||
void handleWriteRequest(const boost::system::error_code& err); | ||
void handleReadStatusLine(const boost::system::error_code& err); | ||
void handleReadHeaders(const boost::system::error_code& err); | ||
void handleReadContent(const boost::system::error_code& err); | ||
|
||
void onError(const boost::system::error_code& error); | ||
|
||
tcp::resolver mResolver; | ||
tcp::socket mSocket; | ||
boost::asio::streambuf mRequest; | ||
boost::asio::streambuf mResponse; | ||
|
||
Status mStatus; | ||
std::stringstream mContent; | ||
unsigned int mResponseStatusCode; | ||
boost::system::error_code mError; | ||
}; |