libcurl的封装,支持同步请求,支持异步通知请求

将libcurl封装了一下

满足同步请求,堵塞操作

也可以异步请求,马上返回,由请求在完成操作之后通知主函数请求已经就绪

闲话不多说,直接上代码

//header
#ifndef __HTTP_REQUEST_H
#define __HTTP_REQUEST_H


#include <string>
#include <map>
#include <memory>

class HttpNotify {
public:
    HttpNotify() {}
    ~HttpNotify() {}

    typedef enum {
        STATE_PROCEEDING,
        STATE_SUCCESS,
        STATE_ERROR = -1,
    }NotifyState;

public:
    virtual void FinishedNotify(bool, const std::string&) = 0;
    virtual int ProcessCallback(NotifyState state, long size_data, const void* data) = 0;
};

//************************************
// Usage:    
// HttpRequest request;
// request.SetRequestUrl("http://www.baidu.com");
// request.SetPostMessage("hello liyanhong");
// request.SetHttpHeader("User-Agent:Mozilla/4.04[en](Win95;I;Nav)");
// request.SetHttpHeader("Range:bytes=554554-");
// request.SetNotifyObject(obj);
// /*sync request will block and async request will return immedately*/
// HANDLE hRequest = request.Perform(REQUEST_SYNC);
// /*recomment HttpRequest::Close(hRequest) while async request job done*/
// /*sync request will ignore Close*/
//************************************
class HttpRequest
{
public:
    typedef enum {
        REQUEST_SYNC,
        REQUEST_ASYNC,
    }RequestType;

    typedef enum {
        REQUEST_OK,
        REQUEST_INVALID_OPT,
        REQUEST_PERFORM_ERROR,
        REQUEST_INIT_ERROR,
    }RequestResult;

    friend class RequestHelper;

    HttpRequest();
    ~HttpRequest();

    void SetRetryTimes(int retry_times = s_kRetryCount);

    RequestResult SetRequestTimeout(long time_out = 0);
    RequestResult SetRequestUrl(const std::string& url);
    RequestResult SetPostMessage(const std::string& message);
    RequestResult SetHttpHeader(std::map<std::string, std::string>& headers);
    RequestResult SetHttpHeader(const std::string& header);

    void SetNotifyObject(HttpNotify* object = NULL);

    HANDLE Perform(RequestType request_type);
    static void Close(HANDLE request_handle);

    bool GetHttpCode(HANDLE request_handle, long* http_code);
    bool GetReceiveHeader(HANDLE request_handle, std::string* header);
    bool GetReceiveContent(HANDLE request_handle, std::string* receive);

protected:   

    class HttpInternal {
    public:
        HttpInternal();
        ~HttpInternal();

        friend class HttpRequest;
        friend class RequestHelper;

        void SetRetryTimes(int retry_times) { m_retry_times = retry_times; }

        int SetRequestTimeout(long time_out = 0);
        int SetRequestUrl(const std::string& url);
        int SetPostMessage(const std::string& message);
        int SetHttpHeader(const std::string& header);
        void SetNotify(HttpNotify*);

        int Perform();

        int GetHttpCode() { return m_http_code; }
        bool GetHeader(std::string* header);
        bool GetContent(std::string* receive);

        bool SelfClose(void) { return m_close_self; }

    private:
        HANDLE  m_curl_handle;
        HANDLE  m_perform_thread;
        int     m_retry_times;
        std::string receive_content;
        std::string receive_header;
        bool m_close_self;

        HttpNotify* m_notify;
        HANDLE m_http_headers;

        long m_http_code;
    };

private:

    //HANDLE      m_request_handle;
    std::tr1::shared_ptr<HttpInternal> m_request_handle;
    static const int s_kRetryCount = 3;
};

#endif  /*__HTTP_REQUEST_H*/
//cpp
#include "stdafx.h"
#include <Windows.h>
#include "HttpRequest.h"
#include "./curl/curl.h"

#include <list>

#ifdef _DEBUG
#pragma comment(lib, "libcurld.lib")
#else
#pragma comment(lib, "libcurl.lib")
#endif

#pragma comment(lib, "Wldap32.lib")
#pragma comment(lib, "Ws2_32.lib")

class RequestHelper {
protected:
    RequestHelper() { curl_global_init(CURL_GLOBAL_DEFAULT); }

public:
    ~RequestHelper() 
    { 
        curl_global_cleanup();
        s_async_requests.clear();
    }

