Search code examples
c++curllibcurlvoid-pointers

How to store sent/received data with libcurl


Im trying to implement some curl and curlcpp functions for a small project.

So far i have implemented a few functions but now i want to add functionality. The idea is to store in a buffer the sent and receive data so later i can access:

  • Sent Data
  • Sent Headers
  • Received Data
  • Received Headers

I know there is CURLOPT_WRITEFUNCTION but this only works for response data and i need the output data/headers too. CURLOPT_DEBUGFUNCTION looks that works for me but i cant configure it on CPP.

Here is an example code of my class:

class CurlCPPClient
{
    private:
        ...
        int trace_data(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr);
        curl::curl_easy curl_easy;
        std::string raw_data;
    public:
        ...
}

CurlCPPClient::CurlCPPClient()
{
    curl_easy.add<CURLOPT_DEBUGFUNCTION>(this->trace_data);
}

int CurlCPPClient::trace_data(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr) 
{
  std::string frame_info(data);

  switch(type)
  {
    case CURLINFO_HEADER_OUT:
    case CURLINFO_DATA_OUT:
      raw_data += frame_info;
      break;
    case CURLINFO_HEADER_IN:
    case CURLINFO_DATA_IN:
      raw_data += frame_info;
      break;
    default:
      break;
  }

  return 0;
}

I have compiler issues when setting the function pointer to trace_data. I get "error C3867" and "error C2228" with: this->trace_data, this.trace_data, trace_data. If i dont use trace_data as a method of my class, i can call it but i cant save the info on a member to access it later in another part of the class.


Solution

  • libcurl expects standalone C-style functions for its callbacks. You can't use a non-static class method for a callback.

    Declare your trace_data() method as static. You can use CURLOPT_DEBUGDATA to pass the this pointer of the CurlCPPClient object to the method's userptr parameter.

    class CurlCPPClient
    {
        private:
            ...
            static int trace_data(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr);
            curl::curl_easy curl_easy;
            std::string raw_data;
        public:
            ...
    }
    
    CurlCPPClient::CurlCPPClient()
    {
        curl_easy.add<CURLOPT_DEBUGFUNCTION>(&CurlCPPClient::trace_data);
        curl_easy.add<CURLOPT_DEBUGDATA>(this);
    }
    
    int CurlCPPClient::trace_data(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr) 
    {
      CurlCPPClient *pThis = static_cast<CurlCPPClient*>(userptr);
      std::string frame_info(data, size);
    
      switch(type)
      {
        case CURLINFO_HEADER_OUT:
        case CURLINFO_DATA_OUT:
          pThis->raw_data += frame_info;
          break;
        case CURLINFO_HEADER_IN:
        case CURLINFO_DATA_IN:
          pThis->raw_data += frame_info;
          break;
        default:
          break;
      }
    
      return 0;
    }
    

    Alternatively, use CURLOPT_DEBUGDATA to pass just the raw_data member to the callback:

    class CurlCPPClient
    {
        private:
            ...
            static int trace_data(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr);
            curl::curl_easy curl_easy;
            std::string raw_data;
        public:
            ...
    }
    
    CurlCPPClient::CurlCPPClient()
    {
        curl_easy.add<CURLOPT_DEBUGFUNCTION>(&CurlCPPClient::trace_data);
        curl_easy.add<CURLOPT_DEBUGDATA>(&raw_data);
    }
    
    int CurlCPPClient::trace_data(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr) 
    {
      std::string *pRawData = static_cast<std::string*>(userptr);
      std::string frame_info(data, size);
    
      switch(type)
      {
        case CURLINFO_HEADER_OUT:
        case CURLINFO_DATA_OUT:
          *pRawData += frame_info;
          break;
        case CURLINFO_HEADER_IN:
        case CURLINFO_DATA_IN:
          *pRawData += frame_info;
          break;
        default:
          break;
      }
    
      return 0;
    }