Blog of roxlu, co-founder of Apollo Media. Contact info[shift+2]apollomedia.nl.

Download HTML page using CURL

A quick snippet that uses libuv + libcurl to download an RSS feed into a std::string in a separate thread. This code was used in the SWNT project we did at Apollo to retrieve the weather forecasts for a specific location. The code below shows only how to use libcurl to download some data from a url. The code needs to add some cleanup.

Weather.h

/*
 
  Weather
  -------
  Fetches an RSS feed from the Yahoo weather services. The current
  weather state is used to influence the visuals of the water.
 
 */
#ifndef SWNT_WEATHER_H
#define SWNT_WEATHER_H
 
extern "C" {
#  include <uv.h>
#  include <curl/curl.h>
};
 
#include <vector>
#include <string>
 
#define WEATHER_TASK_NONE 0
#define WEATHER_TASK_FETCH_RSS 1
#define WEATHER_TASK_STOP 2
 
size_t weather_write_data(void* ptr, size_t size, size_t nmemb, void* str);
std::string weather_download_yahoo_rss();
bool weather_parse_yahoo_rss(std::string& rss);
void weather_thread(void* user);
 
struct WeatherTask {
  WeatherTask():type(WEATHER_TASK_NONE){}
  WeatherTask(int t):type(t){}
  int type;
};
 
class Weather {
 
 public:
  Weather();
  ~Weather();
  bool setup();
  void update();
  void fetchYahooRSS();
 public:
  uv_thread_t thread;
  uv_mutex_t task_mutex;
  uv_cond_t task_cv;
  std::vector<WeatherTask*> tasks;
};
#endif

Weather.cpp

#include <swnt/Weather.h>
 
size_t weather_write_data(void* ptr, size_t size, size_t nmemb, void* str) {
  std::string* s = static_cast<std::string*>(str);
  std::copy((char*)ptr, (char*)ptr + (size * nmemb), std::back_inserter(*s));
  return size * nmemb;
}
 
std::string weather_download_yahoo_rss() {
  std::string result;
  std::string url = "http://weather.yahooapis.com/forecastrss?w=10242&u=c"; // w=10242 is Aberaron
  CURL* curl = NULL;
 
  CURLcode res;
  curl = curl_easy_init();
  if(!curl) {
    printf("Error: cannot initialize CURL.\n");
    return result;
  }
 
  res = curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
  if(res != CURLE_OK) {
    printf("Cannot set curl url.\n");
    goto curl_error;
  }
 
  res = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
  if(res != CURLE_OK) {
    printf("Cannot set curl follow location flag.\n");
    goto curl_error;
  }
 
  res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, weather_write_data);
  if(res != CURLE_OK) {
    printf("Cannot set the weather write function.\n");
    goto curl_error;
  }
 
  res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result);
  if(res != CURLE_OK) {
    printf("Cannot set the curl write data.\n");
    goto curl_error;
  }
 
  res = curl_easy_perform(curl);
  if(res != CURLE_OK) {
    printf("Cannot perform curl.\n");
    goto curl_error;
  }
  printf("Got: %s\n", result.c_str());
  printf("performed!\n");
 
  return result;
 
 curl_error: 
    curl_easy_cleanup(curl);
    curl = NULL;
    return result;
}
 
void weather_thread(void* user) {
  Weather* w = static_cast<Weather*>(user);
  std::vector<WeatherTask*> work;
  bool must_stop = false;
 
  printf("weather thread.\n");
  while(true) {
 
    uv_mutex_lock(&w->task_mutex);
    while(w->tasks.size() == 0) {
      uv_cond_wait(&w->task_cv, &w->task_mutex);
    }
    std::copy(w->tasks.begin(), w->tasks.end(), std::back_inserter(work));
    w->tasks.clear();
    uv_mutex_unlock(&w->task_mutex);
 
    for(std::vector<WeatherTask*>::iterator it = work.begin(); it != work.end(); ++it) {
      WeatherTask* task = *it;
      if(task->type == WEATHER_TASK_FETCH_RSS) {
        weather_download_yahoo_rss();
        delete task;
        task = NULL;
      }
      else if(task->type == WEATHER_TASK_STOP) {
        must_stop = true;
        delete task;
        task = NULL;
        break;
      }
    }
 
    work.clear();
 
    if(must_stop) {
      break;
    }
 
  }
 
  printf("Weather thread stopped.\n");
}
 
Weather::Weather() {
  uv_mutex_init(&task_mutex);
  uv_cond_init(&task_cv);
}
 
 
Weather::~Weather() {
  printf("@todo - cleanup weather threads\n");
}
 
bool Weather::setup() {
  uv_thread_create(&thread, weather_thread, this);
  fetchYahooRSS();
  return true;
}
 
void Weather::fetchYahooRSS() {
  WeatherTask* t = new WeatherTask(WEATHER_TASK_FETCH_RSS);
  uv_mutex_lock(&task_mutex);
  tasks.push_back(t);
  uv_cond_signal(&task_cv);
  uv_mutex_unlock(&task_mutex);
}
 
void Weather::update() {
}