Unfortunately, standard C++ library provides no tools for working with HTTP protocol. Therefore, when we want to run some REST service, parse a webpage or write a simple bot or web crawler, we always wonder which library is better and faster in use. Sometimes a project already uses some framework (or even several). But how do we create an HTTP request using available facilities? Not to get confused each time performing such tasks, I decided to make a cheat sheet with examples of HTTP requests in C++ using different libraries. I guess Kukuruku is the best place for keeping such cheat sheets.

We’re going to take a look at the following libraries:


WinInet

Website: http://msdn.microsoft.com/en-us/library/windows/desktop/aa385483(v=vs.85).aspx
Platform: Windows 95 and later
Example
#include <tchar.h>
    #include <wininet.h>  

    /// ....

    HINTERNET hIntSession = 
      ::InternetOpen(_T("MyApp"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);

    HINTERNET hHttpSession = 
      InternetConnect(hIntSession, _T("api.twitter.com"), 80, 0, 0, INTERNET_SERVICE_HTTP, 0, NULL);

    HINTERNET hHttpRequest = HttpOpenRequest(
      hHttpSession, 
      _T("GET"), 
      _T("1/statuses/user_timeline.xml?screen_name=twitterapi"),
      0, 0, 0, INTERNET_FLAG_RELOAD, 0);

    TCHAR* szHeaders = _T("Content-Type: text/html\nMySpecialHeder: whatever");
    CHAR szReq[1024] = "";
    if( !HttpSendRequest(hHttpRequest, szHeaders, _tcslen(szHeaders), szReq, strlen(szReq))) {
      DWORD dwErr = GetLastError();
      /// handle error
    }

    CHAR szBuffer[1025];
    DWORD dwRead=0;
    while(::InternetReadFile(hHttpRequest, szBuffer, sizeof(szBuffer)-1, &dwRead) && dwRead) {
      szBuffer[dwRead] = 0;
      OutputDebugStringA(szBuffer);
      dwRead=0;
    }

    ::InternetCloseHandle(hHttpRequest);
    ::InternetCloseHandle(hHttpSession);
    ::InternetCloseHandle(hIntSession);



WinHttp

Website: http://msdn.microsoft.com/en-us/library/windows/desktop/aa382925(v=vs.85).aspx
Platform: Windows 2000 and later
Example
DWORD dwSize = 0;
  DWORD dwDownloaded = 0;
  LPSTR pszOutBuffer;
  BOOL  bResults = FALSE;
  HINTERNET  hSession = NULL, 
             hConnect = NULL,
             hRequest = NULL;

  // Use WinHttpOpen to obtain a session handle.
  hSession = WinHttpOpen( L"WinHTTP Example/1.0",  
                          WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
                          WINHTTP_NO_PROXY_NAME, 
                          WINHTTP_NO_PROXY_BYPASS, 0 );

  // Specify an HTTP server.
  if( hSession )
    hConnect = WinHttpConnect( hSession, L"www.microsoft.com",
                               INTERNET_DEFAULT_HTTPS_PORT, 0 );

  // Create an HTTP request handle.
  if( hConnect )
    hRequest = WinHttpOpenRequest( hConnect, L"GET", NULL,
                                   NULL, WINHTTP_NO_REFERER, 
                                   WINHTTP_DEFAULT_ACCEPT_TYPES, 
                                   WINHTTP_FLAG_SECURE );

  // Send a request.
  if( hRequest )
    bResults = WinHttpSendRequest( hRequest,
                                   WINHTTP_NO_ADDITIONAL_HEADERS, 0,
                                   WINHTTP_NO_REQUEST_DATA, 0, 
                                   0, 0 );


  // End the request.
  if( bResults )
    bResults = WinHttpReceiveResponse( hRequest, NULL );

  // Keep checking for data until there is nothing left.
  if( bResults )
  {
    do 
    {
      // Check for available data.
      dwSize = 0;
      if( !WinHttpQueryDataAvailable( hRequest, &dwSize ) )
        printf( "Error %u in WinHttpQueryDataAvailable.\n",
                GetLastError( ) );

      // Allocate space for the buffer.
      pszOutBuffer = new char[dwSize+1];
      if( !pszOutBuffer )
      {
        printf( "Out of memory\n" );
        dwSize=0;
      }
      else
      {
        // Read the data.
        ZeroMemory( pszOutBuffer, dwSize+1 );

        if( !WinHttpReadData( hRequest, (LPVOID)pszOutBuffer, 
                              dwSize, &dwDownloaded ) )
          printf( "Error %u in WinHttpReadData.\n", GetLastError( ) );
        else
          printf( "%s", pszOutBuffer );

        // Free the memory allocated to the buffer.
        delete [] pszOutBuffer;
      }
    } while( dwSize > 0 );
  }


  // Report any errors.
  if( !bResults )
    printf( "Error %d has occurred.\n", GetLastError( ) );

  // Close any open handles.
  if( hRequest ) WinHttpCloseHandle( hRequest );
  if( hConnect ) WinHttpCloseHandle( hConnect );
  if( hSession ) WinHttpCloseHandle( hSession );



Casablanca

Website: https://casablanca.codeplex.com
Platform: all
Example
http_client client(L"http://www.myhttpserver.com");
http_request request(methods::GET);
client.request(request).then([](http_response response)
    {
        // Perform actions here to inspect the HTTP response...
        if(response.status_code() == status_codes::OK)
        {
        }
    });



Qt

Website: http://qt-project.org
Platform: all
Example use for Qt 4.x (already outdated)
#include "handler.h"
 
Handler::Handler(QObject *parent) :QObject(parent)  {
    http = new QHttp(this);
    connect(http, SIGNAL(stateChanged(int)), this, SLOT(stateChanged(int)));
    connect(http, SIGNAL(responseHeaderReceived(QHttpResponseHeader)), this, SLOT(responseHeaderReceived(QHttpResponseHeader)));
    connect(http, SIGNAL(requestFinished(int,bool)), this, SLOT(requestFinished(int,bool)));
}
 
void Handler::doHttp()  {
    http->setHost("google.com");
    http->get("/");
}
 
void Handler::stateChanged(int state)   {
    switch(state)   {
    case 0:
        qDebug() << "Unconnected";
        break;
    case 1:
        qDebug() << "Host Lookup";
        break;
    case 2:
        qDebug() << "Connecting";
        break;
    case 3:
        qDebug() << "Sending";
        break;
    case 4:
        qDebug() << "Reading";
        break;
    case 5:
        qDebug() << "Connect";
        break;
    case 6:
        qDebug() << "Closing";
        break;
    }
}
 
void Handler::responseHeaderReceived(const QHttpResponseHeader &resp)   {
    qDebug() << "Size : " << resp.contentLength();
    qDebug() << "Type : " << resp.contentType();
    qDebug() << "Status Code : " << resp.statusCode();
}
 
void Handler::requestFinished(int id, bool error)   {
    qDebug() << "Request Id : " << id;
    if(error)   {
        qDebug() << "Error";
    }   else    {
        qDebug() << http->readAll();
    }
}


Example use for Qt 5.x
void Downloader::doDownload()
{
    manager = new QNetworkAccessManager(this);

    connect(manager, SIGNAL(finished(QNetworkReply*)),
            this, SLOT(replyFinished(QNetworkReply*)));

    manager->get(QNetworkRequest(QUrl("http://google.com")));
}

void Downloader::replyFinished (QNetworkReply *reply)
{
   if(reply->error() != QNetworkReply::NoError)
    {
        qDebug() << "ERROR!";
        qDebug() << reply->errorString();
    }
    else
    {
        qDebug() << reply->header(QNetworkRequest::ContentTypeHeader).toString();
        qDebug() << reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().toString();
        qDebug() << reply->header(QNetworkRequest::ContentLengthHeader).toULongLong();
        qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
        qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();

        QFile file("C:/Qt/Dummy/downloaded.txt");
        if(file.open(QFile::Append))
        {
            file.write(reply->readAll());
        }
    }

    reply->deleteLater();
}



POCO

Website: http://pocoproject.org
Platform: all
Example
#include <Poco/Net/HTTPClientSession.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/StreamCopier.h>
#include <Poco/Path.h>
#include <Poco/URI.h>
#include <Poco/Exception.h>
#include <iostream>
#include <string>

using namespace Poco::Net;
using namespace Poco;
using namespace std;

int main(int argc, char **argv)
{
  if (argc != 2)
  {
    cout << "Usage: " << argv[0] << " <uri>" << endl;
    cout << "       fetches the resource identified by <uri> and print it" << endl;
    return -1;
  }

  try
  {
    // prepare session
    URI uri(argv[1]);
    HTTPClientSession session(uri.getHost(), uri.getPort());

    // prepare path
    string path(uri.getPathAndQuery());
    if (path.empty()) path = "/";

    // send request
    HTTPRequest req(HTTPRequest::HTTP_GET, path, HTTPMessage::HTTP_1_1);
    session.sendRequest(req);

    // get response
    HTTPResponse res;
    cout << res.getStatus() << " " << res.getReason() << endl;

    // print response
    istream &is = session.receiveResponse(res);
    StreamCopier::copyStream(is, cout);
  }
  catch (Exception &ex)
  {
    cerr << ex.displayText() << endl;
    return -1;
  }

  return 0;
}



wxWidgets

Website: http://www.wxwidgets.org
Platform: all
Example
#include <wx/sstream.h>
#include <wx/protocol/http.h>
 
wxHTTP get;
get.SetHeader(_T("Content-type"), _T("text/html; charset=utf-8"));
get.SetTimeout(10); // 10 seconds of timeout instead of 10 minutes ...
 
// this will wait until the user connects to the internet. It is important in case of dialup (or ADSL) connections
while (!get.Connect(_T("www.google.com")))  // only the server, no pages here yet ...
    wxSleep(5);
 
wxApp::IsMainLoopRunning(); // should return true
 
// use _T("/") for index.html, index.php, default.asp, etc.
wxInputStream *httpStream = get.GetInputStream(_T("/intl/en/about.html"));
 
// wxLogVerbose( wxString(_T(" GetInputStream: ")) << get.GetResponse() << _T("-") << ((resStream)? _T("OK ") : _T("FAILURE ")) << get.GetError() );
 
if (get.GetError() == wxPROTO_NOERR)
{
    wxString res;
    wxStringOutputStream out_stream(&res);
    httpStream->Read(out_stream);
 
    wxMessageBox(res);
    // wxLogVerbose( wxString(_T(" returned document length: ")) << res.Length() );
}
else
{
    wxMessageBox(_T("Unable to connect!"));
}
 
wxDELETE(httpStream);
get.Close();



Boost.Asio

Website: http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio.html
Platform: all
Example
#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 3)
    {
      std::cout << "Usage: sync_client <server> <path>\n";
      std::cout << "Example:\n";
      std::cout << "  sync_client www.boost.org /LICENSE_1_0.txt\n";
      return 1;
    }

    boost::asio::io_service io_service;

    // Get a list of endpoints corresponding to the server name.
    tcp::resolver resolver(io_service);
    tcp::resolver::query query(argv[1], "http");
    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);

    // Try each endpoint until we successfully establish a connection.
    tcp::socket socket(io_service);
    boost::asio::connect(socket, endpoint_iterator);

    // Form the request. We specify the "Connection: close" header so that the
    // server will close the socket after transmitting the response. This will
    // allow us to treat all data up until the EOF as the content.
    boost::asio::streambuf request;
    std::ostream request_stream(&request);
    request_stream << "GET " << argv[2] << " HTTP/1.0\r\n";
    request_stream << "Host: " << argv[1] << "\r\n";
    request_stream << "Accept: */*\r\n";
    request_stream << "Connection: close\r\n\r\n";

    // Send the request.
    boost::asio::write(socket, request);

    // 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::streambuf response;
    boost::asio::read_until(socket, response, "\r\n");

    // Check that response is OK.
    std::istream response_stream(&response);
    std::string http_version;
    response_stream >> http_version;
    unsigned int status_code;
    response_stream >> status_code;
    std::string status_message;
    std::getline(response_stream, status_message);
    if (!response_stream || http_version.substr(0, 5) != "HTTP/")
    {
      std::cout << "Invalid response\n";
      return 1;
    }
    if (status_code != 200)
    {
      std::cout << "Response returned with status code " << status_code << "\n";
      return 1;
    }

    // Read the response headers, which are terminated by a blank line.
    boost::asio::read_until(socket, response, "\r\n\r\n");

    // Process the response headers.
    std::string header;
    while (std::getline(response_stream, header) && header != "\r")
      std::cout << header << "\n";
    std::cout << "\n";

    // Write whatever content we already have to output.
    if (response.size() > 0)
      std::cout << &response;

    // Read until EOF, writing data to output as we go.
    boost::system::error_code error;
    while (boost::asio::read(socket, response,
          boost::asio::transfer_at_least(1), error))
      std::cout << &response;
    if (error != boost::asio::error::eof)
      throw boost::system::system_error(error);
  }
  catch (std::exception& e)
  {
    std::cout << "Exception: " << e.what() << "\n";
  }

  return 0;
}



