How do you make a HTTP request with C++?
Asked Answered
F

25

363

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?

Fuselage answered 18/6, 2009 at 7:52 Comment(3)
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.Practiced
How about BoostBeast?Unpolite
@Practiced Fortunately, most of the people dont think destructively, to VTC the useful question. These are made for people's usage, let it be library.Vibrate
A
334

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

Acidhead answered 10/7, 2009 at 21:29 Comment(24)
latest version seems to be broken under mac.. something is messed up with config.h when linked as library.Wallet
could you please provide a sample project? I tried your given code, but I am unable to execute it.Bedfast
@Zain: look at the curlpp examples like : curlpp.org/index.php/examples/47-example-00Acidhead
Hi neuro, what's the myCleanup object for? I don't see it used in any way? Thanks :)Earpiece
It is using the RAII paradigm. When destroyed at the end of the scope, it automagically clean-up everything created by the library. See en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization for RAIIAcidhead
Please add the headers and namespace necessary to execute this code.Hatpin
Could you please specify what you mean by 'Url'? I get the "use of undeclared identifier 'Url'" error.Surly
@Arman: I checked the code, it should work with 0.7.3 (last revision I know). You can check the examples given in the source archive.Acidhead
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.Omar
@Zane: yep. I did not notice that the API has slightly changed in 0.7.3. I've tested the code it compiles and runs with 0.7.3Acidhead
I can't say I'd ever recommend curlpp. With a bit of unique_ptr you can make things better already. I've ended up bridging libcurl with Boost Asio (which is testament to libcurl's maturity + flexibility). I recommend libcurl for features and maturityYasmin
@sehe: well 6 years ago using curlpp knowing that libcurl is a great lib seemed the way to go. Now it seems not to be maintened anymore... Should I have to make a choice now I will probably go for my own wrapper around libcurl ...Acidhead
what if my request returns an XML file? how can I store response into a .xml file?Memling
@ubisum: You will get the result as a stream or as a string you can save in a file using standard c++ methods or any library.Acidhead
thanks for answer. I tried your code above, but compiler doesn't find the second included header of curl. i downloaded curl from repository, ran configure, make and make install. then, I modified my CMakeLists with row target_link_libraries( squares ${OpenCV_LIBS} ${CURL_LIBS}), which should link curl libraries. anyway, that doesn't solve my problem. any suggestion? I'm programming in C++ on Mac Os Sierra. thanksMemling
How do you build curlpp in MSVS? I can't get it working :(Republican
libCurl will make your application depend on newest VC++ version installed.Grunter
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".Presa
@bostrot: Yep that what I meant. I reverted and added a link, I think people thought I wrote webdev. What a pity :)Acidhead
curl.haxx.se/libcurl/c/simplepost.html works with both c and c++ just need a working makefile to get it up a running #56048448Citrange
@Greg: Yes. The question was C++ targeted and the OP wants a C++ string. But of course you can use libcurl directly as stated in my answer.Acidhead
Hi/ I'm wondering if what you are saying is possible to do this? I tried but failed. #51899231Daniels
I can spend a day on setting up a curlpp, or I can just use HTTPRequest library: github.com/elnormous/HTTPRequest . For curlpp, they wrote a whole-аss book on just how to compile and use it: github.com/jpbarrette/curlpp/blob/master/doc/guide.pdfCallus
@KullaGGin, well 13 years ago, that was not an option. I haven't tested HTTPRequest but nice to know about it :)Acidhead
D
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);
}
Dancette answered 11/9, 2012 at 16:52 Comment(4)
I couldn't get curlpp to work, so i used your code, ThanksUrtication
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'Spitler
@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).Distort
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?Resolvent
H
65

Update 2020: I have a new answer that replaces this, now 8-years-old, one: https://mcmap.net/q/92498/-how-do-you-make-a-http-request-with-c

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://mcmap.net/q/92498/-how-do-you-make-a-http-request-with-c, 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://mcmap.net/q/93701/-c-http-request-with-poco

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.

Henrieta answered 24/7, 2012 at 4:39 Comment(6)
Here's an additional example: github.com/pocoproject/poco/blob/develop/Net/samples/httpget/…Henrieta
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.Mieshamiett
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/configureHenrieta
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.Mieshamiett
it was a while ago, but this is just to let you know that none of those your links are working now, even the github repo looks like removed...Sendoff
@Sendoff Thanks for the comment. I've provided a new answer that now replaces this one: https://mcmap.net/q/92498/-how-do-you-make-a-http-request-with-cHenrieta
H
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.

Hyperaesthesia answered 1/9, 2015 at 15:13 Comment(6)
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.Quindecennial
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.Statutory
@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.Hyperaesthesia
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 ...December
that's how you do it. when you compare this to the 200 lines of the second most upvoted answer.......Wabble
Honestly, you kept talking at that CppCon "about people", "for people", "simple" and sure it sounds so yet few people including me asked to good documentation, maybe even better video on how to start with it, how to install it, how to write simple REST calls and that got never provided. Their documentation page is not helpful at all. So, sorry but all the talk was for nothingChessa
H
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).

