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