    static RequestHelper& Instance()
    {
        static RequestHelper the_single_instance;

        return the_single_instance;
    }

    static std::list< std::tr1::shared_ptr<HttpRequest::HttpInternal> > s_async_requests;

    static DWORD WINAPI PerformThread(LPVOID param)
    {
        std::tr1::shared_ptr<HttpRequest::HttpInternal>* request = reinterpret_cast< std::tr1::shared_ptr<HttpRequest::HttpInternal>* >(param);

        if (request)
        {
            (*request)->Perform();

            if ((*request)->SelfClose())
            {
                RequestHelper::s_async_requests.remove(*request);
            }

        }
        
        return 1;
    }

    static size_t RetriveHeaderFunction(void *ptr, size_t size, size_t nmemb, void *stream)
    {
        std::string* receive_header = (std::string*)stream;
        if (receive_header && ptr)
        {
            receive_header->append(reinterpret_cast<const char*>(ptr), size * nmemb);
        }

        return nmemb;
    }

    static size_t RetriveContentFunction(void *ptr, size_t size, size_t nmemb, void *stream)
    {
        HttpRequest::HttpInternal* request = reinterpret_cast<HttpRequest::HttpInternal* >(stream);       
        if (request && ptr)
        {
            std::string* receive = &(request->receive_content);
            receive->append(reinterpret_cast<const char*>(ptr), size * nmemb);
            if (request->m_notify)
            {
                if (request->m_notify->ProcessCallback(HttpNotify::STATE_PROCEEDING, size*nmemb, ptr) == 0)
                    return 0;   //interrupt request process
            }
        }

        return nmemb;
    }
};

std::list< std::tr1::shared_ptr<HttpRequest::HttpInternal> > RequestHelper::s_async_requests;

HttpRequest::HttpRequest()
    : m_request_handle(new HttpInternal)
{
    RequestHelper::Instance();
}

HttpRequest::~HttpRequest()
{
}

void HttpRequest::SetRetryTimes(int retry_times)
{
    if (m_request_handle)
    {
        m_request_handle->SetRetryTimes(retry_times);
    }
}

HttpRequest::RequestResult HttpRequest::SetRequestTimeout(long time_out)
{
    if (m_request_handle)
    {
        if (m_request_handle->SetRequestTimeout(time_out) == CURLE_OK)
        {
            return REQUEST_OK;
        }
        else
        {
            return REQUEST_INVALID_OPT;
        }
    }

    return REQUEST_INIT_ERROR;
}

HttpRequest::RequestResult HttpRequest::SetRequestUrl(const std::string& url)
{
    if (m_request_handle)
    {
        if (m_request_handle->SetRequestUrl(url) == CURLE_OK)
        {
            return REQUEST_OK;
        }
        else
        {
            return REQUEST_INVALID_OPT;
        }
    }

    return REQUEST_INIT_ERROR;
}

HttpRequest::RequestResult HttpRequest::SetPostMessage(const std::string& message)
{
    if (m_request_handle)
    {
        if (m_request_handle->SetPostMessage(message) == CURLE_OK)
        {
            return REQUEST_OK;
        }
        else
        {
            return REQUEST_INVALID_OPT;
        }
    }
    return REQUEST_INIT_ERROR;
}

HttpRequest::RequestResult HttpRequest::SetHttpHeader(std::map<std::string, std::string>& headers)
{
    if (m_request_handle)
    {
        std::map<std::string, std::string>::iterator it;
        for (it = headers.begin(); it != headers.end(); ++it)
        {
            std::string header = it->first;
            header += ":";
            header += it->second;
            if (m_request_handle->SetHttpHeader(header) != CURLE_OK)
            {
                return REQUEST_INVALID_OPT;
            }
        }
        return REQUEST_OK;
    }

    return REQUEST_INIT_ERROR;
}

HttpRequest::RequestResult HttpRequest::SetHttpHeader(const std::string& header)
{
    if (m_request_handle)
    {
        if (m_request_handle->SetHttpHeader(header) == CURLE_OK)
        {
            return REQUEST_OK;
        }
        else
        {
            return REQUEST_INVALID_OPT;
        }
    }
    return REQUEST_INIT_ERROR;
}

