libcurl的封装,支持同步异步请求,支持多线程下载,支持https

最近在做一个项目,需要用到http get post等

需求分析需要做到同步和异步,异步请求的返回以可选的回调通知的方式进行。

本人以Linux为例,一步一步的来实现。

  1. 配置并且编译libcurl
    我以在Linux底下的交叉编译举例。
    libcurl源码下载: http://curl.haxx.se/download.html
    配置libcurl支持https和zlib压缩,必须需要openssl和zlib库
    openssl库源码下载: http://www.openssl.org/source/。下载1.02a以上的版本,避开心脏出血漏洞。
    zlib源码下载:http://www.zlib.net/。下载最新版本代码。
    新建文件夹carbon。源码解压至目录carbon。

    1.1 配置openssl并且编译
    配置和编译脚本:
      1 #!/bin/bash
      2 # Cross-compile environment for Android on ARMv7 and x86
      3 #
      4 # Contents licensed under the terms of the OpenSSL license
      5 # http://www.openssl.org/source/license.html
      6 #
      7 # See http://wiki.openssl.org/index.php/FIPS_Library_and_Android
      8 #   and http://wiki.openssl.org/index.php/Android
      9 
     10 #####################################################################
     11 
     12 # Set ANDROID_NDK_ROOT to you NDK location. For example,
     13 # /opt/android-ndk-r8e or /opt/android-ndk-r9. This can be done in a
     14 # login script. If ANDROID_NDK_ROOT is not specified, the script will
     15 # try to pick it up with the value of _ANDROID_NDK_ROOT below. If
     16 # ANDROID_NDK_ROOT is set, then the value is ignored.
     17 # _ANDROID_NDK="android-ndk-r8e"
     18 #_ANDROID_NDK="android-ndk-r9"
     19 _ANDROID_NDK="android-ndk-r10"
     20 ANDROID_NDK_ROOT=$HOME/ndk/android-ndk-r10d
     21 # Set _ANDROID_EABI to the EABI you want to use. You can find the
     22 # list in $ANDROID_NDK_ROOT/toolchains. This value is always used.
     23 # _ANDROID_EABI="x86-4.6"
     24 # _ANDROID_EABI="arm-linux-androideabi-4.6"
     25 _ANDROID_EABI="arm-linux-androideabi-4.8"
     26 export ROOTDIR="${PWD}"
     27 
     28 # Set _ANDROID_ARCH to the architecture you are building for.
     29 # This value is always used.
     30 # _ANDROID_ARCH=arch-x86
     31 _ANDROID_ARCH=arch-arm
     32 
     33 # Set _ANDROID_API to the API you want to use. You should set it
     34 # to one of: android-14, android-9, android-8, android-14, android-5
     35 # android-4, or android-3. You can't set it to the latest (for
     36 # example, API-17) because the NDK does not supply the platform. At
     37 # Android 5.0, there will likely be another platform added (android-22?).
     38 # This value is always used.
     39 # _ANDROID_API="android-14"
     40 # _ANDROID_API="android-18"
     41 # _ANDROID_API="android-19"
     42 _ANDROID_API="android-5"
     43 
     44 #####################################################################
     45 
     46 # If the user did not specify the NDK location, try and pick it up.
     47 # We expect something like ANDROID_NDK_ROOT=/opt/android-ndk-r8e
     48 # or ANDROID_NDK_ROOT=/usr/local/android-ndk-r8e.
     49 
     50 if [ -z "$ANDROID_NDK_ROOT" ]; then
     51 
     52   _ANDROID_NDK_ROOT=""
     53   if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "/usr/local/$_ANDROID_NDK" ]; then
     54     _ANDROID_NDK_ROOT="/usr/local/$_ANDROID_NDK"
     55   fi
     56 
     57   if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "/opt/$_ANDROID_NDK" ]; then
     58     _ANDROID_NDK_ROOT="/opt/$_ANDROID_NDK"
     59   fi
     60 
     61   if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "$HOME/$_ANDROID_NDK" ]; then
     62     _ANDROID_NDK_ROOT="$HOME/$_ANDROID_NDK"
     63   fi
     64 
     65   if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "$PWD/$_ANDROID_NDK" ]; then
     66     _ANDROID_NDK_ROOT="$PWD/$_ANDROID_NDK"
     67   fi
     68 
     69   # If a path was set, then export it
     70   if [ ! -z "$_ANDROID_NDK_ROOT" ] && [ -d "$_ANDROID_NDK_ROOT" ]; then
     71     export ANDROID_NDK_ROOT="$_ANDROID_NDK_ROOT"
     72   fi
     73 fi
     74 
     75 # Error checking
     76 # ANDROID_NDK_ROOT should always be set by the user (even when not running this script)
     77 # http://groups.google.com/group/android-ndk/browse_thread/thread/a998e139aca71d77
     78 if [ -z "$ANDROID_NDK_ROOT" ] || [ ! -d "$ANDROID_NDK_ROOT" ]; then
     79   echo "Error: ANDROID_NDK_ROOT is not a valid path. Please edit this script."
     80   # echo "$ANDROID_NDK_ROOT"
     81   # exit 1
     82 fi
     83 
     84 # Error checking
     85 if [ ! -d "$ANDROID_NDK_ROOT/toolchains" ]; then
     86   echo "Error: ANDROID_NDK_ROOT/toolchains is not a valid path. Please edit this script."
     87   # echo "$ANDROID_NDK_ROOT/toolchains"
     88   # exit 1
     89 fi
     90 
     91 # Error checking
     92 if [ ! -d "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI" ]; then
     93   echo "Error: ANDROID_EABI is not a valid path. Please edit this script."
     94   # echo "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI"
     95   # exit 1
     96 fi
     97 
     98 #####################################################################
     99 
    100 # Based on ANDROID_NDK_ROOT, try and pick up the required toolchain. We expect something like:
    101 # /opt/android-ndk-r83/toolchains/arm-linux-androideabi-4.7/prebuilt/linux-x86_64/bin
    102 # Once we locate the toolchain, we add it to the PATH. Note: this is the 'hard way' of
    103 # doing things according to the NDK documentation for Ice Cream Sandwich.
    104 # https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html
    105 
    106 ANDROID_TOOLCHAIN=""
    107 for host in "linux-x86_64" "linux-x86" "darwin-x86_64" "darwin-x86"
    108 do
    109   if [ -d "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI/prebuilt/$host/bin" ]; then
    110     ANDROID_TOOLCHAIN="$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI/prebuilt/$host/bin"
    111     break
    112   fi
    113 done
    114 
    115 # Error checking
    116 if [ -z "$ANDROID_TOOLCHAIN" ] || [ ! -d "$ANDROID_TOOLCHAIN" ]; then
    117   echo "Error: ANDROID_TOOLCHAIN is not valid. Please edit this script."
    118   # echo "$ANDROID_TOOLCHAIN"
    119   # exit 1
    120 fi
    121 
    122 case $_ANDROID_ARCH in
    123     arch-arm)      
    124       ANDROID_TOOLS="arm-linux-androideabi-gcc arm-linux-androideabi-ranlib arm-linux-androideabi-ld"
    125       ;;
    126     arch-x86)      
    127       ANDROID_TOOLS="i686-linux-android-gcc i686-linux-android-ranlib i686-linux-android-ld"
    128       ;;      
    129     *)
    130       echo "ERROR ERROR ERROR"
    131       ;;
    132 esac
    133 
    134 for tool in $ANDROID_TOOLS
    135 do
    136   # Error checking
    137   if [ ! -e "$ANDROID_TOOLCHAIN/$tool" ]; then
    138     echo "Error: Failed to find $tool. Please edit this script."
    139     # echo "$ANDROID_TOOLCHAIN/$tool"
    140     # exit 1
    141   fi
    142 done
    143 
    144 # Only modify/export PATH if ANDROID_TOOLCHAIN good
    145 if [ ! -z "$ANDROID_TOOLCHAIN" ]; then
    146   export ANDROID_TOOLCHAIN="$ANDROID_TOOLCHAIN"
    147   export PATH="$ANDROID_TOOLCHAIN":"$PATH"
    148 fi
    149 
    150 #####################################################################
    151 
    152 # For the Android SYSROOT. Can be used on the command line with --sysroot
    153 # https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html
    154 export ANDROID_SYSROOT="$ANDROID_NDK_ROOT/platforms/$_ANDROID_API/$_ANDROID_ARCH"
    155 export SYSROOT="$ANDROID_SYSROOT"
    156 export NDK_SYSROOT="$ANDROID_SYSROOT"
    157 
    158 # Error checking
    159 if [ -z "$ANDROID_SYSROOT" ] || [ ! -d "$ANDROID_SYSROOT" ]; then
    160   echo "Error: ANDROID_SYSROOT is not valid. Please edit this script."
    161   # echo "$ANDROID_SYSROOT"
    162   # exit 1
    163 fi
    164 
    165 #####################################################################
    166 
    167 # If the user did not specify the FIPS_SIG location, try and pick it up
    168 # If the user specified a bad location, then try and pick it up too.
    169 if [ -z "$FIPS_SIG" ] || [ ! -e "$FIPS_SIG" ]; then
    170 
    171   # Try and locate it
    172   _FIPS_SIG=""
    173   if [ -d "/usr/local/ssl/$_ANDROID_API" ]; then
    174     _FIPS_SIG=`find "/usr/local/ssl/$_ANDROID_API" -name incore`
    175   fi
    176 
    177   if [ ! -e "$_FIPS_SIG" ]; then
    178     _FIPS_SIG=`find $PWD -name incore`
    179   fi
    180 
    181   # If a path was set, then export it
    182   if [ ! -z "$_FIPS_SIG" ] && [ -e "$_FIPS_SIG" ]; then
    183     export FIPS_SIG="$_FIPS_SIG"
    184   fi
    185 fi
    186 
    187 # Error checking. Its OK to ignore this if you are *not* building for FIPS
    188 if [ -z "$FIPS_SIG" ] || [ ! -e "$FIPS_SIG" ]; then
    189   echo "Error: FIPS_SIG does not specify incore module. Please edit this script."
    190   # echo "$FIPS_SIG"
    191   # exit 1
    192 fi
    193 
    194 #####################################################################
    195 
    196 # Most of these should be OK (MACHINE, SYSTEM, ARCH). RELEASE is ignored.
    197 export MACHINE=armv7
    198 export RELEASE=2.6.37
    199 export SYSTEM=android
    200 export ARCH=arm
    201 export CROSS_COMPILE="arm-linux-androideabi-"
    202 
    203 if [ "$_ANDROID_ARCH" == "arch-x86" ]; then
    204     export MACHINE=i686
    205     export RELEASE=2.6.37
    206     export SYSTEM=android
    207     export ARCH=x86
    208     export CROSS_COMPILE="i686-linux-android-"
    209 fi
    210 
    211 # For the Android toolchain
    212 # https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html
    213 export ANDROID_SYSROOT="$ANDROID_NDK_ROOT/platforms/$_ANDROID_API/$_ANDROID_ARCH"
    214 export SYSROOT="$ANDROID_SYSROOT"
    215 export NDK_SYSROOT="$ANDROID_SYSROOT"
    216 export ANDROID_NDK_SYSROOT="$ANDROID_SYSROOT"
    217 export ANDROID_API="$_ANDROID_API"
    218 
    219 # CROSS_COMPILE and ANDROID_DEV are DFW (Don't Fiddle With). Its used by OpenSSL build system.
    220 # export CROSS_COMPILE="arm-linux-androideabi-"
    221 export ANDROID_DEV="$ANDROID_NDK_ROOT/platforms/$_ANDROID_API/$_ANDROID_ARCH/usr"
    222 export HOSTCC=gcc
    223 
    224 VERBOSE=1
    225 if [ ! -z "$VERBOSE" ] && [ "$VERBOSE" != "0" ]; then
    226   echo "ANDROID_NDK_ROOT: $ANDROID_NDK_ROOT"
    227   echo "ANDROID_ARCH: $_ANDROID_ARCH"
    228   echo "ANDROID_EABI: $_ANDROID_EABI"
    229   echo "ANDROID_API: $ANDROID_API"
    230   echo "ANDROID_SYSROOT: $ANDROID_SYSROOT"
    231   echo "ANDROID_TOOLCHAIN: $ANDROID_TOOLCHAIN"
    232   echo "FIPS_SIG: $FIPS_SIG"
    233   echo "CROSS_COMPILE: $CROSS_COMPILE"
    234   echo "ANDROID_DEV: $ANDROID_DEV"
    235 fi
    236 
    237 cd openssl
    238 if [ $# -gt 0 ]; then
    239 perl -pi -e 's/install: all install_docs install_sw/install: install_docs install_sw/g' Makefile.org
    240 ./config -DOPENSSL_NO_HEARTBEATS no-shared no-ssl2 no-ssl3 no-comp no-hw no-engine --openssldir=${ROOTDIR}/build/openssl
    241 fi
    242 make depend
    243 make && make install
    openssl configure

    1.2 配置zlib并且编译
    配置脚本:

     1 #!/bin/sh
     2 
     3 export ROOTDIR="${PWD}"
     4 cd zlib/
     5 
     6 export CROSS_COMPILE="arm-linux-androideabi"
     7 export CPPFLAGS="-fPIC"
     8 export CFLAGS="-fPIC"
     9 export AR=${CROSS_COMPILE}-ar
    10 export AS=${CROSS_COMPILE}-as
    11 export LD=${CROSS_COMPILE}-ld
    12 export RANLIB=${CROSS_COMPILE}-ranlib
    13 export CC=${CROSS_COMPILE}-gcc
    14 export CXX=${CROSS_COMPILE}-g++
    15 export NM=${CROSS_COMPILE}-nm
    16 
    17 ./configure --prefix=${ROOTDIR}/build/zlib --static
    zlib configure

    配置成功之后,cd进代码目录执行make && make install命令即可

    1.3 配置libcurl并且编译

    配置脚本:
     1 #!/bin/sh
     2 
     3 export ROOTDIR="${PWD}"
     4 cd curl-7.42.1/
     5 
     6 export CROSS_COMPILE="arm-linux-androideabi"
     7 export CPPFLAGS="-fPIC -I${ROOTDIR}/build/openssl/include -I${ROOTDIR}/build/zlib/include"
     8 export CFLAGS="-fPIC -I${ROOTDIR}/build/openssl/include -I${ROOTDIR}/build/zlib/include"
     9 
    10 export LDFLAGS="-L${ROOTDIR}/build/openssl/lib -L${ROOTDIR}/build/zlib/lib"
    11 export LIBS="-lssl -lcrypto -lz"
    12 
    13 export AR=${CROSS_COMPILE}-ar
    14 export AS=${CROSS_COMPILE}-as
    15 export LD=${CROSS_COMPILE}-ld
    16 export RANLIB=${CROSS_COMPILE}-ranlib
    17 export CC=${CROSS_COMPILE}-gcc
    18 export CXX=${CROSS_COMPILE}-g++
    19 export NM=${CROSS_COMPILE}-nm
    20 
    21 ./configure --prefix=${ROOTDIR}/build/curl --target=${CROSS_COMPILE} --host=${CROSS_COMPILE} --build=i686-linux --enable-static=libcurl.a --enable-shared=libcurl.so --enable-symbol-hiding --enable-optimize --enable-ftp --enable-http --enable-file  --enable-proxy --enable-tftp --enable-smtp --enable-telnet --enable-cookies --enable-ipv6 --with-ssl --with-zlib --without-libssh2 --with-random=/dev/urandom
    libcurl configure

    配置成功之后,cd进代码目录执行make && make install命令即可

    本配置使用的是android的ndk工具链gcc 4.8
    在配置openssl时,指定了ANDROID_NDK_ROOT的值为ndk的路径,可以参看脚本的值进行对应的设置
    可以在ndk目录的build/tools目录找到make-standalone-toolchain.sh文件,执行make-standalone-toolchain.sh --help --help来查看帮助
    构建自己的ndk gcc工具链,最后将生成的工具链路径加入进环境变量PATH即可

  2. 封装libcurl库
    代码使用C++封装,并且使用了C++11的特性,编译时需要指定-std=c++11
    头文件:
      1 #ifndef __HTTP_REQUEST_H
      2 #define __HTTP_REQUEST_H
      3 
      4 
      5 #include <string>
      6 #include <map>
      7 #include <memory>
      8 #include <functional>
      9 #include <vector>
     10 
     11 //************************************
     12 // Usage:    
     13 // class MyResultClass
     14 // {
     15 // public:
     16 //     MyResultClass() : m_request_finished(false) { }
     17 //     ~MyResultClass() { }
     18 // 
     19 // public:
     20 //     void MyRequestResultCallback(int id, bool success, const std::string& data)
     21 //     {
     22 //       if (success)
     23 //       {
     24 //        std::ofstream outfile;
     25 //        outfile.open("baidu.html", std::ios_base::binary | std::ios_base::trunc);
     26 //        if (outfile.good()) outfile.write(data.c_str(), data.size());
     27 //       }
     28 //       m_request_finished = true;
     29 //     }
     30 //     bool IsRequestFinish(void) { return m_request_finished; }
     31 // private:
     32 //     bool m_request_finished;
     33 // };
     34 //
     35 // MyResultClass mc;
     36 // HttpRequest request;
     37 // request.SetRequestUrl("http://www.baidu.com");
     38 // request.SetResultCallback(std::bind(&MyResultClass::MyRequestResultCallback, &mc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
     39 // request.SetRequestHeader("User-Agent:Mozilla/4.04[en](Win95;I;Nav)");
     40 // HANDLE hRequest = request.PerformRequest(HttpRequest::REQUEST_ASYNC);
     41 // if (hRequest)
     42 // {
     43 //     while (mc.IsRequestFinish() == false) Sleep(300);
     44 //     long http_code;
     45 //     if (request.GetHttpCode(hRequest, &http_code))
     46 //       std::cout << "http code: " << http_code << std::endl;
     47 //     std::string header;
     48 //     if (request.GetReceiveHeader(hRequest, &header))
     49 //       std::cout << header << std::endl;
     50 //     HttpRequest::Close(hRequest);
     51 // }
     52 // /*recommended HttpRequest::Close(hRequest) while doing async request job and dont need request handle anymore*/
     53 //************************************
     54 
     55 class HttpLock;
     56 
     57 #ifndef _WIN32
     58 typedef void* HANDLE;
     59 #endif
     60 
     61 class HttpRequest
     62 {
     63 public:
     64     typedef enum {
     65         REQUEST_SYNC,
     66         REQUEST_ASYNC,
     67     }RequestType;
     68 
     69     typedef enum {
     70         REQUEST_OK,
     71         REQUEST_INVALID_OPT,
     72         REQUEST_PERFORM_ERROR,
     73         REQUEST_OPENFILE_ERROR,
     74         REQUEST_INIT_ERROR,
     75     }RequestResult;
     76 
     77     //int id, bool success, const std::string& data
     78     typedef std::function<void(int, bool, const std::string&)> ResultCallback;
     79 
     80     friend class HttpHelper;
     81 
     82     HttpRequest();
     83     ~HttpRequest();
     84 
     85     
     86     int SetRetryTimes(int retry_times = s_kRetryCount);
     87     int SetRequestId(int id);
     88     int SetRequestTimeout(long time_out = 0);
     89     int SetRequestUrl(const std::string& url);
     90 
     91     //************************************
     92     // Method:    SetMovedUrl
     93     // FullName:  HttpRequest::SetMovedUrl
     94     // Access:    public 
     95     // Returns:   int
     96     // Description: set http redirect follow location
     97     // Parameter: bool get_moved_url -- true means redirect http url
     98     //************************************
     99     int SetMovedUrl(bool get_moved_url);
    100 
    101     int SetPostData(const std::string& message);
    102     int SetPostData(const void* data, unsigned int size);
    103 
    104     //************************************
    105     // Method:    SetRequestHeader
    106     // FullName:  HttpRequest::SetRequestHeader
    107     // Access:    public 
    108     // Returns:   int
    109     // Description: set http request header, for example : Range:bytes=554554- 
    110     // Parameter: std::map<std::string, std::string>&
    111     // Parameter: std::string> & headers
    112     //************************************
    113     int SetRequestHeader(const std::map<std::string, std::string>& headers);
    114     int SetRequestHeader(const std::string& header);
    115 
    116     int SetRequestProxy(const std::string& proxy, long proxy_port);
    117 
    118 
    119     int SetResultCallback(ResultCallback rc);
    120 
    121     HANDLE PerformRequest(RequestType request_type);
    122     static void Close(HANDLE request_handle);
    123 
    124     static bool GetHttpCode(HANDLE request_handle, long* http_code);
    125     static bool GetReceiveHeader(HANDLE request_handle, std::string* header);
    126     static bool GetReceiveContent(HANDLE request_handle, std::string* receive);
    127     static bool GetErrorString(HANDLE request_handle, std::string* error_string);
    128 
    129 protected:
    130 
    131     class RequestHelper {
    132     public:
    133         RequestHelper();
    134         ~RequestHelper();
    135 
    136         friend class HttpRequest;
    137         friend class HttpHelper;
    138 
    139         int      SetRetryTimes(int retry_times) { m_retry_times = retry_times; return REQUEST_OK; }
    140 
    141         int      SetRequestTimeout(long time_out = 0);
    142         int      SetRequestUrl(const std::string& url);
    143         int      SetMovedUrl(bool get_moved_url);
    144         int      SetPostData(const void* data, unsigned int size);
    145         int      SetRequestHeader(const std::string& header);
    146         int      SetRequestProxy(const std::string& proxy, long proxy_port);
    147 
    148         int      SetResultCallback(ResultCallback rc);
    149 
    150         int      Perform();
    151 
    152         long     GetHttpCode() { return m_http_code; }
    153         bool     GetHeader(std::string* header);
    154         bool     GetContent(std::string* receive);
    155         bool     GetErrorString(std::string* error_string);
    156 
    157         bool     SelfClose(void) { return m_close_self; }
    158 
    159     protected:
    160         void    ReqeustResultDefault(int id, bool success, const std::string& data);
    161 
    162     private:
    163         HANDLE       m_curl_handle;
    164         HANDLE       m_http_headers;
    165 #ifdef _WIN32
    166         HANDLE       m_perform_thread;
    167 #else
    168         pthread_t    m_perform_thread;
    169 #endif
    170 
    171         int         m_retry_times;
    172         int         m_id;
    173         bool        m_close_self;
    174         bool        m_is_running;
    175         long        m_http_code;
    176 
    177         std::string     m_receive_content;
    178         std::string     m_receive_header;
    179         std::string     m_error_string;
    180         char*               m_post_data;
    181 
    182         ResultCallback  m_result_callback;
    183     };
    184 
    185 private:
    186     std::shared_ptr<RequestHelper>  m_request_handle;
    187     static const int               s_kRetryCount = 3;
    188 };
    189 
    190 //************************************
    191 // Usage:    HttpDownloader
    192 // class DownCallbackClass
    193 // {
    194 // public:
    195 //     DownCallbackClass() :m_down_finished(false) {}
    196 //     ~DownCallbackClass() {}
    197 // public:
    198 //     void DownResultCallback(int id, bool success, const std::string& data)
    199 //     {
    200 //       m_down_finished = true;
    201 //     }
    202 //     int down_callback(double total_size, double downloaded_size, void* userdata)
    203 //     {
    204 //       long tmp = static_cast<long>(downloaded_size / total_size * 100);
    205 //      printf("\r下载进度%d", tmp);
    206 //      return 0;
    207 //     }
    208 //     bool IsDownFinished(void) { return m_down_finished; }
    209 // private:
    210 //     bool m_down_finished;
    211 // };
    212 // HttpDownloader download;
    213 // DownCallbackClass dc;
    214 // const char* down_url = "http://dlsw.baidu.com/sw-search-sp/soft/71/10998/OfflineBaiduPlayer_151_V4.1.2.263.1432003947.exe";
    215 // const char* down_file = "BaiduPlayer.exe";
    216 // 
    217 // download.SetDownloadUrl(down_url);
    218 // download.SetProgressCallback(std::bind(&DownCallbackClass::down_callback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    219 // download.SetResultCallback(std::bind(&DownCallbackClass::DownResultCallback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    220 // download.DownloadFile(down_file);
    221 // HANDLE hDownload = download.StartDownload(HttpDownloader::DOWN_ASYNC);
    222 // if (hDownload)
    223 // {
    224 //     while (dc.IsDownFinished() == false) Sleep(300); 
    225 //     //to do download finish clean up
    226 //     HttpDownloader::Close(hDownload);
    227 // }
    228 //************************************
    229 
    230 class HttpDownloader
    231 {
    232 public:
    233     typedef enum {
    234         DOWN_SYNC,
    235         DOWN_ASYNC,
    236     }DownType;
    237 
    238     //double total_size, double downloaded_size, void* userdata
    239     typedef std::function<int(double, double, void*)> ProgressCallback;
    240     //int id, bool success, const std::string& data
    241     typedef std::function<void(int, bool, const std::string&)> ResultCallback;
    242 
    243     friend class HttpHelper;
    244 
    245     HttpDownloader();
    246     ~HttpDownloader();
    247 
    248     int         SetRequestProxy(const std::string& proxy, long proxy_port);
    249     int         SetRetryTimes(int retry_times = s_kRetryCount);
    250     int         SetTimeout(long time_out = 0);
    251     int         SetDownloadUrl(const std::string& url);
    252     int         SetUserData(void* userdata);
    253     int         SetRequestId(int id);
    254     int         SetProgressCallback(ProgressCallback pc);
    255     int         SetResultCallback(ResultCallback rc);
    256 
    257     int         DownloadFile(const std::string& file_name, int thread_count = 5);
    258     HANDLE      StartDownload(DownType down_type);
    259     static bool CancelDownload(HANDLE handle);
    260     static void Close(HANDLE handle);
    261 
    262     static bool        GetHttpCode(HANDLE handle, long* http_code);
    263     static bool        GetReceiveHeader(HANDLE handle, std::string* header);
    264     static bool        GetErrorString(HANDLE handle, std::string* error_string);
    265     static void*       GetUserData(HANDLE handle);
    266 
    267 protected:
    268 
    269     class DownloadHelper {
    270     public:
    271         typedef struct tThreadChunk
    272         {
    273             FILE*       _fp;
    274             long        _startidx;
    275             long        _endidx;
    276 
    277             DownloadHelper*     _download;
    278         }ThreadChunk;
    279 
    280         DownloadHelper();
    281         ~DownloadHelper();
    282 
    283         friend class HttpDownloader;
    284         friend class HttpHelper;
    285         friend ThreadChunk;
    286 
    287         void     SetRetryTimes(int retry_times) { m_retry_times = retry_times; }
    288         void      SetRequestId(int id) { m_id = id;  }
    289         int      SetTimeout(long time_out = 0);
    290         int      SetRequestUrl(const std::string& url);
    291         int      SetRequestProxy(const std::string& proxy, long proxy_port);
    292 
    293         void     SetUserData(void *userdata) { m_userdata = userdata; }
    294         int      SetProgressCallback(ProgressCallback pc);
    295         int      SetResultCallback(ResultCallback rc);
    296         int      SetDownloadFile(const std::string& file_name);
    297         int      SetDownloadThreadCount(int thread_count);
    298 
    299         int      Perform();
    300 
    301         int      GetHttpCode() { return m_http_code; }
    302         bool     GetHeader(std::string* header);
    303         bool     GetErrorString(std::string* error_string);
    304         bool     SelfClose(void) { return m_close_self; }
    305         void*    GetUserData(void) { return m_userdata; }
    306 
    307     protected:
    308         int      DownloadDefaultCallback(double total_size, double downloaded_size, void* userdata);
    309         void     ResultDefaultCallback(int id, bool success, const std::string& data);
    310         double   GetDownloadFileSize();
    311         int      DoDownload(ThreadChunk* thread_chunk);
    312         int      SplitDownloadCount(double down_size);
    313         void  Reset(void);
    314 
    315     private:
    316 #ifdef _WIN32
    317         HANDLE        m_perform_thread;
    318 #else
    319         pthread_t     m_perform_thread;
    320 #endif
    321 
    322         int          m_retry_times;
    323         int          m_thread_count;
    324         int          m_id;
    325         long         m_time_out;
    326 
    327         std::string  m_file_path;
    328         std::string  m_url;
    329         std::string  m_http_proxy;
    330         std::string  m_receive_header;
    331         std::string  m_error_string;
    332 
    333         bool          m_close_self;
    334         bool            m_multi_download;
    335         bool         m_download_fail;
    336         bool          m_is_running;
    337         bool         m_is_cancel;
    338         void*        m_userdata;
    339         long         m_http_code;
    340         long         m_proxy_port;
    341         double       m_total_size;
    342         double       m_downloaded_size;
    343 
    344         std::shared_ptr<HttpLock> m_httplock;
    345         ProgressCallback  m_download_callback;
    346         ResultCallback    m_result_callback;
    347     };
    348 
    349 private:
    350     std::shared_ptr<DownloadHelper>    m_request_handle;
    351 
    352     static const int          s_kRetryCount = 3;
    353     static const int          s_kThreadCount = 4;
    354 };
    355 
    356 #endif  /*__HTTP_REQUEST_H*/
    HttpRequest.h

    实现文件:

       1 //  [5/11/2015 Carbon]
       2 /*
       3                                               _ooOoo_
       4                                           o888888888o              
       5                                          888    " . "   888
       6                                          (|        -_-       |)                  
       7                                        O\          =         /O      
       8                                     ____/`     ---   '\____    
       9                                   .'  \\|                     |//  `.              
      10                               /  \\|||          :          |||//  \             
      11                            /  _|||||        -:-         |||||-  \           
      12                          |   | \\\              -               /// |   |         
      13                          | \_|             ''\---/''              |_/ |        
      14                           \  .-\__             `-`              __/-. /        
      15                      _____`. .'           /--.--\            `. . _____        
      16                   ."" '<  `.___\_        <|>          _/___.'  >' "".     
      17                  | | :  `- \`.;`             \ _ /              `;.`/ - ` : | |     
      18                   \  \ `-.              \_ __\ /__ _/              .-` /  /   
      19     ========`-.____`-.___\_____/___.-`____.-'========   
      20                                                 `=---='
      21 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      22                        佛祖保佑       永无BUG
      23 */   
      24 #ifdef _WIN32
      25 #include "stdafx.h"
      26 #else
      27 #include <pthread.h>
      28 #include <stdio.h>
      29 #include <unistd.h>
      30 #endif
      31 
      32 #include "./curl/curl.h"        //libcurl interface
      33 #include "HttpRequest.h"   //HttpRequest class
      34 
      35 #include <list>
      36 #include <regex>
      37 #include <sstream>
      38 
      39 
      40 #ifndef _WIN32
      41 typedef unsigned long DWORD;
      42 #define INVALID_HANDLE_VALUE    (void*)0xffffffff
      43 #define TRUE    1
      44 #define FALSE   0
      45 #endif  //#ifndef _WIN32
      46 
      47 class HttpLock
      48 {
      49 public:
      50 #ifdef _WIN32
      51     HttpLock() { InitializeCriticalSection(&m_cs); }
      52     ~HttpLock() { DeleteCriticalSection(&m_cs); }
      53 
      54     void Lock() { EnterCriticalSection(&m_cs); }
      55     void UnLock() { LeaveCriticalSection(&m_cs); }
      56 #else
      57     HttpLock() { pthread_mutex_init(&m_lock, NULL); }
      58     ~HttpLock() { pthread_mutex_destroy(&m_lock); }
      59 
      60     int Lock(){ return pthread_mutex_lock(&m_lock); }
      61     int UnLock() { return pthread_mutex_unlock(&m_lock); }
      62 #endif
      63 
      64 private:
      65 #ifdef _WIN32
      66     CRITICAL_SECTION m_cs;
      67 #else
      68     pthread_mutex_t    m_lock;
      69 #endif
      70 };
      71 
      72 class DoHttpLock
      73 {
      74 public:
      75     DoHttpLock(std::shared_ptr<HttpLock> & lock)
      76         : m_lock(lock)
      77     {
      78         m_lock->Lock();
      79     }
      80 
      81     ~DoHttpLock()
      82     {
      83         m_lock->UnLock();
      84     }
      85 
      86 private:
      87     std::shared_ptr<HttpLock> m_lock;
      88 };
      89 
      90 class HttpHelper {
      91 protected:
      92     HttpHelper()
      93     {
      94         curl_global_init(CURL_GLOBAL_DEFAULT);
      95 
      96         s_share_handle = curl_share_init();
      97         curl_share_setopt(s_share_handle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
      98     }
      99 
     100 public:
     101     ~HttpHelper()
     102     {
     103         curl_share_cleanup(s_share_handle);
     104         curl_global_cleanup();
     105 
     106         s_async_requests.clear();
     107         s_async_downloads.clear();
     108     }
     109 
     110     static HttpHelper& Instance()
     111     {
     112         static HttpHelper the_single_instance;
     113         s_id++;
     114         return the_single_instance;
     115     }
     116 
     117     static void set_share_handle(CURL* curl_handle)
     118     {
     119         curl_easy_setopt(curl_handle, CURLOPT_SHARE, s_share_handle);
     120         curl_easy_setopt(curl_handle, CURLOPT_DNS_CACHE_TIMEOUT, 60 * 5);
     121     }
     122 
     123     static std::list< std::shared_ptr<HttpRequest::RequestHelper> > s_async_requests;
     124     static std::list< std::shared_ptr<HttpDownloader::DownloadHelper> > s_async_downloads;
     125 
     126     static int s_id;
     127     static std::shared_ptr<HttpLock>       s_request_lock;
     128     static std::shared_ptr<HttpLock>       s_download_lock;
     129     static CURLSH*        s_share_handle;
     130 
     131 #ifdef _WIN32
     132     static DWORD WINAPI RequestThread(LPVOID param)
     133 #else
     134     static void* RequestThread(void* param)
     135 #endif
     136     {
     137 #ifdef _WIN32
     138         Sleep(10);
     139 #else
     140         usleep(10 * 1000);
     141 #endif
     142 
     143         std::shared_ptr<HttpRequest::RequestHelper>* request = reinterpret_cast<std::shared_ptr<HttpRequest::RequestHelper>*>(param);
     144 
     145         if (request)
     146         {
     147             (*request)->Perform();
     148             if ((*request)->SelfClose())
     149             {
     150                 DoHttpLock http_lock(s_request_lock);
     151                 HttpHelper::s_async_requests.remove(*request);
     152             }
     153 
     154         }
     155 
     156 #ifdef _WIN32
     157         return 1;
     158 #else
     159         return NULL;
     160 #endif
     161     }
     162 
     163     static size_t RetriveHeaderFunction(char *buffer, size_t size, size_t nitems, void *userdata)
     164     {
     165         std::string* receive_header = reinterpret_cast<std::string*>(userdata);
     166         if (receive_header && buffer)
     167         {
     168             receive_header->append(reinterpret_cast<const char*>(buffer), size * nitems);
     169         }
     170 
     171         return nitems * size;
     172     }
     173 
     174     static size_t RetriveContentFunction(char *ptr, size_t size, size_t nmemb, void *userdata)
     175     {
     176         std::string* receive_content = reinterpret_cast<std::string*>(userdata);
     177         if (receive_content && ptr)
     178         {
     179             receive_content->append(reinterpret_cast<const char*>(ptr), size * nmemb);
     180         }
     181 
     182         return nmemb * size;
     183     }
     184 
     185 #ifdef _WIN32
     186     static DWORD WINAPI DownloadThread(LPVOID param)
     187 #else
     188     static void* DownloadThread(void* param)
     189 #endif
     190     {
     191 #ifdef _WIN32
     192         Sleep(10);
     193 #else
     194         usleep(10 * 1000);
     195 #endif
     196 
     197         std::shared_ptr<HttpDownloader::DownloadHelper>* request = reinterpret_cast<std::shared_ptr<HttpDownloader::DownloadHelper>*>(param);
     198 
     199         if (request)
     200         {
     201             (*request)->Perform();
     202 
     203             if ((*request)->SelfClose())
     204             {
     205                 DoHttpLock http_lock(s_download_lock);
     206                 HttpHelper::s_async_downloads.remove(*request);
     207             }
     208 
     209         }
     210 
     211 #ifdef _WIN32
     212         return 1;
     213 #else
     214         return NULL;
     215 #endif
     216     }
     217 
     218     static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
     219     {
     220         HttpDownloader::DownloadHelper::ThreadChunk* thread_chunk = reinterpret_cast<HttpDownloader::DownloadHelper::ThreadChunk*>(userdata);
     221 
     222         if (thread_chunk->_download->m_is_cancel)
     223         {
     224             return 0;
     225         }
     226 
     227         DoHttpLock http_lock(thread_chunk->_download->m_httplock);
     228         size_t written = 0;
     229         int real_size = size * nmemb;
     230         if (thread_chunk->_endidx > 0)
     231         {
     232             if (thread_chunk->_startidx <= thread_chunk->_endidx)
     233             {
     234                 if (thread_chunk->_startidx + real_size > thread_chunk->_endidx)
     235                 {
     236                     real_size = thread_chunk->_endidx - thread_chunk->_startidx + 1;
     237                 }
     238             }
     239         }
     240 
     241         int seek_error = fseek(thread_chunk->_fp, thread_chunk->_startidx, SEEK_SET);
     242         if (seek_error != 0)
     243         {
     244             perror("fseek");
     245         }
     246         else
     247         {
     248             written = fwrite(ptr, 1, real_size, thread_chunk->_fp);
     249         }
     250         thread_chunk->_download->m_downloaded_size += written;
     251         thread_chunk->_startidx += written;
     252 
     253         return written;
     254     }
     255 
     256     static int progress_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
     257     {
     258         HttpDownloader::DownloadHelper::ThreadChunk* thread_chunk = reinterpret_cast<HttpDownloader::DownloadHelper::ThreadChunk*>(clientp);
     259 
     260         DoHttpLock http_lock(thread_chunk->_download->m_httplock);
     261 
     262         double total_size = thread_chunk->_download->m_total_size;
     263         double downloaded_size = thread_chunk->_download->m_downloaded_size;
     264         void* userdata = thread_chunk->_download->m_userdata;
     265         int callback_result = thread_chunk->_download->m_download_callback(total_size, downloaded_size, userdata);
     266 
     267         return callback_result;
     268     }
     269 
     270 #ifdef _WIN32
     271     static DWORD WINAPI DownloadWork(LPVOID param)
     272 #else
     273     static void* DownloadWork(void* param)
     274 #endif
     275     {
     276         HttpDownloader::DownloadHelper::ThreadChunk* thread_chunk = reinterpret_cast<HttpDownloader::DownloadHelper::ThreadChunk*>(param);
     277 
     278 #ifdef _WIN32
     279         return thread_chunk->_download->DoDownload(thread_chunk);
     280 #else
     281         return (void *)(thread_chunk->_download->DoDownload(thread_chunk));
     282 #endif
     283     }
     284 };
     285 
     286 std::list< std::shared_ptr<HttpRequest::RequestHelper> > HttpHelper::s_async_requests;
     287 std::list< std::shared_ptr<HttpDownloader::DownloadHelper> > HttpHelper::s_async_downloads;
     288 int HttpHelper::s_id = 0;
     289 std::shared_ptr<HttpLock> HttpHelper::s_request_lock(new HttpLock);
     290 std::shared_ptr<HttpLock> HttpHelper::s_download_lock(new HttpLock);
     291 CURLSH* HttpHelper::s_share_handle = nullptr;
     292 
     293 HttpRequest::HttpRequest()
     294     : m_request_handle(new HttpRequest::RequestHelper)
     295 {
     296     HttpHelper::Instance();
     297 }
     298 
     299 HttpRequest::~HttpRequest()
     300 {
     301 }
     302 
     303 int HttpRequest::SetRetryTimes(int retry_times)
     304 {
     305     if (m_request_handle)
     306     {
     307         m_request_handle->SetRetryTimes(retry_times);
     308         return REQUEST_OK;
     309     }
     310 
     311     return REQUEST_INIT_ERROR;
     312 }
     313 
     314 int HttpRequest::SetRequestId(int id)
     315 {
     316     if (m_request_handle)
     317     {
     318         m_request_handle->m_id = id;
     319         return REQUEST_OK;
     320     }
     321 
     322     return REQUEST_INIT_ERROR;
     323 }
     324 
     325 int HttpRequest::SetRequestTimeout(long time_out)
     326 {
     327     if (m_request_handle)
     328     {
     329         if (m_request_handle->SetRequestTimeout(time_out) == CURLE_OK)
     330         {
     331             return REQUEST_OK;
     332         }
     333         else
     334         {
     335             return REQUEST_INVALID_OPT;
     336         }
     337     }
     338 
     339     return REQUEST_INIT_ERROR;
     340 }
     341 
     342 int HttpRequest::SetRequestUrl(const std::string& url)
     343 {
     344     if (m_request_handle)
     345     {
     346         if (m_request_handle->SetRequestUrl(url) == CURLE_OK)
     347         {
     348             return REQUEST_OK;
     349         }
     350         else
     351         {
     352             return REQUEST_INVALID_OPT;
     353         }
     354     }
     355 
     356     return REQUEST_INIT_ERROR;
     357 }
     358 
     359 int HttpRequest::SetMovedUrl(bool get_moved_url)
     360 {
     361     if (m_request_handle)
     362     {
     363         if (m_request_handle->SetMovedUrl(get_moved_url) == CURLE_OK)
     364         {
     365             return REQUEST_OK;
     366         }
     367         else
     368         {
     369             return REQUEST_INVALID_OPT;
     370         }
     371     }
     372 
     373     return REQUEST_INIT_ERROR;
     374 }
     375 
     376 int HttpRequest::SetPostData(const std::string& message)
     377 {
     378     return SetPostData(message.c_str(), message.size());
     379 }
     380 
     381 int HttpRequest::SetPostData(const void* data, unsigned int size)
     382 {
     383     if (m_request_handle)
     384     {
     385         if (m_request_handle->SetPostData(data, size) == CURLE_OK)
     386         {
     387             return REQUEST_OK;
     388         }
     389         else
     390         {
     391             return REQUEST_INVALID_OPT;
     392         }
     393     }
     394     return REQUEST_INIT_ERROR;
     395 }
     396 
     397 int HttpRequest::SetRequestHeader(const std::map<std::string, std::string>& headers)
     398 {
     399     if (m_request_handle)
     400     {
     401         for (auto it = headers.begin(); it != headers.end(); ++it)
     402         {
     403             std::string header = it->first;
     404             header += ": ";
     405             header += it->second;
     406             if (m_request_handle->SetRequestHeader(header) != CURLE_OK)
     407             {
     408                 return REQUEST_INVALID_OPT;
     409             }
     410         }
     411         return REQUEST_OK;
     412     }
     413 
     414     return REQUEST_INIT_ERROR;
     415 }
     416 
     417 int HttpRequest::SetRequestHeader(const std::string& header)
     418 {
     419     if (m_request_handle)
     420     {
     421         if (m_request_handle->SetRequestHeader(header) == CURLE_OK)
     422         {
     423             return REQUEST_OK;
     424         }
     425         else
     426         {
     427             return REQUEST_INVALID_OPT;
     428         }
     429     }
     430     return REQUEST_INIT_ERROR;
     431 }
     432 
     433 int HttpRequest::SetRequestProxy(const std::string& proxy, long proxy_port)
     434 {
     435     if (m_request_handle)
     436     {
     437         if (m_request_handle->SetRequestProxy(proxy, proxy_port) == CURLE_OK)
     438         {
     439             return REQUEST_OK;
     440         }
     441         else
     442         {
     443             return REQUEST_INVALID_OPT;
     444         }
     445     }
     446 
     447     return REQUEST_INIT_ERROR;
     448 }
     449 
     450 int HttpRequest::SetResultCallback(ResultCallback rc)
     451 {
     452     if (m_request_handle)
     453     {
     454         m_request_handle->SetResultCallback(rc);
     455         return REQUEST_OK;
     456     }
     457 
     458     return REQUEST_INIT_ERROR;
     459 }
     460 
     461 void HttpRequest::Close(HANDLE request_handle)
     462 {
     463     std::shared_ptr<RequestHelper>* request = (reinterpret_cast<std::shared_ptr<RequestHelper> *>(request_handle));
     464     if (request == INVALID_HANDLE_VALUE || request == nullptr)
     465     {
     466         return;
     467     }
     468 
     469     bool basync = false;
     470 
     471     DoHttpLock http_lock(HttpHelper::s_request_lock);
     472     for (auto it = HttpHelper::s_async_requests.begin(); it != HttpHelper::s_async_requests.end(); ++it)
     473     {
     474         if ((*request) == *it)
     475         {
     476 #ifdef _WIN32
     477             if (WaitForSingleObject((*request)->m_perform_thread, 10) == WAIT_OBJECT_0)
     478 #else
     479             if(pthread_kill((*request)->m_perform_thread, 0) != 0)
     480 #endif
     481             {
     482                 HttpHelper::s_async_requests.remove(*request);
     483             }
     484             else
     485             {
     486                 (*request)->m_close_self = true;
     487             }
     488             basync = true;
     489             break;
     490         }
     491     }
     492 
     493     if (basync == false)
     494     {
     495         //request->reset();
     496     }
     497 }
     498 
     499 HANDLE HttpRequest::PerformRequest(RequestType request_type)
     500 {
     501     if (m_request_handle)
     502     {
     503         if (m_request_handle->m_is_running)
     504         {
     505             return nullptr;
     506         }
     507 
     508         if (request_type == REQUEST_SYNC)
     509         {
     510             m_request_handle->Perform();
     511 
     512             return &m_request_handle;
     513         }
     514         else if (request_type == REQUEST_ASYNC)
     515         {
     516             DoHttpLock http_lock(HttpHelper::s_request_lock);
     517 
     518             HttpHelper::s_async_requests.push_back(m_request_handle);
     519             std::shared_ptr<RequestHelper>& request = HttpHelper::s_async_requests.back();
     520 
     521 #ifdef _WIN32
     522             DWORD thread_id;
     523             HANDLE async_thread = CreateThread(NULL, 0, HttpHelper::RequestThread, &request, 0, &thread_id);
     524             request->m_perform_thread = async_thread;
     525 #else
     526             pthread_create(&(request->m_perform_thread), NULL, HttpHelper::RequestThread, &request);
     527 #endif
     528 
     529             return &request;
     530         }
     531 
     532         return nullptr;
     533     }
     534 
     535     return nullptr;
     536 }
     537 
     538 bool HttpRequest::GetHttpCode(HANDLE request_handle, long* http_code)
     539 {
     540     std::shared_ptr<RequestHelper>* request = reinterpret_cast<std::shared_ptr<RequestHelper>*>(request_handle);
     541     if (request && http_code)
     542     {
     543         *http_code = (*request)->GetHttpCode();
     544         return true;
     545     }
     546 
     547     return false;
     548 }
     549 
     550 bool HttpRequest::GetReceiveHeader(HANDLE request_handle, std::string* header)
     551 {
     552     std::shared_ptr<RequestHelper>* request = reinterpret_cast<std::shared_ptr<RequestHelper>*>(request_handle);
     553     if (request)
     554     {
     555         return (*request)->GetHeader(header);
     556     }
     557 
     558     return false;
     559 }
     560 
     561 bool HttpRequest::GetReceiveContent(HANDLE request_handle, std::string* receive)
     562 {
     563     std::shared_ptr<RequestHelper>* request = reinterpret_cast<std::shared_ptr<RequestHelper>*>(request_handle);
     564     if (request)
     565     {
     566         return (*request)->GetContent(receive);
     567     }
     568 
     569     return false;
     570 }
     571 
     572 bool HttpRequest::GetErrorString(HANDLE request_handle, std::string* error_string)
     573 {
     574     std::shared_ptr<RequestHelper>* request = reinterpret_cast<std::shared_ptr<RequestHelper>*>(request_handle);
     575     if (request)
     576     {
     577         return (*request)->GetErrorString(error_string);
     578     }
     579 
     580     return false;
     581 }
     582 
     583 HttpRequest::RequestHelper::RequestHelper()
     584     : m_curl_handle(nullptr)
     585 #ifdef _WIN32
     586     , m_perform_thread(nullptr)
     587 #else
     588     , m_perform_thread(-1)
     589 #endif
     590     , m_http_headers(nullptr)
     591     , m_close_self(false)
     592     , m_is_running(false)
     593     , m_retry_times(HttpRequest::s_kRetryCount)
     594     , m_http_code(0)
     595     , m_post_data(nullptr)
     596 {
     597     m_result_callback = std::bind(&RequestHelper::ReqeustResultDefault, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
     598     m_id = HttpHelper::s_id;
     599     m_curl_handle = curl_easy_init();
     600     HttpHelper::set_share_handle(m_curl_handle);
     601 }
     602 
     603 HttpRequest::RequestHelper::~RequestHelper()
     604 {
     605     if (m_curl_handle)
     606     {
     607         curl_easy_cleanup(m_curl_handle);
     608     }
     609     if (m_http_headers)
     610     {
     611         curl_slist_free_all(reinterpret_cast<curl_slist*>(m_http_headers));
     612     }
     613     if (m_post_data)
     614     {
     615         delete m_post_data;
     616         m_post_data = nullptr;
     617     }
     618 #ifdef _WIN32
     619     if (m_perform_thread)
     620     {
     621         CloseHandle(m_perform_thread);
     622     }
     623 #endif
     624 }
     625 
     626 int HttpRequest::RequestHelper::SetRequestTimeout(long time_out)
     627 {
     628     if (m_curl_handle)
     629     {
     630         return curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT, 0);
     631     }
     632 
     633     return CURLE_FAILED_INIT;
     634 }
     635 
     636 int HttpRequest::RequestHelper::SetRequestUrl(const std::string& url)
     637 {
     638     if (m_curl_handle)
     639     {
     640         if (url.substr(0, 5) == "https")
     641         {
     642             curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
     643             curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
     644         }
     645 
     646         return curl_easy_setopt(m_curl_handle, CURLOPT_URL, url.c_str());
     647     }
     648 
     649     return CURLE_FAILED_INIT;
     650 }
     651 
     652 int HttpRequest::RequestHelper::SetMovedUrl(bool get_moved_url)
     653 {
     654     if (m_curl_handle)
     655     {
     656         if (get_moved_url)
     657         {
     658             curl_easy_setopt(m_curl_handle, CURLOPT_MAXREDIRS, 5);
     659             return curl_easy_setopt(m_curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
     660         }
     661         else
     662         {
     663             return curl_easy_setopt(m_curl_handle, CURLOPT_FOLLOWLOCATION, 0L);
     664         }
     665     }
     666 
     667     return CURLE_FAILED_INIT;
     668 }
     669 
     670 int HttpRequest::RequestHelper::SetPostData(const void* data, unsigned int size)
     671 {
     672     if (m_curl_handle /*&& data && size > 0*/)
     673     {
     674         CURLcode curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_POST, 1);
     675         if (curl_code == CURLE_OK)
     676         {
     677             if (m_post_data)
     678             {
     679                 delete m_post_data;
     680                 m_post_data = nullptr;
     681             }
     682 
     683             if (size == 0)
     684             {
     685                 curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_POSTFIELDS, "");
     686             }
     687             else
     688             {
     689                 m_post_data = new char[size];
     690                 memcpy(m_post_data, data, size);
     691                 curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_POSTFIELDS, m_post_data);
     692             }
     693         }
     694 
     695         if (curl_code == CURLE_OK)
     696         {
     697             curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_POSTFIELDSIZE, size);
     698         }
     699 
     700         return curl_code;
     701     }
     702 
     703     return CURLE_FAILED_INIT;
     704 }
     705 
     706 int HttpRequest::RequestHelper::SetRequestHeader(const std::string& header)
     707 {
     708     if (m_curl_handle && header.empty() == false)
     709     {
     710         m_http_headers = curl_slist_append(reinterpret_cast<curl_slist*>(m_http_headers), header.c_str());
     711 
     712         return m_http_headers ? CURLE_OK : CURLE_FAILED_INIT;
     713     }
     714 
     715     return CURLE_FAILED_INIT;
     716 }
     717 
     718 int HttpRequest::RequestHelper::SetRequestProxy(const std::string& proxy, long proxy_port)
     719 {
     720     //CURLOPT_PROXY
     721     if (m_curl_handle)
     722     {
     723         CURLcode curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_PROXYPORT, proxy_port);
     724 
     725         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_PROXY, proxy.c_str());
     726 
     727         return curl_code;
     728     }
     729 
     730     return CURLE_FAILED_INIT;
     731 }
     732 
     733 int HttpRequest::RequestHelper::SetResultCallback(ResultCallback rc)
     734 {
     735     m_result_callback = rc;
     736 
     737     return CURLE_OK;
     738 }
     739 
     740 void HttpRequest::RequestHelper::ReqeustResultDefault(int id, bool success, const std::string& data)
     741 {
     742     //default request callback do nothing
     743 }
     744 
     745 int HttpRequest::RequestHelper::Perform()
     746 {
     747     if (m_curl_handle)
     748     {
     749         CURLcode curl_code;
     750         if (m_http_headers)
     751         {
     752             curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_HTTPHEADER, reinterpret_cast<curl_slist*>(m_http_headers));
     753             if (curl_code != CURLE_OK)
     754             {
     755                 return curl_code;
     756             }
     757         }
     758 
     759         m_is_running = true;
     760         m_receive_header.clear();
     761         m_receive_content.clear();
     762 
     763         //set force http redirect
     764         SetMovedUrl(true);
     765 
     766         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_HEADERFUNCTION, HttpHelper::RetriveHeaderFunction);
     767         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_HEADERDATA, &m_receive_header);
     768 
     769         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, HttpHelper::RetriveContentFunction);
     770         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, &m_receive_content);
     771 
     772         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_NOPROGRESS, 1);
     773 
     774         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1);
     775         curl_code = curl_easy_setopt(m_curl_handle, CURLOPT_CONNECTTIMEOUT_MS, 0);
     776 
     777         curl_code = curl_easy_perform(m_curl_handle);
     778         if (curl_code == CURLE_OPERATION_TIMEDOUT)
     779         {
     780             int retry_count = m_retry_times;
     781             while (retry_count > 0)
     782             {
     783                 curl_code = curl_easy_perform(m_curl_handle);
     784                 if (curl_code != CURLE_OPERATION_TIMEDOUT) break;
     785                 retry_count--;
     786             }
     787         }
     788 
     789         curl_easy_getinfo(m_curl_handle, CURLINFO_RESPONSE_CODE, &m_http_code);
     790         if (curl_code == CURLE_OK && m_http_code == 200)
     791         {
     792             m_result_callback(m_id, true, m_receive_content);
     793         }
     794         else
     795         {
     796             const char* err_string = curl_easy_strerror(curl_code);
     797             m_error_string = err_string;
     798             curl_code = CURLE_HTTP_POST_ERROR;
     799             m_result_callback(m_id, false, m_receive_content);
     800         }
     801 
     802         m_is_running = false;
     803 
     804         if (m_http_headers)
     805         {
     806             curl_slist_free_all(reinterpret_cast<curl_slist*>(m_http_headers));
     807             m_http_headers = nullptr;
     808         }
     809 
     810         return curl_code;
     811     }
     812 
     813     return CURLE_FAILED_INIT;
     814 }
     815 
     816 bool HttpRequest::RequestHelper::GetHeader(std::string* header)
     817 {
     818     if (m_receive_header.empty()) return false;
     819     else if (header) *header = m_receive_header;
     820 
     821     return true;
     822 }
     823 
     824 bool HttpRequest::RequestHelper::GetContent(std::string* receive)
     825 {
     826     if (m_receive_content.empty()) return false;
     827     else if (receive) *receive = m_receive_content;
     828 
     829     return true;
     830 }
     831 
     832 bool HttpRequest::RequestHelper::GetErrorString(std::string* error_string)
     833 {
     834     if (m_error_string.empty()) return false;
     835     else if (error_string) *error_string = m_error_string;
     836 
     837     return true;
     838 }
     839 
     840 HttpDownloader::HttpDownloader()
     841     :m_request_handle(new HttpDownloader::DownloadHelper)
     842 {
     843     HttpHelper::Instance();
     844 }
     845 
     846 HttpDownloader::~HttpDownloader()
     847 {
     848 
     849 }
     850 
     851 int HttpDownloader::SetRequestProxy(const std::string& proxy, long proxy_port)
     852 {
     853     if (m_request_handle)
     854     {
     855         if (m_request_handle->SetRequestProxy(proxy, proxy_port) == CURLE_OK)
     856         {
     857             return 0;
     858         }
     859         else
     860         {
     861             return HttpRequest::REQUEST_INVALID_OPT;
     862         }
     863     }
     864 
     865     return HttpRequest::REQUEST_INIT_ERROR;
     866 }
     867 
     868 int HttpDownloader::SetRetryTimes(int retry_times /* = s_kRetryCount */)
     869 {
     870     if (m_request_handle)
     871     {
     872         m_request_handle->SetRetryTimes(retry_times);
     873         return HttpRequest::REQUEST_OK;
     874     }
     875 
     876     return HttpRequest::REQUEST_INIT_ERROR;
     877 }
     878 
     879 int HttpDownloader::SetTimeout(long time_out /* = 0 */)
     880 {
     881     if (m_request_handle)
     882     {
     883         if (m_request_handle->SetTimeout(time_out) == CURLE_OK)
     884         {
     885             return HttpRequest::REQUEST_OK;
     886         }
     887         else
     888         {
     889             return HttpRequest::REQUEST_INVALID_OPT;
     890         }
     891     }
     892 
     893     return HttpRequest::REQUEST_INIT_ERROR;
     894 }
     895 
     896 int HttpDownloader::SetDownloadUrl(const std::string& url)
     897 {
     898     if (m_request_handle)
     899     {
     900         if (m_request_handle->SetRequestUrl(url) == CURLE_OK)
     901         {
     902             return HttpRequest::REQUEST_OK;
     903         }
     904         else
     905         {
     906             return HttpRequest::REQUEST_INVALID_OPT;
     907         }
     908     }
     909 
     910     return HttpRequest::REQUEST_INIT_ERROR;
     911 }
     912 
     913 int HttpDownloader::SetUserData(void* userdata)
     914 {
     915     if (m_request_handle)
     916     {
     917         m_request_handle->SetUserData(userdata);
     918 
     919         return HttpRequest::REQUEST_OK;
     920     }
     921     return HttpRequest::REQUEST_INIT_ERROR;
     922 }
     923 
     924 int HttpDownloader::SetRequestId(int id)
     925 {
     926     if (m_request_handle)
     927     {
     928         m_request_handle->SetRequestId(id);
     929         return HttpRequest::REQUEST_OK;
     930     }
     931 
     932     return HttpRequest::REQUEST_INIT_ERROR;
     933 }
     934 
     935 int HttpDownloader::SetProgressCallback(ProgressCallback pc)
     936 {
     937     if (m_request_handle)
     938     {
     939         m_request_handle->SetProgressCallback(pc);
     940 
     941         return HttpRequest::REQUEST_OK;
     942     }
     943 
     944     return HttpRequest::REQUEST_INIT_ERROR;
     945 }
     946 
     947 int HttpDownloader::SetResultCallback(ResultCallback rc)
     948 {
     949     if (m_request_handle)
     950     {
     951         m_request_handle->SetResultCallback(rc);
     952 
     953         return HttpRequest::REQUEST_OK;
     954     }
     955 
     956     return HttpRequest::REQUEST_INIT_ERROR;
     957 }
     958 
     959 int HttpDownloader::DownloadFile(const std::string& file_name, int thread_count /* = 5 */)
     960 {
     961     if (m_request_handle)
     962     {
     963         m_request_handle->SetDownloadFile(file_name);
     964         m_request_handle->SetDownloadThreadCount(thread_count);
     965     }
     966 
     967     return HttpRequest::REQUEST_INIT_ERROR;
     968 }
     969 
     970 HANDLE HttpDownloader::StartDownload(DownType down_type)
     971 {
     972     if (m_request_handle)
     973     {
     974         if (m_request_handle->m_is_running)
     975         {
     976             return nullptr;
     977         }
     978 
     979         m_request_handle->Reset();
     980 
     981         if (down_type == DOWN_SYNC)
     982         {
     983             m_request_handle->Perform();
     984 
     985             return &m_request_handle;
     986         }
     987         else if (down_type == DOWN_ASYNC)
     988         {
     989             DoHttpLock http_lock(HttpHelper::s_download_lock);
     990             HttpHelper::s_async_downloads.push_back(m_request_handle);
     991             std::shared_ptr<DownloadHelper>& request = HttpHelper::s_async_downloads.back();
     992 
     993 #ifdef _WIN32
     994             DWORD thread_id;
     995             HANDLE async_thread = CreateThread(NULL, 0, HttpHelper::DownloadThread, &request, 0, &thread_id);
     996             request->m_perform_thread = async_thread;
     997 #else
     998             pthread_create(&(request->m_perform_thread), NULL, HttpHelper::DownloadThread, &request);
     999 #endif
    1000 
    1001             return &request;
    1002         }
    1003 
    1004         return nullptr;
    1005     }
    1006 
    1007     return nullptr;
    1008 }
    1009 
    1010 void HttpDownloader::Close(HANDLE handle)
    1011 {
    1012     std::shared_ptr<DownloadHelper>* request = (reinterpret_cast<std::shared_ptr<DownloadHelper> *>(handle));
    1013     if (request == INVALID_HANDLE_VALUE || request == nullptr)
    1014     {
    1015         return;
    1016     }
    1017 
    1018     bool basync = false;
    1019 
    1020     DoHttpLock http_lock(HttpHelper::s_download_lock);
    1021     for (auto it = HttpHelper::s_async_downloads.begin(); it != HttpHelper::s_async_downloads.end(); ++it)
    1022     {
    1023         if ((*request) == *it)
    1024         {
    1025 #ifdef _WIN32
    1026             if (WaitForSingleObject((*request)->m_perform_thread, 10) == WAIT_OBJECT_0)
    1027 #else
    1028             if(pthread_kill((*request)->m_perform_thread, 0) != 0)
    1029 #endif
    1030             {
    1031                 HttpHelper::s_async_downloads.remove(*request);
    1032             }
    1033             else
    1034             {
    1035                 (*request)->m_close_self = true;
    1036             }
    1037             basync = true;
    1038             break;
    1039         }
    1040     }
    1041 
    1042     if (basync == false)
    1043     {
    1044         (*request)->m_is_cancel = true;
    1045         //request->reset();
    1046     }
    1047 }
    1048 
    1049 bool HttpDownloader::CancelDownload(HANDLE handle)
    1050 {
    1051     std::shared_ptr<DownloadHelper>* request = (reinterpret_cast<std::shared_ptr<DownloadHelper> *>(handle));
    1052     if (request == INVALID_HANDLE_VALUE || request == nullptr)
    1053     {
    1054         return false;
    1055     }
    1056 
    1057     (*request)->m_is_cancel = true;
    1058 
    1059     return true;
    1060 }
    1061 
    1062 bool HttpDownloader::GetHttpCode(HANDLE handle, long* http_code)
    1063 {
    1064     std::shared_ptr<DownloadHelper>* request = reinterpret_cast<std::shared_ptr<DownloadHelper>*>(handle);
    1065     if (request && http_code)
    1066     {
    1067         *http_code = (*request)->GetHttpCode();
    1068         return true;
    1069     }
    1070 
    1071     return false;
    1072 }
    1073 
    1074 bool HttpDownloader::GetErrorString(HANDLE handle, std::string* error_string)
    1075 {
    1076     std::shared_ptr<DownloadHelper>* request = reinterpret_cast<std::shared_ptr<DownloadHelper>*>(handle);
    1077     if (request)
    1078     {
    1079         return (*request)->GetErrorString(error_string);
    1080     }
    1081 
    1082     return false;
    1083 }
    1084 
    1085 bool HttpDownloader::GetReceiveHeader(HANDLE handle, std::string* header)
    1086 {
    1087     std::shared_ptr<DownloadHelper>* request = reinterpret_cast<std::shared_ptr<DownloadHelper>*>(handle);
    1088     if (request)
    1089     {
    1090         return (*request)->GetHeader(header);
    1091     }
    1092 
    1093     return false;
    1094 }
    1095 
    1096 void* HttpDownloader::GetUserData(HANDLE handle)
    1097 {
    1098 
    1099     std::shared_ptr<DownloadHelper>* request = reinterpret_cast<std::shared_ptr<DownloadHelper>*>(handle);
    1100     if (request)
    1101     {
    1102         return (*request)->GetUserData();
    1103     }
    1104 
    1105     return nullptr;
    1106 }
    1107 
    1108 HttpDownloader::DownloadHelper::DownloadHelper()
    1109 #ifdef _WIN32
    1110     : m_perform_thread(nullptr)
    1111 #else
    1112     : m_perform_thread(-1)
    1113 #endif
    1114     , m_close_self(false)
    1115     , m_retry_times(HttpDownloader::s_kRetryCount)
    1116     , m_thread_count(HttpDownloader::s_kThreadCount)
    1117     , m_http_code(0)
    1118     , m_time_out(0)
    1119     , m_proxy_port(0)
    1120     , m_total_size(0.0)
    1121     , m_downloaded_size(0.0)
    1122     , m_multi_download(false)
    1123     , m_download_fail(true)
    1124     , m_is_running(false)
    1125     , m_httplock(new HttpLock)
    1126     , m_userdata(NULL)
    1127 {
    1128     m_download_callback = std::bind(&DownloadHelper::DownloadDefaultCallback, this,
    1129         std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    1130     m_result_callback = std::bind(&DownloadHelper::ResultDefaultCallback, this,
    1131         std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    1132     m_id = HttpHelper::s_id;
    1133 }
    1134 
    1135 HttpDownloader::DownloadHelper::~DownloadHelper()
    1136 {
    1137     if (m_perform_thread)
    1138     {
    1139 #ifdef _WIN32
    1140         CloseHandle(m_perform_thread);
    1141         m_perform_thread = nullptr;
    1142 #endif
    1143     }
    1144 }
    1145 
    1146 int HttpDownloader::DownloadHelper::SetTimeout(long time_out /* = 0 */)
    1147 {
    1148     m_time_out = time_out;
    1149 
    1150     return CURLE_OK;
    1151 }
    1152 
    1153 int HttpDownloader::DownloadHelper::SetRequestUrl(const std::string& url)
    1154 {
    1155     m_url = url;
    1156 
    1157     return CURLE_OK;
    1158 }
    1159 
    1160 int HttpDownloader::DownloadHelper::SetRequestProxy(const std::string& proxy, long proxy_port)
    1161 {
    1162     m_http_proxy = proxy;
    1163     m_proxy_port = proxy_port;
    1164 
    1165     return CURLE_OK;
    1166 }
    1167 
    1168 int HttpDownloader::DownloadHelper::SetProgressCallback(ProgressCallback pc)
    1169 {
    1170     m_download_callback = pc;
    1171 
    1172     return CURLE_OK;
    1173 }
    1174 
    1175 int HttpDownloader::DownloadHelper::SetResultCallback(ResultCallback rc)
    1176 {
    1177     m_result_callback = rc;
    1178 
    1179     return CURLE_OK;
    1180 }
    1181 
    1182 int HttpDownloader::DownloadHelper::SetDownloadFile(const std::string& file_name)
    1183 {
    1184     m_file_path = file_name;
    1185 
    1186     return CURLE_OK;
    1187 }
    1188 
    1189 int HttpDownloader::DownloadHelper::SetDownloadThreadCount(int thread_count)
    1190 {
    1191     m_thread_count = thread_count;
    1192 
    1193     return CURLE_OK;
    1194 }
    1195 
    1196 int HttpDownloader::DownloadHelper::Perform()
    1197 {
    1198     m_total_size = GetDownloadFileSize();
    1199     if (m_total_size < 0)
    1200     {
    1201         return HttpRequest::REQUEST_PERFORM_ERROR;
    1202     }
    1203 
    1204     std::string out_file_name = m_file_path;
    1205     std::string src_file_name = out_file_name;
    1206     out_file_name += ".dl";
    1207 
    1208     FILE *fp = nullptr;
    1209 #ifdef _WIN32
    1210     DeleteFileA(out_file_name.c_str());
    1211     fopen_s(&fp, out_file_name.c_str(), "wb");
    1212 #else
    1213     unlink(out_file_name.c_str());
    1214     fp = fopen(out_file_name.c_str(), "wb");
    1215 #endif
    1216     if (!fp)
    1217     {
    1218         return HttpRequest::REQUEST_OPENFILE_ERROR;
    1219     }
    1220 
    1221     int down_code = HttpRequest::REQUEST_PERFORM_ERROR;
    1222     int thread_count = SplitDownloadCount(m_total_size);
    1223 
    1224     m_thread_count = thread_count > m_thread_count ? m_thread_count : thread_count;
    1225     //文件大小有分开下载的必要并且服务器支持多线程下载时,启用多线程下载
    1226     if (m_multi_download && m_thread_count > 1)
    1227     {
    1228         long gap = static_cast<long>(m_total_size) / m_thread_count;
    1229 #ifdef _WIN32
    1230         std::vector<HANDLE> threads;
    1231 #else
    1232         std::vector<pthread_t> threads;
    1233 #endif
    1234 
    1235         for (int i = 0; i < m_thread_count; i++)
    1236         {
    1237             ThreadChunk* thread_chunk = new ThreadChunk;
    1238             thread_chunk->_fp = fp;
    1239             thread_chunk->_download = this;
    1240 
    1241             if (i < m_thread_count - 1)
    1242             {
    1243                 thread_chunk->_startidx = i * gap;
    1244                 thread_chunk->_endidx = thread_chunk->_startidx + gap - 1;
    1245             }
    1246             else
    1247             {
    1248                 thread_chunk->_startidx = i * gap;
    1249                 thread_chunk->_endidx = -1;
    1250             }
    1251 
    1252 #ifdef _WIN32
    1253             DWORD thread_id;
    1254             HANDLE hThread = CreateThread(NULL, 0, HttpHelper::DownloadWork, thread_chunk, 0, &(thread_id));
    1255 #else
    1256             pthread_t hThread;
    1257             pthread_create(&hThread, NULL, HttpHelper::DownloadWork, thread_chunk);
    1258 #endif
    1259             threads.push_back(hThread);
    1260         }
    1261 
    1262 #ifdef _WIN32
    1263         WaitForMultipleObjects(threads.size(), &threads[0], TRUE, INFINITE);
    1264         for (HANDLE handle : threads)
    1265         {
    1266             CloseHandle(handle);
    1267         }
    1268 #else
    1269         for(pthread_t thread : threads)
    1270         {
    1271             pthread_join(thread, NULL);
    1272         }
    1273 #endif
    1274     }
    1275     else
    1276     {
    1277         ThreadChunk* thread_chunk = new ThreadChunk;
    1278         thread_chunk->_fp = fp;
    1279         thread_chunk->_download = this;
    1280         thread_chunk->_startidx = 0;
    1281         thread_chunk->_endidx = 0;
    1282         down_code = DoDownload(thread_chunk);
    1283     }
    1284 
    1285     fclose(fp);
    1286 
    1287     if (m_download_fail == false)
    1288     {
    1289 #ifdef _WIN32
    1290         MoveFileExA(out_file_name.c_str(), src_file_name.c_str(), MOVEFILE_REPLACE_EXISTING);
    1291 #else
    1292         unlink(src_file_name.c_str());
    1293         rename(out_file_name.c_str(), src_file_name.c_str());
    1294 #endif
    1295     }
    1296     else
    1297     {
    1298 #ifdef _WIN32
    1299         DeleteFileA(out_file_name.c_str());
    1300 #else
    1301         unlink(out_file_name.c_str());
    1302 #endif
    1303     }
    1304 
    1305     m_result_callback(m_id, m_download_fail ? false : true, m_error_string);
    1306 
    1307     m_is_running = false;
    1308 
    1309     return down_code;
    1310 }
    1311 
    1312 bool HttpDownloader::DownloadHelper::GetHeader(std::string* header)
    1313 {
    1314     if (m_receive_header.empty()) return false;
    1315     else if (header) *header = m_receive_header;
    1316 
    1317     return true;
    1318 }
    1319 
    1320 bool HttpDownloader::DownloadHelper::GetErrorString(std::string* error_string)
    1321 {
    1322     if (m_error_string.empty()) return false;
    1323     else if (error_string) *error_string = m_error_string;
    1324 
    1325     return true;
    1326 }
    1327 
    1328 int HttpDownloader::DownloadHelper::DownloadDefaultCallback(double total_size, double downloaded_size, void* userdata)
    1329 {
    1330     return 0;
    1331 }
    1332 
    1333 void HttpDownloader::DownloadHelper::ResultDefaultCallback(int id, bool success, const std::string& data)
    1334 {
    1335 }
    1336 
    1337 double HttpDownloader::DownloadHelper::GetDownloadFileSize()
    1338 {
    1339     if (m_url.empty())
    1340     {
    1341         return -1.0;
    1342     }
    1343     else
    1344     {
    1345         double down_file_length = -1.0;
    1346         CURL *handle = curl_easy_init();
    1347         HttpHelper::set_share_handle(handle);
    1348 
    1349         if (handle)
    1350         {
    1351             curl_easy_setopt(handle, CURLOPT_URL, m_url.c_str());
    1352             curl_easy_setopt(handle, CURLOPT_HEADER, 1);
    1353             curl_easy_setopt(handle, CURLOPT_NOBODY, 1);
    1354             curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
    1355             curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 5);
    1356             curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, HttpHelper::RetriveHeaderFunction);
    1357             curl_easy_setopt(handle, CURLOPT_HEADERDATA, &m_receive_header);
    1358             curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, HttpHelper::RetriveContentFunction);
    1359             curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
    1360             curl_easy_setopt(handle, CURLOPT_RANGE, "2-");
    1361 
    1362             CURLcode curl_code = curl_easy_perform(handle);
    1363 
    1364             if (curl_code == CURLE_OPERATION_TIMEDOUT)
    1365             {
    1366                 int retry_count = m_retry_times;
    1367                 while (retry_count > 0)
    1368                 {
    1369                     curl_code = curl_easy_perform(handle);
    1370                     if (curl_code != CURLE_OPERATION_TIMEDOUT) break;
    1371                     retry_count--;
    1372                 }
    1373             }
    1374 
    1375             curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &m_http_code);
    1376 
    1377             if (curl_code == CURLE_OK)
    1378             {
    1379                 curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &down_file_length);
    1380 
    1381                 //匹配"Content-Range: bytes 2-1449/26620" 则证明支持多线程下载
    1382                 std::regex pattern("CONTENT-RANGE\\s*:\\s*\\w+\\s*(\\d+)-(\\d*)/(\\d+)", std::regex::icase);
    1383                 m_multi_download = std::regex_search(m_receive_header, pattern);
    1384             }
    1385             else
    1386             {
    1387                const char* err_string = curl_easy_strerror(curl_code);
    1388                m_error_string = err_string;
    1389             }            
    1390 
    1391             curl_easy_cleanup(handle);
    1392         }
    1393 
    1394         return down_file_length;
    1395     }
    1396 }
    1397 
    1398 int HttpDownloader::DownloadHelper::DoDownload(ThreadChunk* thread_chunk)
    1399 {
    1400     CURL* curl_handle = curl_easy_init();
    1401     HttpHelper::set_share_handle(curl_handle);
    1402 
    1403     if (thread_chunk->_download->m_url.substr(0, 5) == "https")
    1404     {
    1405         curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
    1406         curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
    1407     }
    1408 
    1409     curl_easy_setopt(curl_handle, CURLOPT_URL, thread_chunk->_download->m_url.c_str());
    1410 
    1411     const char* user_agent = ("Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0");
    1412     curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, user_agent);
    1413 
    1414     curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 5L);
    1415     curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
    1416 
    1417     curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L);
    1418     curl_easy_setopt(curl_handle, CURLOPT_POST, 0L);
    1419 
    1420     curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT_MS, 0L);
    1421     curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, thread_chunk->_download->m_time_out);   //0 means block always
    1422 
    1423     curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, HttpHelper::write_callback);
    1424     curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, thread_chunk);
    1425     curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, HttpHelper::RetriveHeaderFunction);
    1426     curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, NULL);
    1427 
    1428     curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L);
    1429     curl_easy_setopt(curl_handle, CURLOPT_XFERINFOFUNCTION, HttpHelper::progress_callback);
    1430     curl_easy_setopt(curl_handle, CURLOPT_XFERINFODATA, thread_chunk);
    1431 
    1432     curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_LIMIT, 1L);
    1433     curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_TIME, 5L);
    1434 
    1435     if (thread_chunk->_endidx != 0)
    1436     {
    1437         std::string down_range;
    1438         std::ostringstream ostr;
    1439         if (thread_chunk->_endidx > 0)
    1440         {
    1441             ostr << thread_chunk->_startidx << "-" << thread_chunk->_endidx;
    1442         }
    1443         else
    1444         {
    1445             ostr << thread_chunk->_startidx << "-";
    1446         }
    1447         
    1448         down_range = ostr.str();
    1449         curl_easy_setopt(curl_handle, CURLOPT_RANGE, down_range.c_str());
    1450     }
    1451 
    1452     CURLcode curl_code = curl_easy_perform(curl_handle);
    1453     if (curl_code == CURLE_OPERATION_TIMEDOUT)
    1454     {
    1455         int retry_count = m_retry_times;
    1456         while (retry_count > 0)
    1457         {
    1458             curl_code = curl_easy_perform(curl_handle);
    1459             if (curl_code != CURLE_OPERATION_TIMEDOUT) break;
    1460             retry_count--;
    1461         }
    1462     }
    1463 
    1464     long http_code;
    1465     curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code);
    1466     if (curl_code == CURLE_OK && (http_code >= 200 && http_code <= 300))
    1467     {
    1468             m_http_code = http_code;
    1469             thread_chunk->_download->m_download_fail = false;
    1470     }
    1471     else
    1472     {
    1473             const char* err_string = curl_easy_strerror(curl_code);
    1474             m_error_string = err_string;
    1475             thread_chunk->_download->m_download_fail = true;
    1476             m_http_code = http_code;
    1477     }
    1478 
    1479     curl_easy_cleanup(curl_handle);
    1480 
    1481     delete thread_chunk;
    1482 
    1483     return curl_code;
    1484 }
    1485 
    1486 int HttpDownloader::DownloadHelper::SplitDownloadCount(double down_size)
    1487 {
    1488     const double size_2mb = 2.0 * 1024 * 1024;
    1489     const double size_10mb = 10.0 * 1024 * 1024;
    1490     const double size_50mb = 50.0 * 1024 * 1024;
    1491 
    1492     if (down_size <= size_2mb)
    1493     {
    1494         return 1;
    1495     }
    1496     else if (down_size > size_2mb && down_size <= size_10mb)
    1497     {
    1498         return static_cast<int>(down_size / (size_2mb));
    1499     }
    1500     else if (down_size > size_10mb && down_size <= size_50mb)
    1501     {
    1502         return HttpDownloader::s_kThreadCount + 1;
    1503     }
    1504     else
    1505     {
    1506         int down_count = static_cast<int>(down_size / size_10mb);
    1507         return down_count > 10 ? 10 : down_count;
    1508     }
    1509 
    1510     return 1;
    1511 }
    1512 
    1513 void HttpDownloader::DownloadHelper::Reset()
    1514 {
    1515     if (m_is_running)
    1516     {
    1517         return;
    1518     }
    1519 
    1520     if (m_perform_thread)   //thread run over because if m_is_running set true, Reset wont be invoke
    1521     {
    1522 #ifdef _WIN32
    1523         CloseHandle(m_perform_thread);
    1524         m_perform_thread = nullptr;
    1525 #endif
    1526     }
    1527 
    1528     m_close_self = false;
    1529     m_multi_download = false;
    1530     m_download_fail = true;
    1531     m_is_running = false;
    1532     m_is_cancel = false;
    1533     m_http_code = 0;
    1534     m_total_size = 0.0;
    1535     m_downloaded_size = 0.0;
    1536 
    1537     m_receive_header = "";
    1538     m_error_string = "";
    1539 }
    HttpRequest.cpp

    libcurl的http请求默认是Get。如果指定了Post数据,则是Post请求。

     

  3. 使用libcurl库
    demo使用封装的库来模拟请求数据和下载文件。
    例子很简单,直接看代码:
      1 // http_request.cpp : 定义控制台应用程序的入口点。
      2 //
      3 
      4 #include "HttpRequest.h"
      5 
      6 #include <iostream>
      7 #include <string>
      8 #include <fstream>
      9 #include <functional>
     10 
     11 class DownCallbackClass
     12 {
     13 public:
     14         DownCallbackClass() :m_down_finished(false) {}
     15         ~DownCallbackClass() {}
     16 public:
     17         void DownResultCallback(int id, bool success, const std::string& data)
     18         {
     19                 m_down_finished = true;
     20         }
     21         int down_callback(double total_size, double downloaded_size, void* userdata)
     22         {
     23                 long tmp = static_cast<long>(downloaded_size / total_size * 100);
     24                 printf("\r下载进度%d", tmp);
     25                 return 0;
     26         }
     27         bool IsDownFinished(void) { return m_down_finished;  }
     28 private:
     29         bool m_down_finished;
     30 };
     31 
     32 class MyResultClass
     33 {
     34 public:
     35         MyResultClass() : m_request_finished(false) { }
     36         ~MyResultClass() { }
     37 
     38 public:
     39         void MyRequestResultCallback(int id, bool success, const std::string& data)
     40         {
     41                 if (success)
     42                 {
     43                         std::ofstream outfile;
     44                         outfile.open("baidu.html", std::ios_base::binary | std::ios_base::trunc);
     45                         if (outfile.good()) outfile.write(data.c_str(), data.size());
     46                 }
     47                 m_request_finished = true;
     48         }
     49         bool IsRequestFinish(void) { return m_request_finished;  }
     50 private:
     51         bool m_request_finished;
     52 };
     53 
     54 int _tmain(int argc, _TCHAR* argv[])
     55 {
     56         MyResultClass mc;
     57 
     58         HttpRequest request;
     59         request.SetRequestUrl("http://www.baidu.com");
     60         request.SetResultCallback(std::bind(&MyResultClass::MyRequestResultCallback, &mc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
     61         request.SetRequestHeader("User-Agent:Mozilla/4.04[en](Win95;I;Nav)");
     62 
     63         HANDLE hRequest = request.PerformRequest(HttpRequest::REQUEST_ASYNC);
     64         if (hRequest)
     65         {
     66                 while (mc.IsRequestFinish() == false) Sleep(300);
     67                 long http_code;
     68                 if (request.GetHttpCode(hRequest, &http_code))
     69                         std::cout << "http code: " << http_code << std::endl;
     70 
     71                 std::string header;
     72                 if (request.GetReceiveHeader(hRequest, &header))
     73                 {
     74                         std::cout << header << std::endl;
     75                 }
     76 
     77                 HttpRequest::Close(hRequest);
     78         }
     79 
     80         HttpDownloader download;
     81         DownCallbackClass dc;
     82         const char* down_url = "http://dlsw.baidu.com/sw-search-sp/soft/71/10998/OfflineBaiduPlayer_151_V4.1.2.263.1432003947.exe";
     83         const char* down_file = "BaiduPlayer.exe";
     84 
     85         download.SetDownloadUrl(down_url);
     86         download.SetProgressCallback(std::bind(&DownCallbackClass::down_callback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
     87         download.SetResultCallback(std::bind(&DownCallbackClass::DownResultCallback, &dc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
     88         download.DownloadFile(down_file);
     89         HANDLE hDownload = download.StartDownload(HttpDownloader::DOWN_ASYNC);
     90         if (hDownload)
     91         {
     92                 while (dc.IsDownFinished() == false)
     93                 {
     94                         Sleep(300);
     95                 }
     96                 //to do download finish clean up
     97                 HttpDownloader::Close(hDownload);
     98         }
     99 
    100         return 0;
    101 }

     

posted @ 2015-06-04 10:52  Jojodru  阅读(12046)  评论(1编辑  收藏  举报