#include <curl/curl.h>#include <iostream>#include <vector>#include <utility>#include <algorithm>#include <iterator>#include <map>// a wrapper around select that works with CURL_CSELECT_ valuesclass Poll{public:   typedef std::vector<std::pair<curl_socket_t, int> > action_list;   void add(curl_socket_t fd, int events)   {      if ((events & CURL_CSELECT_IN) == CURL_CSELECT_IN)         read_.push_back(fd);      if ((events & CURL_CSELECT_OUT) == CURL_CSELECT_OUT)         write_.push_back(fd);      if ((events & CURL_CSELECT_ERR) == CURL_CSELECT_ERR)         err_.push_back(fd);   }   void remove(curl_socket_t fd)   {      read_.erase(std::remove(read_.begin(), read_.end(), fd), read_.end());      write_.erase(std::remove(write_.begin(), write_.end(), fd), write_.end());      err_.erase(std::remove(err_.begin(), err_.end(), fd), err_.end());   }   void poll(long /*ms*/ timeout, action_list& al)   {      fd_set r,w,e;      int rmax = build_fdset(&r, read_);      int wmax = build_fdset(&w, write_);      int emax = build_fdset(&e, err_);      int largestfd = (std::max)(rmax, (std::max)(wmax, emax));      timeval tv;      timeval* ptv = NULL;      if (-1 != timeout)      {         tv.tv_sec = timeout / 1000;         tv.tv_usec = (timeout % 1000) * 1000;         ptv = &tv;      }      int wantaction = select(largestfd+1, &r, &w, &e, ptv);      if (0 == wantaction)         return;      //else if (-1 == wantaction)      //   std::cout << WSAGetLastError() << std::endl;      else      {         action_map active;         std::for_each(read_.begin(), read_.end(), add_to_map(active, &r, CURL_CSELECT_IN));         std::for_each(write_.begin(), write_.end(), add_to_map(active, &w, CURL_CSELECT_OUT));         std::for_each(err_.begin(), err_.end(), add_to_map(active, &e, CURL_CSELECT_ERR));         std::copy(active.begin(), active.end(), std::back_inserter(al));      }   }private:   typedef std::vector<curl_socket_t> socket_list;   typedef std::map<curl_socket_t, int> action_map;   struct add_to_map   {      add_to_map(action_map& m, fd_set* fds, int flag)         :map_(&m)         ,fds_(fds)         ,flag_(flag)      {}      void operator()(curl_socket_t fd)      {         if (FD_ISSET(fd, fds_))            (*map_)[fd] |= flag_;      }      action_map* map_;      fd_set* fds_;      int flag_;   };   static int build_fdset(fd_set* fds, socket_list const& sl)   {      FD_ZERO(fds);      int maxfd = 0;      for (socket_list::const_iterator b = sl.begin(), e = sl.end(); b != e; ++b)      {         FD_SET(*b, fds);         maxfd = (std::max)(static_cast<int>(*b), maxfd);      }      return maxfd;   }   socket_list read_;   socket_list write_;   socket_list err_;};struct curl_call_failed{};void check_mcode(CURLMcode c, int line){   if (CURLM_OK != c)   {      std::cout << "Multi call failed: " << c << " (" << curl_multi_strerror(c) << ") on line " << line << std::endl;      throw curl_call_failed();   }}void check_ecode(CURLcode c, int line){   if (CURLE_OK != c)   {      std::cout << "Easy call failed: " << c << " (" << curl_easy_strerror(c) << ") on line " << line << std::endl;      throw curl_call_failed();   }}#define EASY(x) check_ecode(x, __LINE__)#define MULTI(x) check_mcode(x, __LINE__)int multi_socket_all_loop(CURLM* m){   int running = 0;   bool loop = true;   while (loop)   {      CURLMcode res = curl_multi_socket_all(m, &running);      switch (res)      {      case CURLM_OK:         loop = false;         break;      case CURLM_CALL_MULTI_PERFORM:         break;      default:         MULTI(res);      }   }   return running;}int multi_socket_action_loop(CURLM* m, curl_socket_t fd, int event){   //std::cout << "called with " << event << " on " << static_cast<int>(fd) << std::endl;   int running;   CURLMcode res = curl_multi_socket_action(m, fd, event, &running);   if (CURLM_CALL_MULTI_PERFORM == res)      return multi_socket_all_loop(m);   else   {      MULTI(res);      return running;   }}//int (*curl_socket_callback)(CURL *easy, curl_socket_t s, int what, void *userp, void *socketp);int socket_callback(CURL*, curl_socket_t fd, int what, void* userp, void*){   Poll* poll= static_cast<Poll*>(userp);   switch(what)   {   case CURL_POLL_NONE:      break;   case CURL_POLL_IN:      poll->add(fd, CURL_CSELECT_IN);      break;   case CURL_POLL_OUT:      poll->add(fd, CURL_CSELECT_OUT);      break;   case CURL_POLL_INOUT:      poll->add(fd, CURL_CSELECT_IN | CURL_CSELECT_OUT);      break;   case CURL_POLL_REMOVE:      poll->remove(fd);   }   return 0;}//int (*curl_multi_timer_callback)(CURLM *multi, long timeout_ms, void *userp)int timer_callback(CURLM*, long timeoutms, void* userp){ //std::cout << "in timer " << timeoutms << std::endl   long* ms = static_cast<long*>(userp);   *ms = timeoutms;   return 0;}//size_t function( void *ptr, size_t size, size_t nmemb, void *stream);size_t write_callback(void*, size_t size, size_t nmemb, void*){   // do nothing   return size * nmemb;}// stolen from 10-at-a-time.cstatic const char *URLS[] = {
  "http://www.microsoft.com",
  "http://www.opensource.org",
  "http://www.google.com",
  "http://www.yahoo.com",
  "http://www.ibm.com",
  "http://www.mysql.com",
  "http://www.oracle.com",
  "http://www.ripe.net",
  "http://www.iana.org",
  "http://www.amazon.com",
  "http://www.netcraft.com",
  "http://www.heise.de",
  "http://www.chip.de",
  "http://www.ca.com",
  "http://www.cnet.com",
  "http://www.news.com",
  "http://www.cnn.com",
  "http://www.wikipedia.org",
  "http://www.dell.com",
  "http://www.hp.com",
  "http://www.cert.org",
  "http://www.mit.edu",
  "http://www.nist.gov",
  "http://www.ebay.com",
  "http://www.playstation.com",
  "http://www.uefa.com",
  "http://www.ieee.org",
  "http://www.apple.com",
  "http://www.sony.com",
  "http://www.symantec.com",
  "http://www.zdnet.com",
  "http://www.fujitsu.com",
  "http://www.supermicro.com",
  "http://www.hotmail.com",
  "http://www.ecma.com",
  "http://www.bbc.co.uk",
  "http://news.google.com",
  "http://www.foxnews.com",
  "http://www.msn.com",
  "http://www.wired.com",
  "http://www.sky.com",
  "http://www.usatoday.com",
  "http://www.cbs.com",
  "http://www.nbc.com",
  "http://slashdot.org",
  "http://www.bloglines.com",
  "http://www.techweb.com",
  "http://www.newslink.org",
  "http://www.un.org",
   NULL
};
int main(){   try   {      size_t CONCURRENT = 5;      char const* const* urls = URLS;      EASY(curl_global_init(CURL_GLOBAL_ALL));      CURLM* multi = curl_multi_init();  // no error code check      Poll poll;      long timeoutcb;      MULTI(curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, socket_callback));      MULTI(curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, &poll));      MULTI(curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, timer_callback));      MULTI(curl_multi_setopt(multi, CURLMOPT_TIMERDATA, &timeoutcb));      multi_socket_all_loop(multi);      std::vector<CURL*> easys;      std::generate_n(std::back_inserter(easys), CONCURRENT, curl_easy_init);  // no error code check      int running = 0;      while (*urls || easys.size() != CONCURRENT)      {         size_t easycount = easys.size();         while (*urls && !(easys.empty()))         {            CURL* handle = easys.back();            easys.pop_back();            EASY(curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback));            EASY(curl_easy_setopt(handle, CURLOPT_URL, *urls));            urls += 1;            MULTI(curl_multi_add_handle(multi, handle));         }         if (easys.size() != easycount)            running = multi_socket_all_loop(multi);         int active = running;         long timeout = 0;         MULTI(curl_multi_timeout(multi, &timeout));         if (timeout != timeoutcb)            std::cout << "function: " <<timeout << ", callback: " << timeoutcb << std::endl;         if (0 == timeout)            active = multi_socket_all_loop(multi);         else         {            Poll::action_list al;            poll.poll(timeout, al);            if (al.empty()) // timeout               active = multi_socket_action_loop(multi, CURL_SOCKET_TIMEOUT, 0);            else            {               for (Poll::action_list::const_iterator b = al.begin(), e = al.end(); b != e; ++b)                  active = multi_socket_action_loop(multi, b->first, b->second);                     }         }         if (active < running)         {            running = active;            int count;            while (CURLMsg* msg = curl_multi_info_read(multi, &count))            {               //char const* url;               //long httpcode;               //curl_easy_getinfo(msg->easy_handle, CURLINFO_EFFECTIVE_URL, &url);               //curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &httpcode);               //if (CURLE_OK == msg->data.result)               //   std::cout << url << "->" << httpcode << std::endl;               //else               //   std::cout << "FAILED: " << url << "->" << httpcode << " (" << msg->data.result << ") " << std::endl;               CURL* h = msg->easy_handle;               MULTI(curl_multi_remove_handle(multi, h));               curl_easy_reset(h);               easys.push_back(h);            }         }      }      MULTI(curl_multi_cleanup(multi));      std::for_each(easys.begin(), easys.end(), curl_easy_cleanup);      curl_global_cleanup();   }   catch (curl_call_failed&)   {      // just leak everything if something fails   }}
