Jonathan Henson
Jonathan Henson

Reputation: 8206

libcurl c++ segfault on POSTing JSON

Disclaimer: I am not asking anyone to debug this code, I am more interested to know if anyone sees that I am using libcurl improperly, because as far as I can tell, I am following the documentation exactly.

The problem is in the MakeRequest() method. At curl_easy_perform(), I get std output of

* About to connect() to dynamodb.us-east-1.amazonaws.com port 80 (#0)
*   Trying 72.21.195.244... * connected

Then a segfault.

Here is the stack trace:

Thread [1] 30267 [core: 0] (Suspended : Signal : SIGSEGV:Segmentation fault)    
    Curl_getformdata() at 0x7ffff79069bb    
    Curl_http() at 0x7ffff790b178   
    Curl_do() at 0x7ffff791a298 
    Curl_do_perform() at 0x7ffff7925457 
    CurlHttpClient::MakeRequest() at CurlHttpClient.cpp:91 0x7ffff7ba17f5   
    AWSClient::MakeRequest() at AWSClient.cpp:54 0x7ffff7bbac4d 
    DynamoDbV2Client::GetItem() at DynamoDbV2Client.cpp:34 0x7ffff7bb7380   
    GetItemResultTest_TestLiveRequest_Test::TestBody() at GetItemResultTest.cpp:88 0x43db5a 
    testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>() at gtest-all.cc:3,562 0x46502f 
    testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>() at gtest-all.cc:3,598 0x4602f6    
    <...more frames...> 

Here is the code in question.

#include "http/curl/CurlHttpClient.h"
#include "http/standard/StandardHttpResponse.h"
#include "utils/StringUtils.h"
#include <curl/curl.h>
#include <sstream>
#include <algorithm>
#include <functional>
#include <vector>

bool CurlHttpClient::isInit = false;

void SetOptCodeForHttpMethod(CURL* requestHandle, HttpMethod method)
{
  switch (method)
  {
  case GET:
    curl_easy_setopt(requestHandle, CURLOPT_HTTPGET, 1);
    break;
  case POST:
    curl_easy_setopt(requestHandle, CURLOPT_HTTPPOST, 1);
    break;
  case PUT:
    curl_easy_setopt(requestHandle, CURLOPT_PUT, 1);
    break;
  default:
    curl_easy_setopt(requestHandle, CURLOPT_CUSTOMREQUEST, "DELETE");
    break;
  }
}

CurlHttpClient::CurlHttpClient()
{
  if (!isInit)
  {
    isInit = true;
    curl_global_init(CURL_GLOBAL_ALL);
  }
}

CurlHttpClient::~CurlHttpClient()
{
}

HttpResponse* CurlHttpClient::MakeRequest(const HttpRequest& request) const
{
  struct curl_slist* headers = NULL;

  std::stringstream headerStream;
  HeaderValueCollection requestHeaders = request.GetHeaders();

  for (HeaderValueCollection::iterator iter = requestHeaders.begin();
      iter != requestHeaders.end(); ++iter)
  {
    headerStream.str("");
    headerStream << iter->first << ": " << iter->second;
    headers = curl_slist_append(headers, headerStream.str().c_str());
  }

  CURL* singleRequestHandle = curl_easy_init();
  HttpResponse* response = NULL;

  if (singleRequestHandle)
  {
    if (headers)
    {
      curl_easy_setopt(singleRequestHandle, CURLOPT_HTTPHEADER, headers);
    }

    if(request.GetMethod() == HttpMethod::POST)
    {
      curl_easy_setopt(singleRequestHandle, CURLOPT_POSTFIELDS, request.GetUri().GetFormParameters().c_str());
    }

    response = new StandardHttpResponse(request);

    SetOptCodeForHttpMethod(singleRequestHandle, request.GetMethod());
    std::string url = request.GetURIString(false);
    curl_easy_setopt(singleRequestHandle, CURLOPT_URL, url.c_str());
    curl_easy_setopt(singleRequestHandle, CURLOPT_WRITEFUNCTION, &CurlHttpClient::WriteData);
    curl_easy_setopt(singleRequestHandle, CURLOPT_WRITEDATA, response);
    curl_easy_setopt(singleRequestHandle, CURLOPT_HEADERFUNCTION, &CurlHttpClient::WriteHeader);
    curl_easy_setopt(singleRequestHandle, CURLOPT_HEADERDATA, response);

    if (request.GetContentBody() != NULL)
    {
      curl_easy_setopt(singleRequestHandle, CURLOPT_POSTFIELDSIZE, request.GetContentBody()->tellp());
      curl_easy_setopt(singleRequestHandle, CURLOPT_READFUNCTION, &CurlHttpClient::ReadBody);
      curl_easy_setopt(singleRequestHandle, CURLOPT_READDATA, &request);
    }

    curl_easy_setopt(singleRequestHandle, CURLOPT_VERBOSE, 1L);
    curl_easy_perform(singleRequestHandle);

    int responseCode;
    curl_easy_getinfo(singleRequestHandle, CURLINFO_RESPONSE_CODE, &responseCode);
    response->SetResponseCode((HttpResponseCode) responseCode);

    char* contentType = NULL;
    curl_easy_getinfo(singleRequestHandle, CURLINFO_CONTENT_TYPE, &contentType);
    response->SetContentType(contentType);
    curl_easy_cleanup(singleRequestHandle);
  }

  if (headers)
  {
    curl_slist_free_all(headers);
  }

  return response;
}

size_t CurlHttpClient::WriteData(char *ptr, size_t size, size_t nmemb, void* userdata)
{
  if (ptr)
  {
    HttpResponse* response = (HttpResponse*)userdata;
    if (!response->GetResponseBody())
    {
      std::streambuf* strBuffer = new std::stringbuf;
      response->SetResponseBody(new std::iostream(strBuffer));
    }

    int sizeToWrite = size * nmemb;
    response->GetResponseBody()->write(ptr, sizeToWrite);

    return sizeToWrite;
  }

  return 0;
}

size_t CurlHttpClient::WriteHeader(char *ptr, size_t size, size_t nmemb, void* userdata)
{
  if (ptr)
  {
    HttpResponse* response = (HttpResponse*)userdata;
    std::string headerLine(ptr);
    std::vector<std::string> keyValuePair = StringUtils::Split(headerLine, ':');

    if (keyValuePair.size() == 2)
    {
      std::string headerName = keyValuePair[0];
      headerName = StringUtils::Trim(headerName);

      std::string headerValue = keyValuePair[1];
      headerValue = StringUtils::Trim(headerValue);

      response->AddHeader(headerName, headerValue);
    }
    return size * nmemb;
  }

  return 0;
}

size_t CurlHttpClient::ReadBody(char* ptr, size_t size, size_t nmemb, void* userdata)
{
  HttpRequest* request = (HttpRequest*)userdata;
  std::shared_ptr<std::iostream> outputStream = request->GetContentBody();

  if (outputStream != NULL && size * nmemb)
  {
    size_t written = outputStream->readsome(ptr, size * nmemb);
    return written;
  }

  return 0;
}

For reference here is the definition for CurlHttpClient:

//Curl implementation of an http client. Right now it is only synchronous.
class CurlHttpClient : public HttpClient
{  
  public: 
   //Creates client, intializes curl handle if it hasn't been created already.
   CurlHttpClient();
   //cleans up curl lib
   virtual ~CurlHttpClient();
   //Makes request and recieves response synchronously
   virtual HttpResponse* MakeRequest(const HttpRequest& request) const;

  private:    
    //Callback to read the content from the content body of the request
    static size_t ReadBody(char* ptr, size_t size, size_t nmemb, void* userdata);
    //callback to write the content from the response to the response object
    static size_t WriteData( char* ptr, size_t size, size_t nmemb, void* userdata);
    //callback to write the headers from the response to the response
    static size_t WriteHeader( char* ptr, size_t size, size_t nmemb, void* userdata);

    //init flag.
    static bool isInit;     
};

Upvotes: 2

Views: 893

Answers (1)

ArtemGr
ArtemGr

Reputation: 12567

One definite problem I see with the code is

curl_easy_setopt(requestHandle, CURLOPT_HTTPPOST, 1);

CURLOPT_HTTPPOST expects a pointer to a structure of type struct curl_httppost. Passing 1 creates a dangling pointer. You probably might want to use the CURLOPT_POST instead.

Upvotes: 4

Related Questions