libcurl

Website: http://curl.haxx.se/libcurl
Platform: all
Example
#include <stdio.h>
#include <curl/curl.h>
 
int main(void)
{
  CURL *curl;
  CURLcode res;
 
  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
    /* example.com is redirected, so we tell libcurl to follow redirection */ 
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
 
    /* Perform the request, res will get the return code */ 
    res = curl_easy_perform(curl);
    /* Check for errors */ 
    if(res != CURLE_OK)
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));
 
    /* always cleanup */ 
    curl_easy_cleanup(curl);
  }
  return 0;
}



neon

Website: http://www.webdav.org/neon
Platform: all
Example
#include <ne_session.h>
#include <ne_request.h>
#include <ne_utils.h>
#include <ne_uri.h>

int httpResponseReader(void *userdata, const char *buf, size_t len)
{
    string *str = (string *)userdata;
    str->append(buf, len);
    return 0;
}
 
int do_get(string host)
{
    ne_session *sess;
    ne_request *req;
    string response;
 
    ne_sock_init();
 
    sess = ne_session_create("http", host.c_str(), 80);
    ne_set_useragent(sess, "MyAgent/1.0");
 
    req = ne_request_create(sess, "GET", "/SomeURL/method?with=parameter&value=data");
    // if accepting only 2xx codes, use "ne_accept_2xx"
    ne_add_response_body_reader(req, ne_accept_always, httpResponseReader, &response);
 
    int result = ne_request_dispatch(req);
    int status = ne_get_status(req)->code;
 
    ne_request_destroy(req);
 
    string errorMessage = ne_get_error(sess);
    ne_session_destroy(sess);
 
    printf("result %d, status %d\n", result, status);
    cout << response << "\n";
 
    switch (result) {
    case NE_OK:
        break;
    case NE_CONNECT:
        throw ConnectionError(errorMessage);
    case NE_TIMEOUT:
        throw TimeOutError(errorMessage);
    case NE_AUTH:
        throw AuthenticationError(errorMessage);
    default:
        throw AnotherWebError(errorMessage);
    }
 
    return 0;
}



