Files
eepp/include/eepp/network/chttp.hpp
2013-11-01 23:31:12 -03:00

354 lines
14 KiB
C++

#ifndef EE_NETWORKCHTTP_HPP
#define EE_NETWORKCHTTP_HPP
#include <eepp/network/base.hpp>
#include <eepp/network/cipaddress.hpp>
#include <eepp/network/ctcpsocket.hpp>
#include <eepp/base/noncopyable.hpp>
#include <eepp/system/ctime.hpp>
#include <eepp/system/tthreadlocalptr.hpp>
#include <eepp/system/cthread.hpp>
#include <eepp/system/cmutex.hpp>
#include <eepp/system/clock.hpp>
#include <eepp/helper/PlusCallback/callback.hpp>
#include <map>
#include <string>
#include <list>
using namespace EE::System;
namespace EE { namespace Network {
/** @brief A HTTP client */
class EE_API cHttp : NonCopyable {
public :
/** @brief Define a HTTP request */
class EE_API Request {
public :
/** @brief Enumerate the available HTTP methods for a request */
enum Method {
Get, ///< Request in get mode, standard method to retrieve a page
Post, ///< Request in post mode, usually to send data to a page
Head, ///< Request a page's header only
Put, ///< Request in put mode, useful for a REST API
Delete ///< Request in delete mode, useful for a REST API
};
/** @brief Default constructor
** This constructor creates a GET request, with the root
** URI ("/") and an empty body.
** @param uri Target URI
** @param method Method to use for the request
** @param body Content of the request's body */
Request(const std::string& uri = "/", Method method = Get, const std::string& body = "");
/** @brief Set the value of a field
** The field is created if it doesn't exist. The name of
** the field is case insensitive.
** By default, a request doesn't contain any field (but the
** mandatory fields are added later by the HTTP client when
** sending the request).
///
** @param field Name of the field to set
** @param value Value of the field */
void SetField(const std::string& field, const std::string& value);
/** @brief Set the request method
** See the Method enumeration for a complete list of all
** the availale methods.
** The method is cHttp::Request::Get by default.
** @param method Method to use for the request */
void SetMethod(Method method);
/** @brief Set the requested URI
** The URI is the resource (usually a web page or a file)
** that you want to get or post.
** The URI is "/" (the root page) by default.
** @param uri URI to request, relative to the host */
void SetUri(const std::string& uri);
/** @brief Set the HTTP version for the request
** The HTTP version is 1.0 by default.
** @param major Major HTTP version number
** @param minor Minor HTTP version number */
void SetHttpVersion(unsigned int major, unsigned int minor);
/** @brief Set the body of the request
** The body of a request is optional and only makes sense
** for POST requests. It is ignored for all other methods.
** The body is empty by default.
** @param body Content of the body */
void SetBody(const std::string& body);
/** @return The request Uri */
const std::string& GetUri() const;
private:
friend class cHttp;
/** @brief Prepare the final request to send to the server
** This is used internally by cHttp before sending the
** request to the web server.
** @return String containing the request, ready to be sent */
std::string Prepare() const;
/** @brief Check if the request defines a field
** This function uses case-insensitive comparisons.
** @param field Name of the field to test
** @return True if the field exists, false otherwise */
bool HasField(const std::string& field) const;
// Types
typedef std::map<std::string, std::string> FieldTable;
// Member data
FieldTable mFields; ///< Fields of the header associated to their value
Method mMethod; ///< Method to use for the request
std::string mUri; ///< Target URI of the request
unsigned int mMajorVersion; ///< Major HTTP version
unsigned int mMinorVersion; ///< Minor HTTP version
std::string mBody; ///< Body of the request
};
/** @brief Define a HTTP response */
class EE_API Response {
public:
/** @brief Enumerate all the valid status codes for a response */
enum Status {
// 2xx: success
Ok = 200, ///< Most common code returned when operation was successful
Created = 201, ///< The resource has successfully been created
Accepted = 202, ///< The request has been accepted, but will be processed later by the server
NoContent = 204, ///< The server didn't send any data in return
ResetContent = 205, ///< The server informs the client that it should clear the view (form) that caused the request to be sent
PartialContent = 206, ///< The server has sent a part of the resource, as a response to a partial GET request
// 3xx: redirection
MultipleChoices = 300, ///< The requested page can be accessed from several locations
MovedPermanently = 301, ///< The requested page has permanently moved to a new location
MovedTemporarily = 302, ///< The requested page has temporarily moved to a new location
NotModified = 304, ///< For conditionnal requests, means the requested page hasn't changed and doesn't need to be refreshed
// 4xx: client error
BadRequest = 400, ///< The server couldn't understand the request (syntax error)
Unauthorized = 401, ///< The requested page needs an authentification to be accessed
Forbidden = 403, ///< The requested page cannot be accessed at all, even with authentification
NotFound = 404, ///< The requested page doesn't exist
RangeNotSatisfiable = 407, ///< The server can't satisfy the partial GET request (with a "Range" header field)
// 5xx: server error
InternalServerError = 500, ///< The server encountered an unexpected error
NotImplemented = 501, ///< The server doesn't implement a requested feature
BadGateway = 502, ///< The gateway server has received an error from the source server
ServiceNotAvailable = 503, ///< The server is temporarily unavailable (overloaded, in maintenance, ...)
GatewayTimeout = 504, ///< The gateway server couldn't receive a response from the source server
VersionNotSupported = 505, ///< The server doesn't support the requested HTTP version
// 10xx: Custom codes
InvalidResponse = 1000, ///< Response is not a valid HTTP one
ConnectionFailed = 1001 ///< Connection with server failed
};
/** @brief Default constructor
** Constructs an empty response. */
Response();
/** @brief Get the value of a field
** If the field @a field is not found in the response header,
** the empty string is returned. This function uses
** case-insensitive comparisons.
** @param field Name of the field to get
** @return Value of the field, or empty string if not found */
const std::string& GetField(const std::string& field) const;
/** @brief Get the response status code
** The status code should be the first thing to be checked
** after receiving a response, it defines whether it is a
** success, a failure or anything else (see the Status
** enumeration).
** @return Status code of the response */
Status GetStatus() const;
/** @brief Get the major HTTP version number of the response
** @return Major HTTP version number
** @see GetMinorHttpVersion */
unsigned int GetMajorHttpVersion() const;
/** @brief Get the minor HTTP version number of the response
** @return Minor HTTP version number
** @see GetMajorHttpVersion */
unsigned int GetMinorHttpVersion() const;
/** @brief Get the body of the response
** The body of a response may contain:
** @li the requested page (for GET requests)
** @li a response from the server (for POST requests)
** @li nothing (for HEAD requests)
** @li an error message (in case of an error)
** @return The response body */
const std::string& GetBody() const;
private :
friend class cHttp;
/** @brief Construct the header from a response string
** This function is used by cHttp to build the response
** of a request.
** @param data Content of the response to parse */
void Parse(const std::string& data);
/** @brief Read values passed in the answer header
** This function is used by Http to extract values passed
** in the response.
** @param in String stream containing the header values */
void ParseFields(std::istream &in);
// Types
typedef std::map<std::string, std::string> FieldTable;
// Member data
FieldTable mFields; ///< Fields of the header
Status mStatus; ///< Status code
unsigned int mMajorVersion; ///< Major HTTP version
unsigned int mMinorVersion; ///< Minor HTTP version
std::string mBody; ///< Body of the response
};
/** @brief Default constructor */
cHttp();
/** @brief Construct the HTTP client with the target host
** This is equivalent to calling SetHost(host, port).
** The port has a default value of 0, which means that the
** HTTP client will use the right port according to the
** protocol used (80 for HTTP, 443 for HTTPS). You should
** leave it like this unless you really need a port other
** than the standard one, or use an unknown protocol.
** @param host Web server to connect to
** @param port Port to use for connection */
cHttp(const std::string& host, unsigned short port = 0);
~cHttp();
/** @brief Set the target host
** This function just stores the host address and port, it
** doesn't actually connect to it until you send a request.
** The port has a default value of 0, which means that the
** HTTP client will use the right port according to the
** protocol used (80 for HTTP, 443 for HTTPS). You should
** leave it like this unless you really need a port other
** than the standard one, or use an unknown protocol.
** @param host Web server to connect to
** @param port Port to use for connection */
void SetHost(const std::string& host, unsigned short port = 0);
/** @brief Send a HTTP request and return the server's response.
** You must have a valid host before sending a request (see SetHost).
** Any missing mandatory header field in the request will be added
** with an appropriate value.
** Warning: this function waits for the server's response and may
** not return instantly; use a thread if you don't want to block your
** application, or use a timeout to limit the time to wait. A value
** of cTime::Zero means that the client will use the system defaut timeout
** (which is usually pretty long).
** @param request Request to send
** @param timeout Maximum time to wait
** @return Server's response */
Response SendRequest(const Request& request, cTime timeout = cTime::Zero);
/** Definition of the async callback response */
typedef cb::Callback3<void, const cHttp&, cHttp::Request&, cHttp::Response&> AsyncResponseCallback;
/** @brief Sends the request and creates a new thread, when got the response informs the result to the callback.
** This function does not lock the caller thread.
** @see SendRequest */
void SendAsyncRequest( AsyncResponseCallback cb, const cHttp::Request& request, cTime timeout = cTime::Zero );
/** @return The host address */
const cIpAddress& GetHost() const;
/** @return The host name */
const std::string& GetHostName() const;
/** @return The host port */
const unsigned short& GetPort() const;
private:
class cAsyncRequest : public cThread {
public:
cAsyncRequest( cHttp * http, AsyncResponseCallback cb, cHttp::Request request, cTime timeout );
void Run();
protected:
friend class cHttp;
cHttp * mHttp;
AsyncResponseCallback mCb;
cHttp::Request mRequest;
cTime mTimeout;
bool mRunning;
};
friend class cAsyncRequest;
tThreadLocalPtr<cTcpSocket> mConnection; ///< Connection to the host
cIpAddress mHost; ///< Web host address
std::string mHostName; ///< Web host name
unsigned short mPort; ///< Port used for connection with host
std::list<cAsyncRequest*> mThreads;
cMutex mThreadsMutex;
void RemoveOldThreads();
};
}}
#endif // EE_NETWORKCHTTP_HPP
/**
@class cHttp
@ingroup Network
cHttp is a very simple HTTP client that allows you
to communicate with a web server. You can retrieve
web pages, send data to an interactive resource,
download a remote file, etc.
The HTTP client is split into 3 classes:
@li cHttp::Request
@li cHttp::Response
@li cHttp
cHttp::Request builds the request that will be
sent to the server. A request is made of:
@li a method (what you want to do)
@li a target URI (usually the name of the web page or file)
@li one or more header fields (options that you can pass to the server)
@li an optional body (for POST requests)
cHttp::Response parse the response from the web server
and provides getters to read them. The response contains:
@li a status code
@li header fields (that may be answers to the ones that you requested)
@li a body, which contains the contents of the requested resource
cHttp provides a simple function, SendRequest, to send a
cHttp::Request and return the corresponding cHttp::Response
from the server.
Usage example:
@code
// Create a new HTTP client
cHttp http;
// We'll work on http://www.google.com
http.SetHost("http://www.google.com");
// Prepare a request to get the 'features.php' page
cHttp::Request request("features.php");
// Send the request
cHttp::Response response = http.SendRequest(request);
// Check the status code and display the result
cHttp::Response::Status status = response.GetStatus();
if (status == cHttp::Response::Ok)
{
std::cout << response.GetBody() << std::endl;
}
else
{
std::cout << "Error " << status << std::endl;
}
@endcode
*/