How do you make a HTTP request with C++?

后端 未结 22 2505
有刺的猬
有刺的猬 2020-11-22 06:25

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 o

相关标签:
22条回答
  • 2020-11-22 06:59

    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 讨论(0)
  • 2020-11-22 07:01

    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);
    }
    
    0 讨论(0)
  • 2020-11-22 07:01

    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

    0 讨论(0)
  • 2020-11-22 07:03

    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 move(data);
    }
    
    0 讨论(0)
  • 2020-11-22 07:04

    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.

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

    0 讨论(0)
  • 2020-11-22 07:04

    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;
    }
    
    0 讨论(0)
提交回复
热议问题