362

Is there any way to easily make a HTTP request with C++? Specifically, I want to download the contents of a page (an API) and check the contents to see if it contains a 1 or a 0. Is it also possible to download the contents into a string?

3
  • 2
    No, there's currently no built-in support either via the language or the standard library for networking. However, there's a Networking TS N4370. I also VTC'd this question since it's attracting library recommendations.
    – user3920237
    Mar 11, 2015 at 9:22
  • How about BoostBeast? Sep 27, 2018 at 2:04
  • 3
    @user3920237 Fortunately, most of the people dont think destructively, to VTC the useful question. These are made for people's usage, let it be library.
    – T.Todua
    Sep 17, 2020 at 18:19

25 Answers 25

333
+100

I had the same problem. libcurl is really complete. There is a C++ wrapper curlpp that might interest you as you ask for a C++ library. neon is another interesting C library that also support WebDAV.

curlpp seems natural if you use C++. There are many examples provided in the source distribution. To get the content of an URL you do something like that (extracted from examples) :

// Edit : rewritten for cURLpp 0.7.3
// Note : namespace changed, was cURLpp in 0.7.2 ...

#include <curlpp/cURLpp.hpp>
#include <curlpp/Options.hpp>

// RAII cleanup

curlpp::Cleanup myCleanup;

// Send request and get a result.
// Here I use a shortcut to get it in a string stream ...

std::ostringstream os;
os << curlpp::options::Url(std::string("http://example.com"));

string asAskedInQuestion = os.str();

See the examples directory in curlpp source distribution, there is a lot of more complex cases, as well as a simple complete minimal one using curlpp.

my 2 cents ...

24
  • 2
    latest version seems to be broken under mac.. something is messed up with config.h when linked as library.
    – eugene
    Apr 8, 2011 at 0:59
  • 1
    Well, I could not compile the above. However, replacing os << myRequest.perform(); with myRequest.setOpt( new curlpp::options::WriteStream( &os ) ); myRequest.perform(); gave results. Make sure to not use http://example.com, this will return an empty page. Better use e.g. http://www.wikipedia.org.
    – Zane
    Nov 4, 2012 at 14:22
  • 9
    How do you build curlpp in MSVS? I can't get it working :(
    – mr5
    Nov 15, 2016 at 6:43
  • 2
    I don't agree with the latest edit by @ryan-sam. It clearly was the intention of the author to write "webdav" and not web development as the given library is made explicitly for "HTTP and WebDAV operations".
    – Bostrot
    Dec 5, 2018 at 11:20
  • 2
    @bostrot: Yep that what I meant. I reverted and added a link, I think people thought I wrote webdev. What a pity :)
    – neuro
    Dec 5, 2018 at 13:43
139

Windows code:

#include <string.h>
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#include <vector>
#include <locale>
#include <sstream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")




int main( void ){

WSADATA wsaData;
SOCKET Socket;
SOCKADDR_IN SockAddr;
int lineCount=0;
int rowCount=0;
struct hostent *host;
locale local;
char buffer[10000];
int i = 0 ;
int nDataLength;
string website_HTML;

// website url
string url = "www.google.com";

//HTTP GET
string get_http = "GET / HTTP/1.1\r\nHost: " + url + "\r\nConnection: close\r\n\r\n";


    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0){
        cout << "WSAStartup failed.\n";
        system("pause");
        //return 1;
    }

    Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    host = gethostbyname(url.c_str());

    SockAddr.sin_port=htons(80);
    SockAddr.sin_family=AF_INET;
    SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);

    if(connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr)) != 0){
        cout << "Could not connect";
        system("pause");
        //return 1;
    }

    // send GET / HTTP
    send(Socket,get_http.c_str(), strlen(get_http.c_str()),0 );

    // recieve html
    while ((nDataLength = recv(Socket,buffer,10000,0)) > 0){        
        int i = 0;
        while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r'){

            website_HTML+=buffer[i];
            i += 1;
        }               
    }

    closesocket(Socket);
    WSACleanup();

    // Display HTML source 
    cout<<website_HTML;

    // pause
    cout<<"\n\nPress ANY key to close.\n\n";
    cin.ignore(); cin.get(); 


 return 0;
}

Here is a much better implementation:

#include <windows.h>
#include <string>
#include <stdio.h>

using std::string;

#pragma comment(lib,"ws2_32.lib")


HINSTANCE hInst;
WSADATA wsaData;
void mParseUrl(char *mUrl, string &serverName, string &filepath, string &filename);
SOCKET connectToServer(char *szServerName, WORD portNum);
int getHeaderLength(char *content);
char *readUrl2(char *szUrl, long &bytesReturnedOut, char **headerOut);


int main()
{
    const int bufLen = 1024;
    char *szUrl = "http://stackoverflow.com";
    long fileSize;
    char *memBuffer, *headerBuffer;
    FILE *fp;

    memBuffer = headerBuffer = NULL;

    if ( WSAStartup(0x101, &wsaData) != 0)
        return -1;


    memBuffer = readUrl2(szUrl, fileSize, &headerBuffer);
    printf("returned from readUrl\n");
    printf("data returned:\n%s", memBuffer);
    if (fileSize != 0)
    {
        printf("Got some data\n");
        fp = fopen("downloaded.file", "wb");
        fwrite(memBuffer, 1, fileSize, fp);
        fclose(fp);
         delete(memBuffer);
        delete(headerBuffer);
    }

    WSACleanup();
    return 0;
}