.NET

Website: http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.aspx
Platform: Windows XP and later
Example
#using <System.dll>

using namespace System;
using namespace System::Net;
using namespace System::Text;
using namespace System::IO;

// Specify the URL to receive the request.
int main()
{
   array<String^>^args = Environment::GetCommandLineArgs();
   HttpWebRequest^ request = dynamic_cast<HttpWebRequest^>(WebRequest::Create( args[ 1 ] ));

   // Set some reasonable limits on resources used by this request
   request->MaximumAutomaticRedirections = 4;
   request->MaximumResponseHeadersLength = 4;

   // Set credentials to use for this request.
   request->Credentials = CredentialCache::DefaultCredentials;
   HttpWebResponse^ response = dynamic_cast<HttpWebResponse^>(request->GetResponse());
   Console::WriteLine( "Content length is {0}", response->ContentLength );
   Console::WriteLine( "Content type is {0}", response->ContentType );

   // Get the stream associated with the response.
   Stream^ receiveStream = response->GetResponseStream();

   // Pipes the stream to a higher level stream reader with the required encoding format. 
   StreamReader^ readStream = gcnew StreamReader( receiveStream,Encoding::UTF8 );
   Console::WriteLine( "Response stream received." );
   Console::WriteLine( readStream->ReadToEnd() );
   response->Close();
   readStream->Close();
}