void HttpRequest::SetNotifyObject(HttpNotify* object)
{
    if (m_request_handle)
    {
        m_request_handle->SetNotify(object);
    }
}

void HttpRequest::Close(HANDLE request_handle)
{
    std::tr1::shared_ptr<HttpInternal>* request = (reinterpret_cast<std::tr1::shared_ptr<HttpInternal> *>(request_handle));
    std::list< std::tr1::shared_ptr<HttpRequest::HttpInternal> >::iterator it;
    for (it = RequestHelper::s_async_requests.begin(); it != RequestHelper::s_async_requests.end(); ++it)
    {
        if ((*request) == *it)
        {
            if (WaitForSingleObject((*request)->m_perform_thread, 10) == WAIT_OBJECT_0)
            {
                RequestHelper::s_async_requests.remove(*request);
            }
            else
            {
                (*request)->m_close_self = true;
            }
            break;
        }
    }
}

HANDLE HttpRequest::Perform(RequestType request_type)
{
    if (m_request_handle)
    {
        if (request_type == REQUEST_SYNC)
        {
            m_request_handle->Perform();

            return &m_request_handle;
        }
        else if (request_type == REQUEST_ASYNC)
        {
            DWORD thread_id;
            RequestHelper::s_async_requests.push_back(m_request_handle);
            std::tr1::shared_ptr<HttpInternal>& request = RequestHelper::s_async_requests.back();

            HANDLE async_thread = CreateThread(NULL, 0, RequestHelper::PerformThread, &(request), 0, &thread_id);
            Sleep(10);
            request->m_perform_thread = async_thread;

            return &request;
        }

        return NULL;
    }

    return NULL;
}

bool HttpRequest::GetHttpCode(HANDLE request_handle, long* http_code)
{
    std::tr1::shared_ptr<HttpInternal>* request = reinterpret_cast<std::tr1::shared_ptr<HttpInternal>*>(request_handle);
    if (request && http_code)
    {
        *http_code = (*request)->GetHttpCode();
        return true;      
    }

    return false;
}

bool HttpRequest::GetReceiveHeader(HANDLE request_handle, std::string* header)
{
    std::tr1::shared_ptr<HttpInternal>* request = reinterpret_cast<std::tr1::shared_ptr<HttpInternal>*>(request_handle);
    if (request)
    {
        return (*request)->GetHeader(header);
    }

    return false;
}

bool HttpRequest::GetReceiveContent(HANDLE request_handle, std::string* receive)
{
    std::tr1::shared_ptr<HttpInternal>* request = reinterpret_cast<std::tr1::shared_ptr<HttpInternal>*>(request_handle);
    if (request)
    {
        return (*request)->GetContent(receive);
    }

    return false;
}

HttpRequest::HttpInternal::HttpInternal()
    : m_curl_handle(NULL)
    , m_perform_thread(NULL)
    , m_notify(NULL)
    , m_http_headers(NULL)
    , m_close_self(false)
    , m_retry_times(HttpRequest::s_kRetryCount)
{
    m_curl_handle = curl_easy_init();
    if (m_curl_handle)
    {
        curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
    }
}

HttpRequest::HttpInternal::~HttpInternal()
{
    if (m_curl_handle)
    {
        curl_easy_cleanup(m_curl_handle);
    }
    if (m_http_headers)
    {
        curl_slist_free_all(reinterpret_cast<curl_slist*>(m_http_headers));
    }
    if (m_perform_thread)
    {
        CloseHandle(m_perform_thread);
    }
}

int HttpRequest::HttpInternal::SetRequestTimeout(long time_out)
{
    if (m_curl_handle)
    {
        return curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT, 0);
    }

    return CURLE_FAILED_INIT;
}

int HttpRequest::HttpInternal::SetRequestUrl(const std::string& url)
{
    if (m_curl_handle)
    {
        return curl_easy_setopt(m_curl_handle, CURLOPT_URL, url.c_str());
    }

    return CURLE_FAILED_INIT;
}

int HttpRequest::HttpInternal::SetPostMessage(const std::string& message)
{
    if (m_curl_handle)
    {
        CURLcode curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_POST, 1);
        if (curl_code == CURLE_OK)
        {
            curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_POSTFIELDS, message.c_str());
        }

        if (curl_code == CURLE_OK)
        {
            curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_POSTFIELDSIZE, message.size());
        }
        
        return curl_code;
    }
    
    return CURLE_FAILED_INIT;
}

