/*
 * This test triggers a read on free()ed data with a combo of
 * keepalive requests, curl_multi and curl_share. 
 *
 * You need to run it with valgrind (I used 2.2.0)
 */
#include <unistd.h>
#include "curl/curl.h"

int main() {
  CURLM   *curlm;
  CURLSH  *curls;
  CURL    *curl1;
  CURL    *curl2;
  CURLMsg *msg;
  int     running;
  int     maxfd;
  int     nb_msg;
  int     nb_requests;
  int     nb_reloaded;
  fd_set  fd_r;
  fd_set  fd_w;
  fd_set  fd_e;

  /* init curl & different handlers, DNS cache timeout is 5 seconds */
  /* we do need a host honoring http keepalive */
  curl_global_init(CURL_GLOBAL_ALL);
  curlm=curl_multi_init();
  curls=curl_share_init();
  curl_share_setopt(curls, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
  curl1=curl_easy_init();
  curl_easy_setopt(curl1, CURLOPT_URL, "http://www.apache.org/");
  curl_easy_setopt(curl1, CURLOPT_SHARE, curls);
  curl_easy_setopt(curl1, CURLOPT_TIMEOUT, 300);
  curl_easy_setopt(curl1, CURLOPT_FOLLOWLOCATION, 1);
  curl_easy_setopt(curl1, CURLOPT_MAXREDIRS, 10);
  curl_easy_setopt(curl1, CURLOPT_HTTPGET, 1);
  curl_easy_setopt(curl1, CURLOPT_VERBOSE, 1);
  curl_easy_setopt(curl1, CURLOPT_DNS_CACHE_TIMEOUT, 5);
  curl2=curl_easy_init();
  curl_easy_setopt(curl2, CURLOPT_URL,"http://www.apache.org/");
  curl_easy_setopt(curl2, CURLOPT_SHARE, curls);
  curl_easy_setopt(curl2, CURLOPT_TIMEOUT, 300);
  curl_easy_setopt(curl2, CURLOPT_FOLLOWLOCATION, 1);
  curl_easy_setopt(curl2, CURLOPT_MAXREDIRS, 10);
  curl_easy_setopt(curl2, CURLOPT_HTTPGET, 1);
  curl_easy_setopt(curl2, CURLOPT_VERBOSE, 1);
  curl_easy_setopt(curl1, CURLOPT_DNS_CACHE_TIMEOUT, 5);
  curl_multi_add_handle(curlm, curl1);
  curl_multi_add_handle(curlm, curl2);

  /* we run at least 20 requests, each one followed by 1 second sleep() */
  nb_reloaded=2;
  nb_requests=0;
  while(nb_requests<20) {
    struct timeval timeout;

    /* if both handlers are reloaded, we need a call to curl_multi_perform() */
    if (nb_reloaded==2) {
      FD_ZERO(&fd_r);
      FD_ZERO(&fd_w);
      FD_ZERO(&fd_e);
      while(curl_multi_perform(curlm, &running)==CURLM_CALL_MULTI_PERFORM);
    }
    nb_reloaded=0;

    /* select() stuff */
    timeout.tv_sec  = 1;
    timeout.tv_usec = 0;
    FD_ZERO(&fd_r);
    FD_ZERO(&fd_w);
    FD_ZERO(&fd_e);
    curl_multi_fdset(curlm, &fd_r, &fd_w, &fd_e, &maxfd);
    switch(select(maxfd+1, &fd_r, &fd_w, &fd_e, &timeout)) {
    case -1:
      /* select() error */
      fprintf(stderr, "select() error\n");
      break;
    case 0:
      /* select() timeout */
      fprintf(stderr, "select() timeout\n");
      break;
    default:
	/* readable/writable sockets */
      while(curl_multi_perform(curlm, &running)==CURLM_CALL_MULTI_PERFORM);
      break;
    }

    /* reload handlers which have the request completed */
    while((msg=curl_multi_info_read(curlm, &nb_msg))) {
      if (msg->msg==CURLMSG_DONE) {
	CURL *this_handle=msg->easy_handle;
	nb_requests++;
	nb_reloaded++;
	curl_multi_remove_handle(curlm, this_handle);
	curl_easy_setopt(this_handle, CURLOPT_URL, "http://www.apache.org/");
	curl_multi_add_handle(curlm, this_handle);
	sleep(1);
      }
    }
  }

  /* cleanup */
  curl_multi_remove_handle(curlm, curl1);
  curl_multi_remove_handle(curlm, curl2);
  curl_easy_cleanup(curl1);
  curl_easy_cleanup(curl2);
  curl_share_cleanup(curls);
  curl_multi_cleanup(curlm);
  curl_global_cleanup();

  return 0;
}

