httpステータスコードをチェックしてからXML解析

前回の記事で使ったlibxml2はhttpレスポンスヘッダのステータスコードの取得ができない。
まずCURLでhttpのヘッダ情報と本文を取得して、ヘッダのチェックをしてステータスコードが200なら本文をlibxml2に渡してパースするようにした。

yahoo!掲示板のRSSのitem要素を出力するだけのプログラム

#include <stdio.h>
#include <libxml/xpath.h>
#include <curl/curl.h>
#include <string>

const char* REQUEST_API = "http://messages.yahoo.co.jp/bbs?action=4&board=552019556&sid=552019556&tid=kcc08bc0obafb3u";
const char* XPATH_ITEM  = "/rss//item/*/text()"; // item以下全てのXPATH

int writer(char *data,
           size_t size,
           size_t nmemb,
           std::string *writerData) {
    if (writerData == NULL) {
        return 0;
    }

    writerData->append(data, size*nmemb);
    return size * nmemb;
}

bool getXML(const char* url,
            std::string& buf) {
    CURL *handle = curl_easy_init();
    CURLcode rc;
    long statusCode = -1;
    
    if (handle) {
        rc = curl_easy_setopt(handle, CURLOPT_URL, url);
        if (rc != CURLE_OK) {
            printf("failed curl_easy_setopt() CURLcode=%d\n", rc);
            return false;
        }
        
        rc = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writer);
        if (rc != CURLE_OK) {
            printf("failed curl_easy_setopt() CURLcode=%d\n", rc);
            return false;
        }

        rc = curl_easy_setopt(handle, CURLOPT_WRITEDATA, &buf);
        if (rc != CURLE_OK) {
            printf("failed curl_easy_setopt() CURLcode=%d\n", rc);
            return false;
        }

        rc = curl_easy_perform(handle);
        if (rc != CURLE_OK) {
            printf("failed curl_easy_setopt() CURLcode=%d\n", rc);
            return false;
        }

        rc = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &statusCode);
        if (rc != CURLE_OK) {
            printf("failed curl_easy_setopt() CURLcode=%d\n", rc);
            return false;
        }
        
        curl_easy_cleanup(handle);
    }

    if (statusCode != 200) { // HTTP response headerのstatus codeをチェック
        printf("failed HTTP request statusCode=%d\n", statusCode);
        return false;
    }

    return true;
}

bool parseXML(const char* buf,
              const char* xpath) {
    xmlDocPtr doc = xmlParseDoc((xmlChar *)buf);
    if (doc == NULL) {
        return false;
    }

    xmlXPathContextPtr ctx = xmlXPathNewContext(doc);
    if (ctx == NULL) {
        xmlFreeDoc(doc);
        return false;
    }
    
    xmlXPathObjectPtr xpobj = xmlXPathEvalExpression((xmlChar *)xpath, ctx);
    if (xpobj == NULL) {
        xmlXPathFreeContext(ctx);
        xmlFreeDoc(doc);
        return false;
    }

    xmlNodeSetPtr nodes = xpobj->nodesetval;
    int size = (nodes) ? nodes->nodeNr : 0;
    for (int i = 0; i < size; ++i) {
        if (!xmlXPathNodeSetIsEmpty(nodes)) {
            xmlNodePtr node = xmlXPathNodeSetItem(nodes, i);
            if (node->content) {
                printf("%s => %s\n", node->parent->name, node->content);
            } else {
                printf("invalid node\n");
                return false;
            }
        }
    }

    xmlXPathFreeObject(xpobj);
    xmlXPathFreeContext(ctx);
    xmlFreeDoc(doc);
    xmlCleanupParser();
    return true;
}

int main(void) {
    std::string buf;
    if (!getXML(REQUEST_API, buf)) {
        printf("failed get XML\n");
        return -1;
    }

    if (!parseXML(buf.c_str(), XPATH_ITEM)) {
        printf("failed parse XML\n");
        return -1;
    }

    return 0;
}