void mParseUrl(char *mUrl, string &serverName, string &filepath, string &filename)
{
    string::size_type n;
    string url = mUrl;

    if (url.substr(0,7) == "http://")
        url.erase(0,7);

    if (url.substr(0,8) == "https://")
        url.erase(0,8);

    n = url.find('/');
    if (n != string::npos)
    {
        serverName = url.substr(0,n);
        filepath = url.substr(n);
        n = filepath.rfind('/');
        filename = filepath.substr(n+1);
    }

    else
    {
        serverName = url;
        filepath = "/";
        filename = "";
    }
}

SOCKET connectToServer(char *szServerName, WORD portNum)
{
    struct hostent *hp;
    unsigned int addr;
    struct sockaddr_in server;
    SOCKET conn;

    conn = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (conn == INVALID_SOCKET)
        return NULL;

    if(inet_addr(szServerName)==INADDR_NONE)
    {
        hp=gethostbyname(szServerName);
    }
    else
    {
        addr=inet_addr(szServerName);
        hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
    }

    if(hp==NULL)
    {
        closesocket(conn);
        return NULL;
    }

    server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
    server.sin_family=AF_INET;
    server.sin_port=htons(portNum);
    if(connect(conn,(struct sockaddr*)&server,sizeof(server)))
    {
        closesocket(conn);
        return NULL;
    }
    return conn;
}

int getHeaderLength(char *content)
{
    const char *srchStr1 = "\r\n\r\n", *srchStr2 = "\n\r\n\r";
    char *findPos;
    int ofset = -1;

    findPos = strstr(content, srchStr1);
    if (findPos != NULL)
    {
        ofset = findPos - content;
        ofset += strlen(srchStr1);
    }

    else
    {
        findPos = strstr(content, srchStr2);
        if (findPos != NULL)
        {
            ofset = findPos - content;
            ofset += strlen(srchStr2);
        }
    }
    return ofset;
}