IXMLHTTPRequest

Website: http://msdn.microsoft.com/en-us/library/ms759148(v=vs.85).aspx
Platform: Windows XP and later
Example
#include <atlbase.h>
#include <msxml6.h>

HRESULT hr;
CComPtr<IXMLHTTPRequest> request;

hr = request.CoCreateInstance(CLSID_XMLHTTP60);
hr = request->open(
    _bstr_t("GET"),
    _bstr_t("https://www.google.com/images/srpr/logo11w.png"),
    _variant_t(VARIANT_FALSE),
    _variant_t(),
    _variant_t());
hr = request->send(_variant_t());

// get status - 200 if succuss
long status;
hr = request->get_status(&status);

// load image data (if url points to an image)
VARIANT responseVariant;
hr = request->get_responseStream(&responseVariant);
IStream* stream = (IStream*)responseVariant.punkVal;
CImage *image = new CImage();
image->Load(stream);
stream->Release();



HappyHttp

Website: http://scumways.com/happyhttp/happyhttp.html
Platform: all
Example
static int count=0;

// invoked when response headers have been received
void OnBegin( const happyhttp::Response* r, void* userdata )
{
    printf( "BEGIN (%d %s)\n", r->getstatus(), r->getreason() );
    count = 0;
}

// invoked to process response body data (may be called multiple times)
void OnData( const happyhttp::Response* r, void* userdata, const unsigned char* data, int n )
{
    fwrite( data,1,n, stdout );
    count += n;
}