Henrieta answered 12/4, 2020 at 19:56 Comment(6)
I really like the 1-file (5K-lines is ok) approach of cpp-httplib. Do you have an idea about its performance?Sendoff
@Sendoff The rough benchmark was about 6000 requests per second (RPS).Henrieta
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 necessaryPall
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 callFoulmouthed
@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.Tangent
You should delete your old answer if that replaces it. AFAIK you will not lose reputation from deleting it.Arabia
S
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.

Skilken answered 13/7, 2009 at 14:57 Comment(2)
I think QHttp has been replaced with QNetworkAccessManager and related classes in Qt 4.6 and later versions.Towandatoward
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.Casework
C
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;
    }
};
Clynes answered 19/11, 2014 at 20:49 Comment(0)
R
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.

Richardricharda answered 14/1, 2014 at 2:1 Comment(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.Genevivegenevra
T
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.

Trim answered 18/6, 2009 at 15:57 Comment(1)
libcurl is C, not C++Fuzee
D
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()

Duumvir answered 6/2, 2015 at 16:6 Comment(0)
C
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

Commonweal answered 22/8, 2018 at 4:3 Comment(12)
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...)Commonweal
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.Commonweal
I concur it's the easiesst, but how would you get the http response code?Sholem
@Sholem 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!Commonweal
There's also this I just found: learn.microsoft.com/en-us/windows/desktop/WinInet/… Have no idea if it's any good.Commonweal
Hi very cool I'm just wondering why is the length of the buffer only 100, does that mean it only reads 100 bytes? I'm confusedUnimproved
@bluejayke You're probably new to programming. Typically when a buffer is used, at least in this type of context, it's to retrieve a fixed size of data at a time. Notice then that if there are any bytes read, and while there continue to be any bytes read, it writes those bytes into the stream and continues reading up to 100 more bytes. Then, the .str() call at the end converts the stream into a single string, which is typically how we want to deal with it in our own code once finished.Commonweal
@bluejayke en.wikipedia.org/wiki/Data_buffer "Buffers are typically used when there is a difference between the rate at which data is received and the rate at which it can be processed, or in the case that these rates are variable, for example in a printer spooler or in online video streaming."Commonweal
I assume you meant stream->Release(); not stream.Release();Biography
@Biography Yeah, I think some compilers will actually treat it as such? Which I find not preferable, but whatever.Commonweal
It is shame of C++ that it is so hard to get http response which is one line of code in C#.Influent
@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.Commonweal
S
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.

Stine answered 18/6, 2009 at 8:13 Comment(7)
It does - now, at least. :) https://mcmap.net/q/92498/-how-do-you-make-a-http-request-with-cCommonweal
@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.Kowalewski
@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.Commonweal
@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".Kowalewski
@SebastianMach Yeah but that's subjective too because c++ also runs on phones tablets microcontrollers etc. If not every single device or O.S. easily supports some functionality in c++ do we call it not directly provided by c++? O.P. didn't say "c++ standard" he just said c++. These answers are providing Linux and Windows solutions, because that's typically what you would use for something like this. So sure, it's not a Linux solution, but yeah, it is provided by a major O.S. directly.Commonweal
@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++".Kowalewski
So the distinction is in what we consider to be contained by "c++".Commonweal
H
8

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.

Hooded answered 26/10, 2018 at 12:22 Comment(5)
What should I put for gateway? There is not damn gateway... Win API is so bad.Gravamen
"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.Hooded
Ah, thanks. I never heard that expression to be used for URL, so that kinda confused me. Thanks for clarification.Gravamen
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?Gravamen
Use InternetOpenUrl() instead of InternetConnect() if you want to use urlHarmonicon
P
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.

Polariscope answered 10/7, 2009 at 9:51 Comment(0)
D
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

Diphtheria answered 15/8, 2016 at 5:20 Comment(1)
Given URL is invalid.Groat
A
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;
}
Append answered 11/11, 2014 at 17:45 Comment(0)
I
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;
}
Infamy answered 19/2, 2016 at 12:18 Comment(7)
Doesn't compile on Win32 :/Pennoncel
@Pennoncel the lib is written for iOS and Android. But I can help you compiling it for Win32. It is not too difficultInfamy
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 yesPennoncel
@Pennoncel 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 closeInfamy
@Pennoncel please create an issue in my repo - I shall add win32 support once I have enough timeInfamy
@Infamy have you had enough time to port your library for windows?Pall
@ImtiazShakilSiddique I am sorry I don't support this lib anymore. Better use curl: it works for windows and everything else in the world =)Infamy
S
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;
}
Suffice answered 12/5, 2022 at 12:13 Comment(0)
V
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.
Vicegerent answered 15/5, 2019 at 16:34 Comment(0)
T
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();
Tale answered 12/2, 2014 at 4:32 Comment(0)
T
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
Tamanaha answered 14/6, 2017 at 1:48 Comment(1)
Does it supports https?Issiah
G
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.

Genevivegenevra answered 22/4, 2021 at 4:3 Comment(0)
A
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;
}
Anglophobia answered 11/3, 2015 at 9:12 Comment(0)
C
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.

Chessa answered 17/8, 2020 at 19:57 Comment(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."Gaul
D
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

Duvall answered 23/6, 2022 at 6:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.