char *readUrl2(char *szUrl, long &bytesReturnedOut, char **headerOut)
{
    const int bufSize = 512;
    char readBuffer[bufSize], sendBuffer[bufSize], tmpBuffer[bufSize];
    char *tmpResult=NULL, *result;
    SOCKET conn;
    string server, filepath, filename;
    long totalBytesRead, thisReadSize, headerLen;

    mParseUrl(szUrl, server, filepath, filename);

    ///////////// step 1, connect //////////////////////
    conn = connectToServer((char*)server.c_str(), 80);

    ///////////// step 2, send GET request /////////////
    sprintf(tmpBuffer, "GET %s HTTP/1.0", filepath.c_str());
    strcpy(sendBuffer, tmpBuffer);
    strcat(sendBuffer, "\r\n");
    sprintf(tmpBuffer, "Host: %s", server.c_str());
    strcat(sendBuffer, tmpBuffer);
    strcat(sendBuffer, "\r\n");
    strcat(sendBuffer, "\r\n");
    send(conn, sendBuffer, strlen(sendBuffer), 0);

//    SetWindowText(edit3Hwnd, sendBuffer);
    printf("Buffer being sent:\n%s", sendBuffer);

    ///////////// step 3 - get received bytes ////////////////
    // Receive until the peer closes the connection
    totalBytesRead = 0;
    while(1)
    {
        memset(readBuffer, 0, bufSize);
        thisReadSize = recv (conn, readBuffer, bufSize, 0);

        if ( thisReadSize <= 0 )
            break;

        tmpResult = (char*)realloc(tmpResult, thisReadSize+totalBytesRead);

        memcpy(tmpResult+totalBytesRead, readBuffer, thisReadSize);
        totalBytesRead += thisReadSize;
    }

    headerLen = getHeaderLength(tmpResult);
    long contenLen = totalBytesRead-headerLen;
    result = new char[contenLen+1];
    memcpy(result, tmpResult+headerLen, contenLen);
    result[contenLen] = 0x0;
    char *myTmp;

    myTmp = new char[headerLen+1];
    strncpy(myTmp, tmpResult, headerLen);
    myTmp[headerLen] = NULL;
    delete(tmpResult);
    *headerOut = myTmp;

    bytesReturnedOut = contenLen;
    closesocket(conn);
    return(result);
}
4
  • 20
    I couldn't get curlpp to work, so i used your code, Thanks Nov 25, 2012 at 11:04
  • 1
    Tried this code on a Windows Vista compiling with Dev-C++ Version 4.9.9.2. I gave me a bunch of errors when linking: [Linker error] undefined reference to `WSAStartup@8'
    – tzengia
    Apr 2, 2014 at 20:15
  • 9
    @Expanding-Dev Only MSVC (visual studio) understands "pragma comment". If you use anything else you must link "ws2_32.lib" manually (like any other library).
    – Navin
    May 15, 2014 at 18:37
  • What is supposed to be received (using recv) here? I am getting a lot of gibberish as output. Also, why did you put what you did in the send buffer (e.g. GET / HTTP/1.1.1/... etc)? How do I find out how to format what I send? Sep 8, 2014 at 0:27
65

Update 2020: I have a new answer that replaces this, now 8-years-old, one: https://stackoverflow.com/a/61177330/278976

On Linux, I tried cpp-netlib, libcurl, curlpp, urdl, boost::asio and considered Qt (but turned it down based on the license). All of these were either incomplete for this use, had sloppy interfaces, had poor documentation, were unmaintained or didn't support https.

Then, at the suggestion of https://stackoverflow.com/a/1012577/278976, I tried POCO. Wow, I wish I had seen this years ago. Here's an example of making an HTTP GET request with POCO:

https://stackoverflow.com/a/26026828/2817595

POCO is free, open source (boost license). And no, I don't have any affiliation with the company; I just really like their interfaces. Great job guys (and gals).

https://pocoproject.org/download.html

Hope this helps someone... it took me three days to try all of these libraries out.

6
  • 1
    Here's an additional example: github.com/pocoproject/poco/blob/develop/Net/samples/httpget/…
    – Homer6
    Jan 10, 2014 at 21:32
  • 2
    I just downloaded Poco on your suggestion. I would prefer something light that builds on STL and boost rather than rewrite much of it. Plus I'm not a fan of CppUnit and in particular hate tests running with the build, and don't expect to have to test their library as I build it.
    – CashCow
    Feb 25, 2014 at 21:30
  • 1
    It is a little big. However, you can disable building the tests and samples (or shared libraries) with configure (ie. --no-tests or --no-samples or --no-sharedlibs). See github.com/pocoproject/poco/blob/develop/configure
    – Homer6
    Feb 25, 2014 at 22:34
  • thank you for that. I want it anyway as I care about getting done the tasks I need to do. And I note they have JSON parsing there too which is good as I will need to do that after sending the HTTP request which is what I got the library for.
    – CashCow
    Feb 26, 2014 at 13:37
  • 1
    @Hack06 Thanks for the comment. I've provided a new answer that now replaces this one: stackoverflow.com/a/61177330/278976
    – Homer6
    Apr 12, 2020 at 19:58
64

There is a newer, less mature curl wrapper being developed called C++ Requests. Here's a simple GET request:

#include <iostream>
#include <cpr.h>

int main(int argc, char** argv) {
    auto response = cpr::Get(cpr::Url{"http://httpbin.org/get"});
    std::cout << response.text << std::endl;
}

It supports a wide variety of HTTP verbs and curl options. There's more usage documentation here.

Disclaimer: I'm the maintainer of this library.

6
  • 18
    I was at your CppCon 2015 lightning talk yesterday. Well done--both the talk and the library. I particularly like the "Curl for people" design philosophy.
    – U007D
    Sep 23, 2015 at 13:59
  • 1
    Hello, I just came across this post here, looking for easier C++ HTTP requests than the plain way. However, I am not really experienced with libraries, and I don't really know how to include this in my visual studio C++ project. Is there any explanation somewhere? I feel like it's not specific to the library, but rather that I don't really know what to do with what I have in front of me now in general. Jan 17, 2016 at 21:18
  • 2
    @Sossenbinder, if you can familiarize yourself with CMake, you can generate Visual Studio build files for this project using that. The appveyor configuration file contains a rough example of how to accomplish this.
    – huu
    Feb 4, 2016 at 3:41
  • 2
    Looks nice, but building is hell, so your lib is useless, I can't rely on package manager (need reliable way how to add deps externally) and need functional lib as soon as possible ...
    – dev1223
    Sep 15, 2016 at 12:43
  • that's how you do it. when you compare this to the 200 lines of the second most upvoted answer.......
    – v.oddou
    Apr 11, 2018 at 15:57
26

Updated answer for April, 2020:

I've had a lot of success, recently, with cpp-httplib (both as a client and a server). It's mature and its approximate, single-threaded RPS is around 6k.

On more of the bleeding edge, there's a really promising framework, cpv-framework, that can get around 180k RPS on two cores (and will scale well with the number of cores because it's based on the seastar framework, which powers the fastest DBs on the planet, scylladb).

However, cpv-framework is still relatively immature; so, for most uses, I highly recommend cpp-httplib.

There are some other libraries, like cpr, listed here: https://github.com/lefticus/cpp_weekly/issues/44#issuecomment-1231847130

This recommendation replaces my previous answer (8 years ago).

6
  • 2
    I really like the 1-file (5K-lines is ok) approach of cpp-httplib. Do you have an idea about its performance?
    – Hack06
    May 3, 2020 at 15:44
  • 3
    @Hack06 The rough benchmark was about 6000 requests per second (RPS).
    – Homer6
    May 10, 2020 at 18:40
  • This library(cpp-httplib) doesn't work below windows 8 ... The error messages are also not very helpful. Also I don't like the idea that I must need a ca-bundle.crt for ssl connection. For client side it's not necessary Mar 28, 2021 at 18:03
  • 2
    i feel crazy coming from c# where a simple http request is sooo easy, cpp-httplib like others, you have to jump through so many hoops just to get it to where you can make a simple https call Nov 4, 2021 at 18:25
  • @ImtiazShakilSiddique Well now, you don't need to care about "below Windows 8" :), the library seems to work fine for me on Windows 10 and 11.
    – user13944038
    Jul 28, 2022 at 15:40
19

Here is my minimal wrapper around cURL to be able just to fetch a webpage as a string. This is useful, for example, for unit testing. It is basically a RAII wrapper around the C code.

Install "libcurl" on your machine yum install libcurl libcurl-devel or equivalent.

Usage example:

CURLplusplus client;
string x = client.Get("http://google.com");
string y = client.Get("http://yahoo.com");

Class implementation:

#include <curl/curl.h>


class CURLplusplus
{
private:
    CURL* curl;
    stringstream ss;
    long http_code;
public:
    CURLplusplus()
            : curl(curl_easy_init())
    , http_code(0)
    {

    }
    ~CURLplusplus()
    {
        if (curl) curl_easy_cleanup(curl);
    }
    std::string Get(const std::string& url)
    {
        CURLcode res;
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);

        ss.str("");
        http_code = 0;
        res = curl_easy_perform(curl);
        if (res != CURLE_OK)
        {
            throw std::runtime_error(curl_easy_strerror(res));
        }
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
        return ss.str();
    }
    long GetHttpCode()
    {
        return http_code;
    }
private:
    static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
    {
        return static_cast<CURLplusplus*>(userp)->Write(buffer,size,nmemb);
    }
    size_t Write(void *buffer, size_t size, size_t nmemb)
    {
        ss.write((const char*)buffer,size*nmemb);
        return size*nmemb;
    }
};
0
19

As you want a C++ solution, you could use Qt. It has a QHttp class you can use.

You can check the docs:

http->setHost("qt.nokia.com");
http->get(QUrl::toPercentEncoding("/index.html"));

Qt also has a lot more to it that you could use in a common C++ app.

2
  • 6
    I think QHttp has been replaced with QNetworkAccessManager and related classes in Qt 4.6 and later versions.
    – juzzlin
    Mar 5, 2015 at 21:31
  • 4
    QNetworkAccessManager has been documented since Qt 4.4; and in Qt 4.8 says: QHttp - This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code. So I guess it is still available, if you ignore the deprecated warnings. Aug 5, 2015 at 23:55
17

You may want to check C++ REST SDK (codename "Casablanca"). http://msdn.microsoft.com/en-us/library/jj950081.aspx

With the C++ REST SDK, you can more easily connect to HTTP servers from your C++ app.

Usage example:

#include <iostream>
#include <cpprest/http_client.h>

using namespace web::http;                  // Common HTTP functionality
using namespace web::http::client;          // HTTP client features

int main(int argc, char** argv) {
    http_client client("http://httpbin.org/");

    http_response response;
    // ordinary `get` request
    response = client.request(methods::GET, "/get").get();
    std::cout << response.extract_string().get() << "\n";

    // working with json
    response = client.request(methods::GET, "/get").get();
    std::cout << "url: " << response.extract_json().get()[U("url")] << "\n";
}

The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design.

1
  • The project seems to be unmaintained now, be aware to use it in production. Its quality isn't that high to meet the standards used in production environments. Apr 22, 2021 at 3:54
15

libCURL is a pretty good option for you. Depending on what you need to do, the tutorial should tell you what you want, specifically for the easy handle. But, basically, you could do this just to see the source of a page:

CURL* c;
c = curl_easy_init();
curl_easy_setopt( c, CURL_URL, "www.google.com" );
curl_easy_perform( c );
curl_easy_cleanup( c );

I believe this will cause the result to be printed to stdout. If you want to handle it instead -- which, I assume, you do -- you need to set the CURL_WRITEFUNCTION. All of that is covered in the curl tutorial linked above.

1
  • libcurl is C, not C++
    – teambob
    Jun 6, 2023 at 8:41
14

With this answer I refer to the answer from Software_Developer. By rebuilding the code I found that some parts are deprecated (gethostbyname()) or do not provide error handling (creation of sockets, sending something) for an operation.

The following windows code is tested with Visual Studio 2013 and Windows 8.1 64-bit as well as Windows 7 64-bit. It will target an IPv4 TCP Connection with the Web Server of www.google.com.

#include <winsock2.h>
#include <WS2tcpip.h>
#include <windows.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
    int main (){
    // Initialize Dependencies to the Windows Socket.
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
        cout << "WSAStartup failed.\n";
        system("pause");
        return -1;
    }

    // We first prepare some "hints" for the "getaddrinfo" function
    // to tell it, that we are looking for a IPv4 TCP Connection.
    struct addrinfo hints;
    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;          // We are targeting IPv4
    hints.ai_protocol = IPPROTO_TCP;    // We are targeting TCP
    hints.ai_socktype = SOCK_STREAM;    // We are targeting TCP so its SOCK_STREAM

    // Aquiring of the IPv4 address of a host using the newer
    // "getaddrinfo" function which outdated "gethostbyname".
    // It will search for IPv4 addresses using the TCP-Protocol.
    struct addrinfo* targetAdressInfo = NULL;
    DWORD getAddrRes = getaddrinfo("www.google.com", NULL, &hints, &targetAdressInfo);
    if (getAddrRes != 0 || targetAdressInfo == NULL)
    {
        cout << "Could not resolve the Host Name" << endl;
        system("pause");
        WSACleanup();
        return -1;
    }

    // Create the Socket Address Informations, using IPv4
    // We dont have to take care of sin_zero, it is only used to extend the length of SOCKADDR_IN to the size of SOCKADDR
    SOCKADDR_IN sockAddr;
    sockAddr.sin_addr = ((struct sockaddr_in*) targetAdressInfo->ai_addr)->sin_addr;    // The IPv4 Address from the Address Resolution Result
    sockAddr.sin_family = AF_INET;  // IPv4
    sockAddr.sin_port = htons(80);  // HTTP Port: 80

    // We have to free the Address-Information from getaddrinfo again
    freeaddrinfo(targetAdressInfo);

    // Creation of a socket for the communication with the Web Server,
    // using IPv4 and the TCP-Protocol
    SOCKET webSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (webSocket == INVALID_SOCKET)
    {
        cout << "Creation of the Socket Failed" << endl;
        system("pause");
        WSACleanup();
        return -1;
    }

    // Establishing a connection to the web Socket
    cout << "Connecting...\n";
    if(connect(webSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr)) != 0)
    {
        cout << "Could not connect";
        system("pause");
        closesocket(webSocket);
        WSACleanup();
        return -1;
    }
    cout << "Connected.\n";

    // Sending a HTTP-GET-Request to the Web Server
    const char* httpRequest = "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n";
    int sentBytes = send(webSocket, httpRequest, strlen(httpRequest),0);
    if (sentBytes < strlen(httpRequest) || sentBytes == SOCKET_ERROR)
    {
        cout << "Could not send the request to the Server" << endl;
        system("pause");
        closesocket(webSocket);
        WSACleanup();
        return -1;
    }

    // Receiving and Displaying an answer from the Web Server
    char buffer[10000];
    ZeroMemory(buffer, sizeof(buffer));
    int dataLen;
    while ((dataLen = recv(webSocket, buffer, sizeof(buffer), 0) > 0))
    {
        int i = 0;
        while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r') {
            cout << buffer[i];
            i += 1;
        }
    }

    // Cleaning up Windows Socket Dependencies
    closesocket(webSocket);
    WSACleanup();

    system("pause");
    return 0;
}

References:

Deprecation of gethostbyname

Return Value of socket()

Return Value of send()

11

Note that this does not require libcurl, Windows.h, or WinSock! No compilation of libraries, no project configuration, etc. I have this code working in Visual Studio 2017 c++ on Windows 10:

#pragma comment(lib, "urlmon.lib")

#include <urlmon.h>
#include <sstream>

using namespace std;

...

IStream* stream;
//Also works with https URL's - unsure about the extent of SSL support though.
HRESULT result = URLOpenBlockingStream(0, "http://google.com", &stream, 0, 0);
if (result != 0)
{
    return 1;
}
char buffer[100];
unsigned long bytesRead;
stringstream ss;
stream->Read(buffer, 100, &bytesRead);
while (bytesRead > 0U)
{
    ss.write(buffer, (long long)bytesRead);
    stream->Read(buffer, 100, &bytesRead);
}
stream->Release();
string resultString = ss.str();

I just figured out how to do this, as I wanted a simple API access script, libraries like libcurl were causing me all kinds of problems (even when I followed the directions...), and WinSock is just too low-level and complicated.

I'm not quite sure about all of the IStream reading code (particularly the while condition - feel free to correct/improve), but hey, it works, hassle free! (It makes sense to me that, since I used a blocking (synchronous) call, this is fine, that bytesRead would always be > 0U until the stream (ISequentialStream?) is finished being read, but who knows.)

See also: URL Monikers and Asynchronous Pluggable Protocol Reference

12
  • 1
    It was edited out, but after a fair amount of experience in c++, I'm going to stand by my original claim that this is probably the easiest way you will ever be able to do this kind of thing in c++... (At least for now...)
    – Andrew
    Aug 23, 2018 at 22:37
  • I just tested URLOpenBlockingStream with a few URL's from badssl.com (pretty handy), and this operation will fail if the SSL certificate is bad. In every case I tested (just a few), the output of the above code will be an empty string (no stream data). So that's pretty nice.
    – Andrew
    Aug 24, 2018 at 1:04
  • I concur it's the easiesst, but how would you get the http response code?
    – Robin
    Aug 28, 2018 at 11:37
  • @Robin Ha! You wanted the message and the response code?! You know as well as I do, except that maybe if you look at the documentation and look in the more manual URL Monikers stuff you might find an answer. I seem to recall someone posting code online implementing URLOpenBlockingStream manually, which would allow for more configuration. Do let me know if you figure anything out!
    – Andrew
    Aug 28, 2018 at 17:33
  • 1
    @ysksilver Indeed, though that has been changing as time goes on. c++ has been drastically improving in its capabilities and built-in support for things. It's only a matter of time. For all I know they probably already have better support for such things built-in e.g. via. the std library. In my view, c++ though looked upon unfavorably by many for actual use, will last a long time and people will eventually start using it more and more.
    – Andrew
    Dec 4, 2023 at 6:26
8

C++ does not provide any way to do it directly. It would entirely depend on what platforms and libraries that you have.

At worst case, you can use the boost::asio library to establish a TCP connection, send the HTTP headers (RFC 2616), and parse the responses directly. Looking at your application needs, this is simple enough to do.

7
  • 1
    It does - now, at least. :) stackoverflow.com/a/51959694/1599699
    – Andrew
    Aug 22, 2018 at 4:08
  • 1
    @Andrew: If your "It does" addresses sybreon's "C++ does not provide any way to do it directly.", then the linked answer is not valid as it shows a way to do it using system specifics. Sep 9, 2019 at 13:23
  • @SebastianMach I mean, it does though. Just import a system-provided library and call a function, and it does the job for you. Compare that with all the other c++ options and it's either really difficult or using 3rd party code. I consider that pretty direct.
    – Andrew
    Sep 9, 2019 at 14:26
  • 2
    @Andrew: It's "system-provided" only on Windows. On other systems, it's different. And it doesn't have anything to do with "C++ does not provide any way to do it directly", which really means "The C++ standard does not". Sep 10, 2019 at 7:49
  • 1
    @Andrew: Uhm, no, he specifically stated "C++ does not provide", which is the same as "it's not standardized". If it is provided for C++ by Windows or Linux or OS/2, then it is provided for C++ by Windows or Linux or OS/2, and not by C++. What you mean is "Available for C++", which simply is not the same as "Available by C++". Sep 11, 2019 at 13:21
7

Here is some code that will work with no need to use any 3rd party library: First define your gateway, user, password and any other parameters you need to send to this specific server.

#define USERNAME "user"
#define PASSWORD "your password"
#define GATEWAY "your gateway"

Here is the code itself:

HINTERNET hOpenHandle, hResourceHandle, hConnectHandle;
const TCHAR* szHeaders = _T("Content-Type:application/json; charset=utf-8\r\n");


hOpenHandle = InternetOpen(_T("HTTPS"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (hOpenHandle == NULL)
{
    return false;
}


hConnectHandle = InternetConnect(hOpenHandle,
    GATEWAY,
    INTERNET_DEFAULT_HTTPS_PORT,
    NULL, NULL, INTERNET_SERVICE_HTTP,
    0, 1);

if (hConnectHandle == NULL)
{
    InternetCloseHandle(hOpenHandle);
    return false;
}


hResourceHandle = HttpOpenRequest(hConnectHandle,
    _T("POST"),
    GATEWAY,
    NULL, NULL, NULL, INTERNET_FLAG_SECURE | INTERNET_FLAG_KEEP_CONNECTION,
    1);

if (hResourceHandle == NULL)
{
    InternetCloseHandle(hOpenHandle);
    InternetCloseHandle(hConnectHandle);
    return false;
}

InternetSetOption(hResourceHandle, INTERNET_OPTION_USERNAME, (LPVOID)USERNAME, _tcslen(USERNAME));
InternetSetOption(hResourceHandle, INTERNET_OPTION_PASSWORD, (LPVOID)PASSWORD, _tcslen(PASSWORD));

std::string buf;
if (HttpSendRequest(hResourceHandle, szHeaders, 0, NULL, 0))
{
    while (true)
    {
        std::string part;
        DWORD size;
        if (!InternetQueryDataAvailable(hResourceHandle, &size, 0, 0))break;
        if (size == 0)break;
        part.resize(size);
        if (!InternetReadFile(hResourceHandle, &part[0], part.size(), &size))break;
        if (size == 0)break;
        part.resize(size);
        buf.append(part);
    }
}

if (!buf.empty())
{
    // Get data back
}

InternetCloseHandle(hResourceHandle);
InternetCloseHandle(hConnectHandle);
InternetCloseHandle(hOpenHandle);

That should work on a Win32 API environment.

Here is an example.

5
  • What should I put for gateway? There is not damn gateway... Win API is so bad. May 24, 2019 at 9:21
  • 1
    "Gateway" is just a generic word for the URI (en.wikipedia.org/wiki/Uniform_Resource_Identifier) provided by the service provider. This has nothing to do with Windows. May 24, 2019 at 11:12
  • Ah, thanks. I never heard that expression to be used for URL, so that kinda confused me. Thanks for clarification. May 24, 2019 at 12:12
  • Ok, I tested the code and your example does not add up. InternetConnect returns null when full URL is given, but returns non-null value when only the domain name is provided. So when/where do I use full URL to get the page that I want to download? May 24, 2019 at 12:52
  • Use InternetOpenUrl() instead of InternetConnect() if you want to use url
    – qpaycm
    Aug 9, 2019 at 20:03
5

C and C++ don't have a standard library for HTTP or even for socket connections. Over the years some portable libraries have been developed. The most widely used, as others have said, is libcurl.

Here is a list of alternatives to libcurl (coming from the libcurl's web site).

Also, for Linux, this is a simple HTTP client. You could implement your own simple HTTP GET client, but this won't work if there are authentication or redirects involved or if you need to work behind a proxy. For these cases you need a full-blown library like libcurl.

For source code with libcurl, this is the closest to what you want (Libcurl has many examples). Look at the main function. The html content will be copied to the buffer, after a successfully connection. Just replace parseHtml with your own function.

5

The HTTP protocol is very simple, so it is very simple to write a HTTP client. Here is one

https://github.com/pedro-vicente/lib_netsockets

It uses HTTP GET to retrieve a file from a web server, both server and file are command line parameters. The remote file is saved to a local copy.

Disclaimer: I am the author

check http.cc https://github.com/pedro-vicente/lib_netsockets/blob/master/src/http.cc

int http_client_t::get(const char *path_remote_file)
{
  char buf_request[1024];

  //construct request message using class input parameters
  sprintf(buf_request, "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", path_remote_file, m_server_ip.c_str());

  //send request, using built in tcp_client_t socket
  if (this->write_all(buf_request, (int)strlen(buf_request)) < 0)
  {
    return -1;
  }

EDIT: edited URL

1
  • Given URL is invalid.
    – Shravan40
    Dec 16, 2016 at 11:18
4

You can use embeddedRest library. It is lightweight header-only library. So it is easy to include it to your project and it does not require compilation cause there no .cpp files in it.

Request example from readme.md from repo:

#include "UrlRequest.hpp"

//...

UrlRequest request;
request.host("api.vk.com");
const auto countryId = 1;
const auto count = 1000;
request.uri("/method/database.getCities",{
    { "lang", "ru" },
    { "country_id", countryId },
    { "count", count },
    { "need_all", "1" },
});
request.addHeader("Content-Type: application/json");
auto response = std::move(request.perform());
if (response.statusCode() == 200) {
  cout << "status code = " << response.statusCode() << ", body = *" << response.body() << "*" << endl;
}else{
  cout << "status code = " << response.statusCode() << ", description = " << response.statusDescription() << endl;
}
7
  • 1
    Doesn't compile on Win32 :/
    – uhfocuz
    Aug 4, 2016 at 4:05
  • @uhfocuz the lib is written for iOS and Android. But I can help you compiling it for Win32. It is not too difficult
    – fnc12
    Aug 4, 2016 at 6:30
  • I love how light it is for my use, it compiled just fine on my Mac but on Windows the libs are different like you dont have netdb.h etc so i'd like some help yes
    – uhfocuz
    Aug 4, 2016 at 6:32
  • 1
    @uhfocuz all you have to do is add conditions like #ifdef _WIN32 and add there windows specific code. Look here - there is no much difference between unix sockets and windows sockets. I see two main differences: 1) call WSAStartup first and 2) use closesocket instead of close
    – fnc12
    Aug 4, 2016 at 6:38
  • @uhfocuz please create an issue in my repo - I shall add win32 support once I have enough time
    – fnc12
    Aug 4, 2016 at 13:53
4

Here is some (relatively) simple C++11 code that uses libCURL to download a URL's content into a std::vector<char>:

http_download.hh

# pragma once

#include <string>
#include <vector>

std::vector<char> download(std::string url, long* responseCode = nullptr);

http_download.cc

#include "http_download.hh"

#include <curl/curl.h>
#include <sstream>
#include <stdexcept>

using namespace std;

size_t callback(void* contents, size_t size, size_t nmemb, void* user)
{
  auto chunk = reinterpret_cast<char*>(contents);
  auto buffer = reinterpret_cast<vector<char>*>(user);

  size_t priorSize = buffer->size();
  size_t sizeIncrease = size * nmemb;

  buffer->resize(priorSize + sizeIncrease);
  std::copy(chunk, chunk + sizeIncrease, buffer->data() + priorSize);

  return sizeIncrease;
}

vector<char> download(string url, long* responseCode)
{
  vector<char> data;

  curl_global_init(CURL_GLOBAL_ALL);
  CURL* handle = curl_easy_init();
  curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
  curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, callback);
  curl_easy_setopt(handle, CURLOPT_WRITEDATA, &data);
  curl_easy_setopt(handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
  CURLcode result = curl_easy_perform(handle);
  if (responseCode != nullptr)
    curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, responseCode);
  curl_easy_cleanup(handle);
  curl_global_cleanup();

  if (result != CURLE_OK)
  {
    stringstream err;
    err << "Error downloading from URL \"" << url << "\": " << curl_easy_strerror(result);
    throw runtime_error(err.str());
  }

  return data;
}
4

Is there any way to easily make a HTTP request with C++? Specifically, I want to download the contents of a page (an API) and check the contents to see if it contains a 1 or a 0. Is it also possible to download the contents into a string?

First off ... I know this question is 12 years old. However . None of the answers provided gave an example that was "simple" without the need to build some external library

Below is the most simple solution I could come up with to retrieve and print the contents of a webpage.

Some Documentation on the functions utilized in the example below

// wininet lib :
https://learn.microsoft.com/en-us/windows/win32/api/wininet/
// wininet->internetopena();
https://learn.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-internetopena
// wininet->intenetopenurla();
https://learn.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-internetopenurla
// wininet->internetreadfile();
https://learn.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-internetreadfile
// wininet->internetclosehandle();
https://learn.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-internetclosehandle

#include <iostream>

#include <WinSock2.h>
#include <wininet.h>
#pragma comment(lib, "wininet.lib")

int main()
{
    //  ESTABLISH SOME LOOSE VARIABLES
    const int size = 4096;
    char buf[size];
    DWORD length;

    //  ESTABLISH CONNECTION TO THE INTERNET
    HINTERNET internet = InternetOpenA("Mozilla/5.0", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, NULL);
    if (!internet)
        ExitProcess(EXIT_FAILURE);  //  Failed to establish connection to internet, Exit

    //  ATTEMPT TO CONNECT TO WEBSITE "google.com"
    HINTERNET response = InternetOpenUrlA(internet, "http://www.google.com", NULL, NULL, NULL, NULL);
    if (!response) {
        //  CONNECTION TO "google.com" FAILED
        InternetCloseHandle(internet);  //  Close handle to internet
        ExitProcess(EXIT_FAILURE);
    }

    //  READ CONTENTS OF WEBPAGE IN HTML FORMAT
    if (!InternetReadFile(response, buf, size, &length)) {     
        //  FAILED TO READ CONTENTS OF WEBPAGE
        //  Close handles and Exit
        InternetCloseHandle(response);                      //  Close handle to response
        InternetCloseHandle(internet);                      //  Close handle to internet
        ExitProcess(EXIT_FAILURE);
    }
    
    //  CLOSE HANDLES AND OUTPUT CONTENTS OF WEBPAGE
    InternetCloseHandle(response);                      //  Close handle to response
    InternetCloseHandle(internet);                      //  Close handle to internet
    std::cout << buf << std::endl;
    return 0;
}
3

If you are looking for a HTTP client library in C++ that is supported in multiple platforms (Linux, Windows and Mac) for consuming Restful web services. You can have below options.

  1. QT Network Library - Allows the application to send network requests and receive replies
  2. C++ REST SDK - An emerging third-party HTTP library with PPL support
  3. Libcurl - It is probably one of the most used http lib in the native world.
2

Generally I'd recommend something cross-platform like cURL, POCO, or Qt. However, here is a Windows example!:

#include <atlbase.h>
#include <msxml6.h>
#include <comutil.h> // _bstr_t

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();
1

Although a little bit late. You may prefer https://github.com/Taymindis/backcurl .

It allows you to do http call on mobile c++ development. Suitable for Mobile game developement

bcl::init(); // init when using

bcl::execute<std::string>([&](bcl::Request *req) {
    bcl::setOpts(req, CURLOPT_URL , "http://www.google.com",
             CURLOPT_FOLLOWLOCATION, 1L,
             CURLOPT_WRITEFUNCTION, &bcl::writeContentCallback,
             CURLOPT_WRITEDATA, req->dataPtr,
             CURLOPT_USERAGENT, "libcurl-agent/1.0",
             CURLOPT_RANGE, "0-200000"
            );
}, [&](bcl::Response * resp) {
    std::string ret =  std::string(resp->getBody<std::string>()->c_str());
    printf("Sync === %s\n", ret.c_str());
});


bcl::cleanUp(); // clean up when no more using
1
  • Does it supports https? Apr 15, 2021 at 13:05
1

All the answers above are helpful. My answer just adds some additions:

  • Use boost beast, sync example, async example, ssl example
  • Use nghttp2, example, It supports SSL, HTTP/2
  • Use Facebook proxygen, this project comprises the core C++ HTTP abstractions used at Facebook. It's aimed at high performance and concurrency. I recommend installing it with vcpkg or you will struggle with the dependencies management. It supports SSL. It also support some advanced protocols:HTTP/1.1, SPDY/3, SPDY/3.1, HTTP/2, and HTTP/3

Both nghttp2 and proxygen are stable, can be considered to use in production.

0

You can use ACE in order to do so:

#include "ace/SOCK_Connector.h"

int main(int argc, ACE_TCHAR* argv[])
{
    //HTTP Request Header
    char* szRequest = "GET /video/nice.mp4 HTTP/1.1\r\nHost: example.com\r\n\r\n"; 
    int ilen = strlen(szRequest);

    //our buffer
    char output[16*1024];

    ACE_INET_Addr server (80, "example.com");
    ACE_SOCK_Stream peer;
    ACE_SOCK_Connector connector;
    int ires = connector.connect(peer, server);
    int sum = 0;
    peer.send(szRequest, ilen);
    while (true)
    {
        ACE_Time_Value timeout = ACE_Time_Value(15);
        int rc = peer.recv_n(output, 16*1024, &timeout);
        if (rc == -1)
        {
            break;
        }
        sum += rc;
    }
    peer.close();
    printf("Bytes transffered: %d",sum);

    return 0;
}
0

CppRest SDK by MS is what I just found and after about 1/2 hour had my first simple web service call working. Compared that to others mentioned here where I was not able to get anything even installed after hours of looking, I'd say it is pretty impressive

https://github.com/microsoft/cpprestsdk

Scroll down and click on Documentation, then click on Getting Started Tutorial and you will have a simple app running in no time.

1
  • quote from cpprestsdk: "cpprestsdk is in maintenance mode and we do not recommend its use in new projects. We will continue to fix critical bugs and address security issues." Jun 22, 2022 at 14:00
0

For the record cesanta's mongoose library seems to also support this: https://github.com/cesanta/mongoose/blob/6.17/examples/http_client/http_client.c

Not the answer you're looking for? Browse other questions tagged or ask your own question.