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_)
|
add_definitions(-D_RPI_)
|
||||||
endif()
|
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)
|
if(MSVC)
|
||||||
set(CMAKE_DEBUG_POSTFIX "d")
|
set(CMAKE_DEBUG_POSTFIX "d")
|
||||||
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
|
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/Font.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/GameData.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/GameData.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/GuiComponent.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/ImageIO.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/InputConfig.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/InputConfig.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/InputManager.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/Font.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/GameData.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/GameData.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/GuiComponent.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/ImageIO.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/InputConfig.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/InputConfig.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/InputManager.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.
|
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).
|
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:**
|
**On Linux:**
|
||||||
All of this be easily installed with apt-get:
|
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:
|
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:**
|
**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)
|
[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