int HttpRequest::HttpInternal::SetHttpHeader(const std::string& header)
{
    if (m_curl_handle)
    {
        m_http_headers = curl_slist_append(reinterpret_cast<curl_slist*>(m_http_headers), header.c_str());

        return m_http_headers ? CURLE_OK : CURLSHE_BAD_OPTION;
    }

    return CURLE_FAILED_INIT;
}

void HttpRequest::HttpInternal::SetNotify(HttpNotify* notify_object)
{
    m_notify = notify_object;
}

int HttpRequest::HttpInternal::Perform()
{
    if (m_curl_handle)
    {
        CURLcode curl_code;
        if (m_http_headers)
        {
            curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_HTTPHEADER, reinterpret_cast<curl_slist*>(m_http_headers));
            if (curl_code != CURLE_OK)
            {
                return curl_code;
            }
        }

        receive_header.clear();
        receive_content.clear();

        curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_HEADERFUNCTION, RequestHelper::RetriveHeaderFunction);
        curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_HEADERDATA, &receive_header);

        curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, RequestHelper::RetriveContentFunction);
        curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, this);
        
        curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_NOPROGRESS, 1);

        curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1);
        curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_CONNECTTIMEOUT_MS, 0);

        curl_code = curl_easy_perform(m_curl_handle);
        if (curl_code == CURLE_OPERATION_TIMEDOUT)
        {
            int retry_count = m_retry_times;
            while (retry_count > 0)
            {
                curl_code = curl_easy_perform(m_curl_handle);
                if (curl_code != CURLE_OPERATION_TIMEDOUT) break;
                retry_count--;
            }
        }

        curl_easy_getinfo(m_curl_handle, CURLINFO_RESPONSE_CODE, &m_http_code);
        if (m_http_code == 200)
        {
            curl_code = CURLE_OK;
            if (m_notify) 
            {
                m_notify->ProcessCallback(HttpNotify::STATE_SUCCESS, 0, "");
                m_notify->FinishedNotify(true, receive_content);
            }
        }
        else
        {
            const char* err_string = curl_easy_strerror(curl_code);
            curl_code = CURLE_HTTP_POST_ERROR;
            if (m_notify)
            {
                m_notify->ProcessCallback(HttpNotify::STATE_ERROR, 0, 0);
                m_notify->FinishedNotify(false, "");
            }
            receive_header.clear();
            receive_content.clear();
        }

        return curl_code;
    }

    return CURLE_FAILED_INIT;
}

bool HttpRequest::HttpInternal::GetHeader(std::string* header)
{
    if (receive_header.empty()) return false;
    else if (header) *header = receive_header;

    return true;
}

bool HttpRequest::HttpInternal::GetContent(std::string* receive)
{
    if (receive_content.empty()) return false;
    else if (receive) *receive = receive_content;

    return true;
}

下面是demo,演示http请求

//demo cpp
// http_request.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <Windows.h>
#include "HttpRequest.h"

#include <iostream>
#include <string>
#include <fstream>

class RequestObject : public HttpNotify
{
public:
    virtual void FinishedNotify(bool success, const std::string& content)
    {
        if(success)
        {
            std::ofstream outfile;
            outfile.open("baidu.html", std::ios_base::trunc | std::ios_base::binary | std::ios_base::out);
            if (outfile.good())
            {
                outfile.write(content.c_str(), content.size());
            }
        }
    }

    virtual int ProcessCallback(NotifyState state, long size_data, const void* data)
    {
        return size_data;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    RequestObject obj;

    HttpRequest request;
    request.SetRequestUrl("http://www.baidu.com");
    //request.SetPostMessage("hello liyanhong");
    request.SetHttpHeader("User-Agent:Mozilla/4.04[en](Win95;I;Nav)");
    request.SetNotifyObject(&obj);

    HANDLE hRequest = request.Perform(HttpRequest::REQUEST_ASYNC);
    if (hRequest)
    {
         Sleep(5000);

        long http_code;
        if(request.GetHttpCode(hRequest, &http_code))
            std::cout << "http code: " << http_code << std::endl;
        
        std::string header;
        if(request.GetReceiveHeader(hRequest, &header))
        {
            std::cout << header << std::endl;
        }

        HttpRequest::Close(hRequest);
    }

    return 0;
}

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。