#include <stdio.h>
#include <string.h>
#include <curl/curl.h>

const char data[]="0123456789";

struct WriteThis {
  const char *readptr;
  long sizeleft;
};

static size_t
write_callback(void *contents, size_t size, size_t nmemb, void *userp)
{
  return size*nmemb;
}

static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
  struct WriteThis *pooh = (struct WriteThis *)userp;

  if(size*nmemb < sizeof(data))
    return 0;

  if(pooh->sizeleft) {
    memcpy(ptr, data, sizeof(data));
    pooh->sizeleft -= 10;
    return 10;
  }

  return 0;                          /* no more data left to deliver */
}

static int runonce(CURL *curl,
                   struct WriteThis *p)
{
  CURLcode res;

  p->sizeleft = (long)strlen(data);

  /* First set the URL that is about to receive our POST. */
  curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:8999/1");

  /* Now specify we want to POST data */
  curl_easy_setopt(curl, CURLOPT_POST, 1L);

  /* we want to use our own read function */
  curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);

  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);

  /* pointer to pass to our read function */
  curl_easy_setopt(curl, CURLOPT_READDATA, p);

  /* get verbose debug output please */
  curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);

  /* Set the expected POST size. If you want to POST large amounts of data,
     consider CURLOPT_POSTFIELDSIZE_LARGE */
  curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, p->sizeleft);
  
  /*
    Using POST with HTTP 1.1 implies the use of a "Expect: 100-continue"
    header.  You can disable this header with CURLOPT_HTTPHEADER as usual.
    NOTE: if you want chunked transfer too, you need to combine these two
    since you can only set one list of headers with CURLOPT_HTTPHEADER. */
  
  /* A less good option would be to enforce HTTP 1.0, but that might also
     have other implications. */
  {
    struct curl_slist *chunk = NULL;

    chunk = curl_slist_append(chunk, "Expect:");
    res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
    /* use curl_slist_free_all() after the *perform() call to free this
       list again */
  }

  /* Perform the request, res will get the return code */
  res = curl_easy_perform(curl);
  /* Check for errors */
  if(res != CURLE_OK)
    fprintf(stderr, "curl_easy_perform() failed: %s\n",
            curl_easy_strerror(res));
}


static int runfixed(CURL *curl,
                    struct WriteThis *p)
{
  CURLcode res;

  /* First set the URL that is about to receive our POST. */
  curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:8999/1");

  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "0123456789");

  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);

  /* get verbose debug output please */
  curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);

  /* Set the expected POST size. If you want to POST large amounts of data,
     consider CURLOPT_POSTFIELDSIZE_LARGE */
  curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 10);
  
  /*
    Using POST with HTTP 1.1 implies the use of a "Expect: 100-continue"
    header.  You can disable this header with CURLOPT_HTTPHEADER as usual.
    NOTE: if you want chunked transfer too, you need to combine these two
    since you can only set one list of headers with CURLOPT_HTTPHEADER. */
  
  /* A less good option would be to enforce HTTP 1.0, but that might also
     have other implications. */
  {
    struct curl_slist *chunk = NULL;

    chunk = curl_slist_append(chunk, "Expect:");
    res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
    /* use curl_slist_free_all() after the *perform() call to free this
       list again */
  }

  /* Perform the request, res will get the return code */
  res = curl_easy_perform(curl);
  /* Check for errors */
  if(res != CURLE_OK)
    fprintf(stderr, "curl_easy_perform() failed: %s\n",
            curl_easy_strerror(res));
}

#define LOOPS 10000

int main(int argc, char **argv)
{
  CURL *curl;
  CURLcode res;
  struct WriteThis pooh;
  int alt=0;

  if(argc > 1) {
    alt = 1;
    printf("runs fixed string version\n");
  }

  pooh.readptr = data;

  /* In windows, this will init the winsock stuff */
  res = curl_global_init(CURL_GLOBAL_DEFAULT);
  /* Check for errors */
  if(res != CURLE_OK) {
    fprintf(stderr, "curl_global_init() failed: %s\n",
            curl_easy_strerror(res));
    return 1;
  }

  /* get a curl handle */
  curl = curl_easy_init();
  if(curl) {
    int i;
    if(alt) {
      for(i=0; i< LOOPS; i++)
        runfixed(curl, &pooh);
    }
    else {
      for(i=0; i< LOOPS; i++)
        runonce(curl, &pooh);
    }

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  curl_global_cleanup();
  return 0;
}
