Implementing a
YouTube Request
The principle of a RESTful service is captured in the
following class function template:
template<class RequestOperator, class ResponseOperator>
YouTubeServiceState YouTubeService::service(
RequestOperator requestOperator, const typename RequestOperator::Input& input,
ResponseOperator responseOperator, typename ResponseOperator::Output& output)
{
const bool developerKeyValid = !m_DeveloperKey.empty();
const bool authenticationValid =
!RequestOperator::requiresAuthentication || !m_AuthenticationToken.empty();
ASSERT_M(authenticationValid, "Authentication required for this service.");
ASSERT_M(developerKeyValid, "Developer key required.");
YouTubeServiceState result = YouTubeServiceStates::Ok;
if (authenticationValid && developerKeyValid)
{
m_WriteBuffer.clear();
result = requestOperator(m_CurlHandle, input, m_ClientId, m_DeveloperKey,
m_AuthenticationToken);
if (result == YouTubeServiceStates::Ok)
{
result = responseOperator(m_WriteBuffer, output);
}
}
return result;
}
This is C++'s way of saying: “First check that the
application has a developer key. If the request requires authentication, also
check that the application is authenticated. Then, perform the HTTP request
implemented in the request operator and have the answer parsed by the
implementation of the response operator.”
The method is templated with request and response
operators because I wanted to separate the algorithm to perform a service from
the details of a specific request. This allows me to add more requests as I
need them by implementing additional operators with the required interface
signature (functors as they are called in C++):
struct Request
{
typedef ... Input; ///< Input type.
enum { requiresAuthentication = false };
/** Performs a request.*/
YouTubeServiceState operator()(
CURL* curlHandle, const Input& input,
const std::string& clientId, const std::string& developerKey,
const std::string& authenticationToken);
};
struct Response
{
typedef ... Output; ///< Output type.
/** Parses a response. */
YouTubeServiceState operator()(
const std::string& response, Output& output);
};
Whereas the request functor is responsible to call a
service based on the given input data, the response functor parses the result
into an output object. In my demo, I'm using libcurl for sending HTTP
requests and TinyXML for parsing the response.
The request functor can also require authentication if requiresAuthentication
is set to true
in the functor's declaration. Not all requests need to be authenticated, but
when sending videos on behalf of a YouTube user, you'll first need to send one
request with the user's name and password to get an authentication token.
In
another request, you include this token to actually perform the upload. This
sequence is illustrated in Figure 3.
The authentication request has to be sent via HTTPS so
that eavesdroppers can't spy on the YouTube password. For this reason, I have
configured libcurl to use OpenSSL, which takes care of verifying YouTube's
server and encrypting the authentication request.
|
I'm going to look into it.