mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-22 06:05:38 +00:00
Added HttpReq class based on Boost.Asio.
This commit is contained in:
parent
8e12ff9506
commit
c807c98b4a
|
@ -49,6 +49,24 @@ if(DEFINED BCMHOST)
|
|||
add_definitions(-D_RPI_)
|
||||
endif()
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#set up _WIN32_WINNT variable (used by boost::asio) on Windows
|
||||
macro(get_WIN32_WINNT version)
|
||||
if (WIN32 AND CMAKE_SYSTEM_VERSION)
|
||||
set(ver ${CMAKE_SYSTEM_VERSION})
|
||||
string(REPLACE "." "" ver ${ver})
|
||||
string(REGEX REPLACE "([0-9])" "0\\1" ver ${ver})
|
||||
|
||||
set(${version} "0x${ver}")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
if(WIN32)
|
||||
get_WIN32_WINNT(ver)
|
||||
add_definitions(-D_WIN32_WINNT=${ver})
|
||||
endif()
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_DEBUG_POSTFIX "d")
|
||||
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
|
||||
|
@ -122,6 +140,7 @@ set(ES_HEADERS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/Font.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/GameData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/GuiComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/HttpReq.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ImageIO.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/InputConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/InputManager.h
|
||||
|
@ -167,6 +186,7 @@ set(ES_SOURCES
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/Font.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/GameData.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/GuiComponent.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/HttpReq.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ImageIO.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/InputConfig.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/InputManager.cpp
|
||||
|
|
|
@ -27,12 +27,12 @@ Building
|
|||
EmulationStation uses some C++11 code, which means you'll need to install at least g++-4.7 on Linux, or VS2010 on Windows.
|
||||
For installing and switching to g++-4.7 see [here](http://lektiondestages.blogspot.de/2013/05/installing-and-switching-gccg-versions.html). You can also just use `export CXX=g++-4.7` to explicitly specify the compiler for CMake (make sure you delete your CMake cache files if it's not working).
|
||||
|
||||
EmulationStation has a few dependencies. For building, you'll need SDL2, Boost.System, Boost.Filesystem, FreeImage, FreeType, and Eigen3. You'll also need the DejaVu TrueType font on Linux to run ES.
|
||||
EmulationStation has a few dependencies. For building, you'll need SDL2, Boost.System, Boost.Filesystem, Boost.Asio, FreeImage, FreeType, and Eigen3. You'll also need the DejaVu TrueType font on Linux to run ES.
|
||||
|
||||
**On Linux:**
|
||||
All of this be easily installed with apt-get:
|
||||
```
|
||||
sudo apt-get install libsdl2-dev libboost-system-dev libboost-filesystem-dev libfreeimage-dev libfreetype6-dev libeigen3-dev ttf-dejavu libasound2-dev
|
||||
sudo apt-get install libsdl2-dev libboost-dev libboost-system-dev libboost-filesystem-dev libfreeimage-dev libfreetype6-dev libeigen3-dev ttf-dejavu libasound2-dev
|
||||
```
|
||||
|
||||
On "desktop" Linux (that is, *not* the Raspberry Pi), you'll also need OpenGL. Try installing the MESA development package with:
|
||||
|
@ -52,7 +52,7 @@ make
|
|||
|
||||
**On Windows:**
|
||||
|
||||
[Boost](http://www.boost.org/users/download/) (you'll need to compile for Boost.Filesystem)
|
||||
[Boost](http://www.boost.org/users/download/) (you'll need to compile for Boost.Filesystem and Boost.System)
|
||||
|
||||
[Eigen3](http://eigen.tuxfamily.org/index.php?title=Main_Page)
|
||||
|
||||
|
|
194
src/HttpReq.cpp
Normal file
194
src/HttpReq.cpp
Normal file
|
@ -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 "???";
|
||||
}
|
||||
}
|
68
src/HttpReq.h
Normal file
68
src/HttpReq.h
Normal file
|
@ -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;
|
||||
};
|
Loading…
Reference in a new issue