Шпаргалка по HTTP-библиотекам для С++

в 7:54, , рубрики: c++, http, Блог компании Инфопульс Украина, Программирование, метки: ,

К сожалению, в стандартной библиотеке языка С++ нет никаких средств для работы с протоколом HTTP. Возможно, в будущем появятся, но на данный момент каждый раз при необходимости дёрнуть какой-нибудь REST-сервис, пропарсить веб-страничку, написать простенького бота или краулера приходится задаваться вопросами «А какую же библиотеку взять, так чтобы побыстрее и попроще?». Иногда проект уже использует какой-то фреймворк (а иногда даже несколько) и тогда приходится вспоминать «А как же сделать HTTP-запрос имеющимися средствами?». Чтобы не путаться я решил написать для себя шпаргалку с примерами HTTP-запросов на С++ с применением разных библиотек. А самое удобное место для хранения подобных шпаргалок — Хабр: и сам не потеряешь, и другим может пригодиться.

Будут рассмотрены:

  • WinInet
  • Casablanca
  • Qt
  • POCO
  • wxWidgets
  • Boost.Asio
  • libcurl
  • neon
  • .NET (С++/CLI)
  • IXMLHTTPRequest
  • HappyHttp
  • cpp-netlib

WinInet

Сайт: http://msdn.microsoft.com/en-us/library/windows/desktop/aa385483(v=vs.85).aspx
Платформа: Windows 95 и выше

Пример использования

 #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/htmlnMySpecialHeder: 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);

Casablanca

Сайт: https://casablanca.codeplex.com
Платформа: все

Пример использования

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

Сайт: http://qt-project.org
Платформа: все

Пример использования

#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();
    }
}

POCO

Сайт: http://pocoproject.org
Платформа: все

Пример использования

#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

Сайт: http://www.wxwidgets.org
Платформа: все

Пример использования

#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

Сайт: http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio.html
Платформа: все

Пример использования

#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.txtn";
      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.0rn";
    request_stream << "Host: " << argv[1] << "rn";
    request_stream << "Accept: */*rn";
    request_stream << "Connection: closernrn";

    // 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, "rn");

    // 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 responsen";
      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, "rnrn");

    // 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

Сайт: http://curl.haxx.se/libcurl
Платформа: все

Пример использования

#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: %sn",
              curl_easy_strerror(res));
 
    /* always cleanup */ 
    curl_easy_cleanup(curl);
  }
  return 0;
}

neon

Сайт: http://www.webdav.org/neon
Платформа: все

Пример использования

#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 %dn", 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

Сайт: http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.aspx
Платформа: Windows XP и выше

Пример использования

#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

Сайт: http://msdn.microsoft.com/en-us/library/ms759148(v=vs.85).aspx
Платформа: Windows XP и выше

Пример использования

#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

Сайт: http://scumways.com/happyhttp/happyhttp.html
Платформа: все

Пример использования

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

Сайт: http://cpp-netlib.org
Платформа: все

Пример использования

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_);

Так, скажи уже в конце концов, что использовать!

Хотите проверенной годами классики — берите libcurl. Пишете приложение с визуальным интерфейсом — берите Qt. Хотите современного С++11 — берите Casablanca. Пишете под .NET — используйте стандартные средства платформы. Пишете что-то без интерфейса и кроме HTTP-клиента хотите вообще иметь разные удобные инструменты — Boost или POCO.

Автор: tangro

Источник

Поделиться

* - обязательные к заполнению поля