1 /*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <unistd.h>
17 #ifdef HTTP_MULTIPATH_CERT_ENABLE
18 #include <openssl/ssl.h>
19 #endif
20 #include <iostream>
21 #include <memory>
22
23 #include "http_client.h"
24 #include "http_client_constant.h"
25 #include "http_client_task.h"
26 #include "http_client_time.h"
27 #include "net_conn_client.h"
28 #include "netstack_common_utils.h"
29 #include "netstack_log.h"
30 #include "timing.h"
31 #if HAS_NETMANAGER_BASE
32 #include "http_client_network_message.h"
33 #endif
34
35 #define NETSTACK_CURL_EASY_SET_OPTION(handle, opt, data) \
36 do { \
37 CURLcode result = curl_easy_setopt(handle, opt, data); \
38 if (result != CURLE_OK) { \
39 const char *err = curl_easy_strerror(result); \
40 error_.SetCURLResult(result); \
41 NETSTACK_LOGE("Failed to set option: %{public}s, %{public}s %{public}d", #opt, err, result); \
42 return false; \
43 } \
44 } while (0)
45
46 namespace OHOS {
47 namespace NetStack {
48 namespace HttpClient {
49
50 static const size_t MAX_LIMIT = HttpConstant::MAX_DATA_LIMIT;
51
52 std::atomic<uint32_t> HttpClientTask::nextTaskId_(0);
53
CheckFilePath(const std::string & fileName,std::string & realPath)54 bool CheckFilePath(const std::string &fileName, std::string &realPath)
55 {
56 char tmpPath[PATH_MAX] = {0};
57 if (!realpath(static_cast<const char *>(fileName.c_str()), tmpPath)) {
58 NETSTACK_LOGE("file name is error");
59 return false;
60 }
61
62 realPath = tmpPath;
63 return true;
64 }
65
HttpClientTask(const HttpClientRequest & request)66 HttpClientTask::HttpClientTask(const HttpClientRequest &request)
67 : request_(request),
68 type_(DEFAULT),
69 status_(IDLE),
70 taskId_(nextTaskId_++),
71 curlHeaderList_(nullptr),
72 canceled_(false),
73 file_(nullptr)
74 {
75 NETSTACK_LOGD("id=%{public}d", taskId_);
76
77 curlHandle_ = curl_easy_init();
78 if (!curlHandle_) {
79 NETSTACK_LOGE("Failed to create task!");
80 return;
81 }
82
83 SetCurlOptions();
84 #if HAS_NETMANAGER_BASE
85 networkProfilerUtils_ = std::make_unique<NetworkProfilerUtils>();
86 #endif
87 }
88
HttpClientTask(const HttpClientRequest & request,TaskType type,const std::string & filePath)89 HttpClientTask::HttpClientTask(const HttpClientRequest &request, TaskType type, const std::string &filePath)
90 : request_(request),
91 type_(type),
92 status_(IDLE),
93 taskId_(nextTaskId_++),
94 curlHeaderList_(nullptr),
95 canceled_(false),
96 filePath_(filePath),
97 file_(nullptr)
98 {
99 curlHandle_ = curl_easy_init();
100 if (!curlHandle_) {
101 NETSTACK_LOGE("Failed to create task!");
102 return;
103 }
104
105 SetCurlOptions();
106 #if HAS_NETMANAGER_BASE
107 networkProfilerUtils_ = std::make_unique<NetworkProfilerUtils>();
108 #endif
109 }
110
~HttpClientTask()111 HttpClientTask::~HttpClientTask()
112 {
113 NETSTACK_LOGD("Destroy: taskId_=%{public}d", taskId_);
114 if (curlHeaderList_ != nullptr) {
115 curl_slist_free_all(curlHeaderList_);
116 curlHeaderList_ = nullptr;
117 }
118
119 if (curlHandle_) {
120 curl_easy_cleanup(curlHandle_);
121 curlHandle_ = nullptr;
122 }
123
124 if (file_ != nullptr) {
125 fclose(file_);
126 file_ = nullptr;
127 }
128 }
129
GetHttpVersion(HttpProtocol ptcl) const130 uint32_t HttpClientTask::GetHttpVersion(HttpProtocol ptcl) const
131 {
132 if (ptcl == HttpProtocol::HTTP1_1) {
133 NETSTACK_LOGD("CURL_HTTP_VERSION_1_1");
134 return CURL_HTTP_VERSION_1_1;
135 } else if (ptcl == HttpProtocol::HTTP2) {
136 NETSTACK_LOGD("CURL_HTTP_VERSION_2_0");
137 return CURL_HTTP_VERSION_2_0;
138 } else if (ptcl == HttpProtocol::HTTP3) {
139 NETSTACK_LOGD("CURL_HTTP_VERSION_3");
140 return CURL_HTTP_VERSION_3;
141 }
142 return CURL_HTTP_VERSION_NONE;
143 }
144
GetHttpProxyInfo(std::string & host,int32_t & port,std::string & exclusions,bool & tunnel)145 void HttpClientTask::GetHttpProxyInfo(std::string &host, int32_t &port, std::string &exclusions, bool &tunnel)
146 {
147 if (request_.GetHttpProxyType() == HttpProxyType::USE_SPECIFIED) {
148 HttpProxy proxy = request_.GetHttpProxy();
149 host = proxy.host;
150 port = proxy.port;
151 exclusions = proxy.exclusions;
152 tunnel = proxy.tunnel;
153 } else {
154 using namespace NetManagerStandard;
155 NetManagerStandard::HttpProxy httpProxy;
156 NetConnClient::GetInstance().GetDefaultHttpProxy(httpProxy);
157 host = httpProxy.GetHost();
158 port = httpProxy.GetPort();
159 exclusions = CommonUtils::ToString(httpProxy.GetExclusionList());
160 }
161 }
162
SslCtxFunction(CURL * curl,void * sslCtx)163 CURLcode HttpClientTask::SslCtxFunction(CURL *curl, void *sslCtx)
164 {
165 #ifdef HTTP_MULTIPATH_CERT_ENABLE
166 if (sslCtx == nullptr) {
167 NETSTACK_LOGE("sslCtx is null");
168 return CURLE_SSL_CERTPROBLEM;
169 }
170 std::vector<std::string> certs;
171 certs.emplace_back(HttpConstant::USER_CERT_ROOT_PATH);
172 certs.emplace_back(
173 HttpConstant::USER_CERT_BASE_PATH + std::to_string(getuid() / HttpConstant::UID_TRANSFORM_DIVISOR));
174 certs.emplace_back(HttpConstant::HTTP_PREPARE_CA_PATH);
175
176 for (const auto &path : certs) {
177 if (path.empty() || access(path.c_str(), F_OK) != 0) {
178 NETSTACK_LOGD("certificate directory path is not exist");
179 continue;
180 }
181 if (!SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(sslCtx), nullptr, path.c_str())) {
182 NETSTACK_LOGE("loading certificates from directory error.");
183 continue;
184 }
185 }
186
187 if (access(request_.GetCaPath().c_str(), F_OK) != 0) {
188 NETSTACK_LOGD("certificate directory path is not exist");
189 } else if (!SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(sslCtx), request_.GetCaPath().c_str(), nullptr)) {
190 NETSTACK_LOGE("loading certificates from context cert error.");
191 }
192 #endif // HTTP_MULTIPATH_CERT_ENABLE
193 return CURLE_OK;
194 }
195
SetSSLCertOption(CURL * handle)196 bool HttpClientTask::SetSSLCertOption(CURL *handle)
197 {
198 #ifndef WINDOWS_PLATFORM
199 #ifdef HTTP_MULTIPATH_CERT_ENABLE
200 curl_ssl_ctx_callback sslCtxFunc = [](CURL *curl, void *sslCtx, void *parm) -> CURLcode {
201 HttpClientTask *task = static_cast<HttpClientTask *>(parm);
202 if (!task) {
203 return CURLE_SSL_CERTPROBLEM;
204 }
205 return task->SslCtxFunction(curl, sslCtx);
206 };
207 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_CTX_FUNCTION, sslCtxFunc);
208 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_CTX_DATA, this);
209 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_CAINFO, nullptr);
210 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYPEER, 1L);
211 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYHOST, 2L);
212 #else
213 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_CAINFO, nullptr);
214 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYPEER, 0L);
215 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYHOST, 0L);
216 #endif // HTTP_MULTIPATH_CERT_ENABLE
217 #else
218 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYPEER, 0L);
219 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYHOST, 0L);
220 #endif // WINDOWS_PLATFORM
221 SetServerSSLCertOption(handle);
222 return true;
223 }
224
SetOtherCurlOption(CURL * handle)225 bool HttpClientTask::SetOtherCurlOption(CURL *handle)
226 {
227 // set proxy
228 std::string host;
229 std::string exclusions;
230 int32_t port = 0;
231 bool tunnel = false;
232 std::string url = request_.GetURL();
233 GetHttpProxyInfo(host, port, exclusions, tunnel);
234 if (!host.empty() && !CommonUtils::IsHostNameExcluded(url, exclusions, ",")) {
235 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_PROXY, host.c_str());
236 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_PROXYPORT, port);
237 auto curlTunnelValue = (url.find("https://") != std::string::npos) ? 1L : 0L;
238 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_HTTPPROXYTUNNEL, curlTunnelValue);
239 auto proxyType = (host.find("https://") != std::string::npos) ? CURLPROXY_HTTPS : CURLPROXY_HTTP;
240 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_PROXYTYPE, proxyType);
241 }
242
243 SetSSLCertOption(handle);
244
245 #ifdef HTTP_CURL_PRINT_VERBOSE
246 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_VERBOSE, 1L);
247 #endif
248
249 #ifndef WINDOWS_PLATFORM
250 NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_ACCEPT_ENCODING, "");
251 #endif
252
253 return true;
254 }
255
SetServerSSLCertOption(CURL * curl)256 bool HttpClientTask::SetServerSSLCertOption(CURL *curl)
257 {
258 auto hostname = CommonUtils::GetHostnameFromURL(request_.GetURL());
259 if (!NetManagerStandard::NetConnClient::GetInstance().IsPinOpenMode(hostname)) {
260 std::string pins;
261 auto ret = NetManagerStandard::NetConnClient::GetInstance().GetPinSetForHostName(hostname, pins);
262 if (ret != 0 || pins.empty()) {
263 NETSTACK_LOGD("Get no pin set by host name invalid");
264 } else {
265 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PINNEDPUBLICKEY, pins.c_str());
266 }
267 }
268
269 return true;
270 }
271
SetUploadOptions(CURL * handle)272 bool HttpClientTask::SetUploadOptions(CURL *handle)
273 {
274 if (filePath_.empty()) {
275 NETSTACK_LOGE("HttpClientTask::SetUploadOptions() filePath_ is empty");
276 error_.SetErrorCode(HttpErrorCode::HTTP_UPLOAD_FAILED);
277 return false;
278 }
279
280 std::string realPath;
281 if (!CheckFilePath(filePath_, realPath)) {
282 NETSTACK_LOGE("filePath_ does not exist! ");
283 error_.SetErrorCode(HttpErrorCode::HTTP_UPLOAD_FAILED);
284 return false;
285 }
286
287 file_ = fopen(realPath.c_str(), "rb");
288 if (file_ == nullptr) {
289 NETSTACK_LOGE("HttpClientTask::SetUploadOptions() Failed to open file");
290 error_.SetErrorCode(HttpErrorCode::HTTP_UPLOAD_FAILED);
291 return false;
292 }
293
294 fseek(file_, 0, SEEK_END);
295 long size = ftell(file_);
296 rewind(file_);
297
298 // Set the file data and file size to upload
299 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_READDATA, file_);
300 NETSTACK_LOGD("CURLOPT_INFILESIZE=%{public}ld", size);
301 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_INFILESIZE, size);
302 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_UPLOAD, 1L);
303
304 return true;
305 }
306
SetCurlOptions()307 bool HttpClientTask::SetCurlOptions()
308 {
309 auto method = request_.GetMethod();
310 if (method == HttpConstant::HTTP_METHOD_HEAD) {
311 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_NOBODY, 1L);
312 }
313
314 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_URL, request_.GetURL().c_str());
315
316 if (type_ == TaskType::UPLOAD) {
317 if (!SetUploadOptions(curlHandle_)) {
318 return false;
319 }
320 } else {
321 if (!method.empty()) {
322 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_CUSTOMREQUEST, request_.GetMethod().c_str());
323 }
324
325 if ((method.empty() || method == HttpConstant::HTTP_METHOD_POST || method == HttpConstant::HTTP_METHOD_PUT) &&
326 !request_.GetBody().empty()) {
327 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_POST, 1L);
328 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_POSTFIELDS, request_.GetBody().c_str());
329 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_POSTFIELDSIZE, request_.GetBody().size());
330 }
331 }
332
333 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_XFERINFOFUNCTION, ProgressCallback);
334 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_XFERINFODATA, this);
335 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_NOPROGRESS, 0L);
336
337 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_WRITEFUNCTION, DataReceiveCallback);
338 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_WRITEDATA, this);
339
340 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_HEADERFUNCTION, HeaderReceiveCallback);
341 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_HEADERDATA, this);
342
343 if (curlHeaderList_ != nullptr) {
344 curl_slist_free_all(curlHeaderList_);
345 curlHeaderList_ = nullptr;
346 }
347 for (const auto &header : request_.GetHeaders()) {
348 std::string headerStr = header.first + HttpConstant::HTTP_HEADER_SEPARATOR + header.second;
349 curlHeaderList_ = curl_slist_append(curlHeaderList_, headerStr.c_str());
350 }
351 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_HTTPHEADER, curlHeaderList_);
352
353 // Some servers don't like requests that are made without a user-agent field, so we provide one
354 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_USERAGENT, HttpConstant::HTTP_DEFAULT_USER_AGENT);
355
356 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_FOLLOWLOCATION, 1L);
357
358 /* first #undef CURL_DISABLE_COOKIES in curl config */
359 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_COOKIEFILE, "");
360
361 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_NOSIGNAL, 1L);
362
363 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_TIMEOUT_MS, request_.GetTimeout());
364 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_CONNECTTIMEOUT_MS, request_.GetConnectTimeout());
365
366 NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_HTTP_VERSION, GetHttpVersion(request_.GetHttpProtocol()));
367
368 if (!SetOtherCurlOption(curlHandle_)) {
369 return false;
370 }
371
372 return true;
373 }
374
Start()375 bool HttpClientTask::Start()
376 {
377 if (GetStatus() != TaskStatus::IDLE) {
378 NETSTACK_LOGD("task is running, taskId_=%{public}d", taskId_);
379 return false;
380 }
381
382 if (!CommonUtils::HasInternetPermission()) {
383 NETSTACK_LOGE("Don't Has Internet Permission()");
384 error_.SetErrorCode(HttpErrorCode::HTTP_PERMISSION_DENIED_CODE);
385 return false;
386 }
387
388 if (error_.GetErrorCode() != HttpErrorCode::HTTP_NONE_ERR) {
389 NETSTACK_LOGE("error_.GetErrorCode()=%{public}d", error_.GetErrorCode());
390 return false;
391 }
392
393 request_.SetRequestTime(HttpTime::GetNowTimeGMT());
394
395 HttpSession &session = HttpSession::GetInstance();
396 NETSTACK_LOGD("taskId_=%{public}d", taskId_);
397 canceled_ = false;
398
399 response_.SetRequestTime(HttpTime::GetNowTimeGMT());
400
401 auto task = shared_from_this();
402 session.StartTask(task);
403 return true;
404 }
405
Cancel()406 void HttpClientTask::Cancel()
407 {
408 canceled_ = true;
409 }
410
SetStatus(TaskStatus status)411 void HttpClientTask::SetStatus(TaskStatus status)
412 {
413 status_ = status;
414 }
415
GetStatus()416 TaskStatus HttpClientTask::GetStatus()
417 {
418 return status_;
419 }
420
GetType()421 TaskType HttpClientTask::GetType()
422 {
423 return type_;
424 }
425
GetFilePath()426 const std::string &HttpClientTask::GetFilePath()
427 {
428 return filePath_;
429 }
430
GetTaskId()431 unsigned int HttpClientTask::GetTaskId()
432 {
433 return taskId_;
434 }
435
OnSuccess(const std::function<void (const HttpClientRequest & request,const HttpClientResponse & response)> & onSucceeded)436 void HttpClientTask::OnSuccess(
437 const std::function<void(const HttpClientRequest &request, const HttpClientResponse &response)> &onSucceeded)
438 {
439 onSucceeded_ = onSucceeded;
440 }
441
OnCancel(const std::function<void (const HttpClientRequest & request,const HttpClientResponse & response)> & onCanceled)442 void HttpClientTask::OnCancel(
443 const std::function<void(const HttpClientRequest &request, const HttpClientResponse &response)> &onCanceled)
444 {
445 onCanceled_ = onCanceled;
446 }
447
OnFail(const std::function<void (const HttpClientRequest & request,const HttpClientResponse & response,const HttpClientError & error)> & onFailed)448 void HttpClientTask::OnFail(
449 const std::function<void(const HttpClientRequest &request, const HttpClientResponse &response,
450 const HttpClientError &error)> &onFailed)
451 {
452 onFailed_ = onFailed;
453 }
454
OnDataReceive(const std::function<void (const HttpClientRequest & request,const uint8_t * data,size_t length)> & onDataReceive)455 void HttpClientTask::OnDataReceive(
456 const std::function<void(const HttpClientRequest &request, const uint8_t *data, size_t length)> &onDataReceive)
457 {
458 onDataReceive_ = onDataReceive;
459 }
460
OnProgress(const std::function<void (const HttpClientRequest & request,u_long dlTotal,u_long dlNow,u_long ulTotal,u_long ulNow)> & onProgress)461 void HttpClientTask::OnProgress(const std::function<void(const HttpClientRequest &request, u_long dlTotal, u_long dlNow,
462 u_long ulTotal, u_long ulNow)> &onProgress)
463 {
464 onProgress_ = onProgress;
465 }
466
DataReceiveCallback(const void * data,size_t size,size_t memBytes,void * userData)467 size_t HttpClientTask::DataReceiveCallback(const void *data, size_t size, size_t memBytes, void *userData)
468 {
469 auto task = static_cast<HttpClientTask *>(userData);
470 NETSTACK_LOGD("taskId=%{public}d size=%{public}zu memBytes=%{public}zu", task->taskId_, size, memBytes);
471
472 if (task->canceled_) {
473 NETSTACK_LOGD("canceled");
474 return 0;
475 }
476
477 if (task->onDataReceive_) {
478 HttpClientRequest request = task->request_;
479 task->onDataReceive_(request, static_cast<const uint8_t *>(data), size * memBytes);
480 }
481
482 if (task->response_.GetResult().size() < MAX_LIMIT) {
483 task->response_.AppendResult(data, size * memBytes);
484 }
485
486 return size * memBytes;
487 }
488
ProgressCallback(void * userData,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)489 int HttpClientTask::ProgressCallback(void *userData, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
490 curl_off_t ulnow)
491 {
492 auto task = static_cast<HttpClientTask *>(userData);
493 NETSTACK_LOGD("taskId=%{public}d dltotal=%{public}" CURL_FORMAT_CURL_OFF_T " dlnow=%{public}" CURL_FORMAT_CURL_OFF_T
494 " ultotal=%{public}" CURL_FORMAT_CURL_OFF_T " ulnow=%{public}" CURL_FORMAT_CURL_OFF_T,
495 task->taskId_, dltotal, dlnow, ultotal, ulnow);
496
497 if (task->canceled_) {
498 NETSTACK_LOGD("canceled");
499 return CURLE_ABORTED_BY_CALLBACK;
500 }
501
502 if (task->onProgress_) {
503 task->onProgress_(task->request_, dltotal, dlnow, ultotal, ulnow);
504 }
505
506 return 0;
507 }
508
HeaderReceiveCallback(const void * data,size_t size,size_t memBytes,void * userData)509 size_t HttpClientTask::HeaderReceiveCallback(const void *data, size_t size, size_t memBytes, void *userData)
510 {
511 auto task = static_cast<HttpClientTask *>(userData);
512 NETSTACK_LOGD("taskId=%{public}d size=%{public}zu memBytes=%{public}zu", task->taskId_, size, memBytes);
513
514 if (size * memBytes > MAX_LIMIT) {
515 NETSTACK_LOGE("size * memBytes(%{public}zu) > MAX_LIMIT(%{public}zu)", size * memBytes, MAX_LIMIT);
516 return 0;
517 }
518
519 task->response_.AppendHeader(static_cast<const char *>(data), size * memBytes);
520
521 return size * memBytes;
522 }
523
ProcessCookie(CURL * handle)524 void HttpClientTask::ProcessCookie(CURL *handle)
525 {
526 struct curl_slist *cookies = nullptr;
527 if (handle == nullptr) {
528 NETSTACK_LOGE("HttpClientTask::ProcessCookie() handle == nullptr");
529 return;
530 }
531
532 CURLcode res = curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &cookies);
533 if (res != CURLE_OK) {
534 NETSTACK_LOGE("HttpClientTask::ProcessCookie() curl_easy_getinfo() error! res = %{public}d", res);
535 return;
536 }
537
538 while (cookies) {
539 response_.AppendCookies(cookies->data, strlen(cookies->data));
540 if (cookies->next != nullptr) {
541 response_.AppendCookies(HttpConstant::HTTP_LINE_SEPARATOR, strlen(HttpConstant::HTTP_LINE_SEPARATOR));
542 }
543 cookies = cookies->next;
544 }
545 }
546
ProcessResponseCode()547 bool HttpClientTask::ProcessResponseCode()
548 {
549 int64_t result = 0;
550 CURLcode code = curl_easy_getinfo(curlHandle_, CURLINFO_RESPONSE_CODE, &result);
551 if (code != CURLE_OK) {
552 error_.SetCURLResult(code);
553 return false;
554 }
555 auto resultCode = static_cast<ResponseCode>(result);
556 NETSTACK_LOGD("id=%{public}d, code=%{public}d", taskId_, resultCode);
557 response_.SetResponseCode(resultCode);
558
559 return true;
560 }
561
GetTimingFromCurl(CURL * handle,CURLINFO info) const562 double HttpClientTask::GetTimingFromCurl(CURL *handle, CURLINFO info) const
563 {
564 time_t timing;
565 CURLcode result = curl_easy_getinfo(handle, info, &timing);
566 if (result != CURLE_OK) {
567 NETSTACK_LOGE("Failed to get timing: %{public}d, %{public}s", info, curl_easy_strerror(result));
568 return 0;
569 }
570 return Timing::TimeUtils::Microseconds2Milliseconds(timing);
571 }
572
GetSizeFromCurl(CURL * handle) const573 curl_off_t HttpClientTask::GetSizeFromCurl(CURL *handle) const
574 {
575 auto info = CURLINFO_SIZE_DOWNLOAD_T;
576 auto method = request_.GetMethod();
577 if (((method.empty() || method == HttpConstant::HTTP_METHOD_POST || method == HttpConstant::HTTP_METHOD_PUT) &&
578 !request_.GetBody().empty()) || type_ == TaskType::UPLOAD) {
579 info = CURLINFO_SIZE_UPLOAD_T;
580 }
581 curl_off_t size = 0;
582 CURLcode result = curl_easy_getinfo(handle, info, &size);
583 if (result != CURLE_OK) {
584 NETSTACK_LOGE("curl_easy_getinfo failed, %{public}d, %{public}s", info, curl_easy_strerror(result));
585 return 0;
586 }
587 return size;
588 }
589
DumpHttpPerformance() const590 void HttpClientTask::DumpHttpPerformance() const
591 {
592 auto dnsTime = GetTimingFromCurl(curlHandle_, CURLINFO_NAMELOOKUP_TIME_T);
593 auto connectTime = GetTimingFromCurl(curlHandle_, CURLINFO_CONNECT_TIME_T);
594 auto tlsTime = GetTimingFromCurl(curlHandle_, CURLINFO_APPCONNECT_TIME_T);
595 auto firstSendTime = GetTimingFromCurl(curlHandle_, CURLINFO_PRETRANSFER_TIME_T);
596 auto firstRecvTime = GetTimingFromCurl(curlHandle_, CURLINFO_STARTTRANSFER_TIME_T);
597 auto totalTime = GetTimingFromCurl(curlHandle_, CURLINFO_TOTAL_TIME_T);
598 auto redirectTime = GetTimingFromCurl(curlHandle_, CURLINFO_REDIRECT_TIME_T);
599
600 int64_t responseCode = 0;
601 (void)curl_easy_getinfo(curlHandle_, CURLINFO_RESPONSE_CODE, &responseCode);
602
603 /*
604 CURL_HTTP_VERSION_NONE 0
605 CURL_HTTP_VERSION_1_0 1
606 CURL_HTTP_VERSION_1_1 2
607 CURL_HTTP_VERSION_2 3
608 */
609 int64_t httpVer = CURL_HTTP_VERSION_NONE;
610 (void)curl_easy_getinfo(curlHandle_, CURLINFO_HTTP_VERSION, &httpVer);
611
612 NETSTACK_CORE_LOGI(
613 "taskid=%{public}d"
614 ", size:%{public}" CURL_FORMAT_CURL_OFF_T
615 ", dns:%{public}.3f"
616 ", connect:%{public}.3f"
617 ", tls:%{public}.3f"
618 ", firstSend:%{public}.3f"
619 ", firstRecv:%{public}.3f"
620 ", total:%{public}.3f"
621 ", redirect:%{public}.3f"
622 ", errCode:%{public}d"
623 ", RespCode:%{public}s"
624 ", httpVer:%{public}s"
625 ", method:%{public}s",
626 taskId_, GetSizeFromCurl(curlHandle_), dnsTime, connectTime == 0 ? 0 : connectTime - dnsTime,
627 tlsTime == 0 ? 0 : tlsTime - connectTime,
628 firstSendTime == 0 ? 0 : firstSendTime - std::max({dnsTime, connectTime, tlsTime}),
629 firstRecvTime == 0 ? 0 : firstRecvTime - firstSendTime, totalTime, redirectTime,
630 error_.GetErrorCode(), std::to_string(responseCode).c_str(), std::to_string(httpVer).c_str(),
631 request_.GetMethod().c_str());
632 }
633
ProcessResponse(CURLMsg * msg)634 void HttpClientTask::ProcessResponse(CURLMsg *msg)
635 {
636 CURLcode code = msg->data.result;
637 NETSTACK_LOGD("taskid=%{public}d code=%{public}d", taskId_, code);
638 error_.SetCURLResult(code);
639 response_.SetResponseTime(HttpTime::GetNowTimeGMT());
640
641 DumpHttpPerformance();
642
643 if (CURLE_ABORTED_BY_CALLBACK == code) {
644 (void)ProcessResponseCode();
645 if (onCanceled_) {
646 onCanceled_(request_, response_);
647 }
648 return;
649 }
650
651 if (code != CURLE_OK) {
652 if (onFailed_) {
653 onFailed_(request_, response_, error_);
654 }
655 return;
656 }
657
658 ProcessCookie(curlHandle_);
659 response_.ParseHeaders();
660
661 if (ProcessResponseCode()) {
662 if (onSucceeded_) {
663 onSucceeded_(request_, response_);
664 }
665 } else if (onFailed_) {
666 onFailed_(request_, response_, error_);
667 }
668 #if HAS_NETMANAGER_BASE
669 HttpClientNetworkMessage httpClientNetworkMessage(std::to_string(GetTaskId()), request_, response_, curlHandle_);
670 networkProfilerUtils_->NetworkProfiling(httpClientNetworkMessage);
671 #endif
672 }
673
SetResponse(const HttpClientResponse & response)674 void HttpClientTask::SetResponse(const HttpClientResponse &response)
675 {
676 response_ = response;
677 }
678 } // namespace HttpClient
679 } // namespace NetStack
680 } // namespace OHOS
681