libcurl的封装,支持同步异步请求,支持多线程下载,支持https(z)
最近在做一个项目,需要用到http get post等
需求分析需要做到同步和异步,异步请求的返回以可选的回调通知的方式进行。
本人以Linux为例,一步一步的来实现。
- 配置并且编译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
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
配置成功之后,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
配置成功之后,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即可 - 封装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*/
实现文件:
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 }
libcurl的http请求默认是Get。如果指定了Post数据,则是Post请求。
- 使用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 }