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