// invoked when response is complete
void OnComplete( const happyhttp::Response* r, void* userdata )
{
    printf( "COMPLETE (%d bytes)\n", count );
}


void TestGET()
{
    happyhttp::Connection conn( "www.scumways.com", 80 );
    conn.setcallbacks( OnBegin, OnData, OnComplete, 0 );

    conn.request( "GET", "/happyhttp/test.php" );

    while( conn.outstanding() )
        conn.pump();
}



cpp-netlib

Website: http://cpp-netlib.org
Platform: all
Example
using namespace boost::network;
using namespace boost::network::http;

client::request request_("http://127.0.0.1:8000/");
request_ << header("Connection", "close");
client client_;
client::response response_ = client_.get(request_);
std::string body_ = body(response_);



It’s High Time to Find Out the Perfect One!

If you want to use the trusted classics – use libcurl. If you’re writing an application with a visual interface – use Qt. For those writing in С++11, Casablanca is the best choice. If you’re writing under .NET, then you should use standard platform facilities. But if you’re writing something with no interface and besides an HTTP client you want to have various handy tools as well, use Boost or POCO.

Write your own articles at Kukuruku Hub

3 comments

Corvusoft
I see Restbed didn't make the cut.

Restbed brings asynchronous RESTful service to C++11 applications. I'm full throttle on the development and hope to have HTTP 2 functionality towards the end of the year.

I'd love to hear any feed back regarding the project.
Anton K2
Restbed has a weird dual license.
Corvusoft
You've the choice of AGPL or Commercial License (CPL), fairly standard setup.

Read Next