最近在做一个项目,需要用到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
6 #include
实现文件:
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
28 #include
29 #include
30 #endif
31
32 #include "./curl/curl.h" //libcurl interface
33 #include "HttpRequest.h" //HttpRequest class
34
35 #include
36 #include
37 #include
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
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
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
124 static std::list< std::shared_ptr
125
126 static int s_id;
127 static std::shared_ptr
128 static std::shared_ptr
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
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
166 if (receive_header && buffer)
167 {
168 receive_header->append(reinterpret_cast
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
177 if (receive_content && ptr)
178 {
179 receive_content->append(reinterpret_cast
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
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
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
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
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
287 std::list< std::shared_ptr
288 int HttpHelper::s_id = 0;
289 std::shared_ptr
290 std::shared_ptr
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1229 #ifdef _WIN32
1230 std::vector
1231 #else
1232 std::vector
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
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
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
7 #include
8 #include
9 #include
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
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 }
手机扫一扫
移动阅读更方便
你可能感兴趣的文章