• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TrustUser0AndUserCa(std::vector<std::string> & certs)148 [[maybe_unused]] void TrustUser0AndUserCa(std::vector<std::string> &certs)
149 {
150 #ifdef HTTP_MULTIPATH_CERT_ENABLE
151     if (NetManagerStandard::NetConnClient::GetInstance().TrustUser0Ca()) {
152         certs.emplace_back(HttpConstant::USER_CERT_ROOT_PATH);
153     }
154     if (NetManagerStandard::NetConnClient::GetInstance().TrustUserCa()) {
155         certs.emplace_back(HttpConstant::USER_CERT_BASE_PATH +
156                            std::to_string(getuid() / HttpConstant::UID_TRANSFORM_DIVISOR));
157     }
158 #endif
159 }
160 
SslCtxFunction(CURL * curl,void * sslCtx)161 CURLcode HttpClientTask::SslCtxFunction(CURL *curl, void *sslCtx)
162 {
163 #ifdef HTTP_MULTIPATH_CERT_ENABLE
164     if (sslCtx == nullptr) {
165         NETSTACK_LOGE("sslCtx is null");
166         return CURLE_SSL_CERTPROBLEM;
167     }
168     std::vector<std::string> certs;
169     TrustUser0AndUserCa(certs);
170     certs.emplace_back(HttpConstant::HTTP_PREPARE_CA_PATH);
171 
172     for (const auto &path : certs) {
173         if (path.empty() || access(path.c_str(), F_OK) != 0) {
174             NETSTACK_LOGD("certificate directory path is not exist");
175             continue;
176         }
177         if (!SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(sslCtx), nullptr, path.c_str())) {
178             NETSTACK_LOGE("loading certificates from directory error.");
179             continue;
180         }
181     }
182 
183     if (access(request_.GetCaPath().c_str(), F_OK) != 0) {
184         NETSTACK_LOGD("certificate directory path is not exist");
185     } else if (!SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(sslCtx), request_.GetCaPath().c_str(), nullptr)) {
186         NETSTACK_LOGE("loading certificates from context cert error.");
187     }
188 #endif // HTTP_MULTIPATH_CERT_ENABLE
189     return CURLE_OK;
190 }
191 
SetSSLCertOption(CURL * handle)192 bool HttpClientTask::SetSSLCertOption(CURL *handle)
193 {
194 #ifndef WINDOWS_PLATFORM
195 #ifdef HTTP_MULTIPATH_CERT_ENABLE
196     curl_ssl_ctx_callback sslCtxFunc = [](CURL *curl, void *sslCtx, void *parm) -> CURLcode {
197         HttpClientTask *task = static_cast<HttpClientTask *>(parm);
198         if (!task) {
199             return CURLE_SSL_CERTPROBLEM;
200         }
201         task->GetTrace().Tracepoint(TraceEvents::TLS);
202         return task->SslCtxFunction(curl, sslCtx);
203     };
204     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_CTX_FUNCTION, sslCtxFunc);
205     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_CTX_DATA, this);
206     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_CAINFO, nullptr);
207     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYPEER, 1L);
208     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYHOST, 2L);
209 #else
210     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_CAINFO, nullptr);
211     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYPEER, 0L);
212     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYHOST, 0L);
213 #endif // HTTP_MULTIPATH_CERT_ENABLE
214 #else
215     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYPEER, 0L);
216     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYHOST, 0L);
217 #endif // WINDOWS_PLATFORM
218     SetServerSSLCertOption(handle);
219     return true;
220 }
221 
SetOtherCurlOption(CURL * handle)222 bool HttpClientTask::SetOtherCurlOption(CURL *handle)
223 {
224     // set proxy
225     std::string host;
226     std::string exclusions;
227     int32_t port = 0;
228     bool tunnel = false;
229     std::string url = request_.GetURL();
230     GetHttpProxyInfo(host, port, exclusions, tunnel);
231     if (!host.empty() && !CommonUtils::IsHostNameExcluded(url, exclusions, ",")) {
232         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_PROXY, host.c_str());
233         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_PROXYPORT, port);
234         auto curlTunnelValue = (url.find("https://") != std::string::npos) ? 1L : 0L;
235         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_HTTPPROXYTUNNEL, curlTunnelValue);
236         auto proxyType = (host.find("https://") != std::string::npos) ? CURLPROXY_HTTPS : CURLPROXY_HTTP;
237         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_PROXYTYPE, proxyType);
238     }
239 
240     SetSSLCertOption(handle);
241     SetDnsCacheOption(handle);
242 
243 #ifdef HTTP_CURL_PRINT_VERBOSE
244     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_VERBOSE, 1L);
245 #endif
246 
247 #ifndef WINDOWS_PLATFORM
248     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_ACCEPT_ENCODING, "");
249 #endif
250 
251     return true;
252 }
253 
SetServerSSLCertOption(CURL * curl)254 bool HttpClientTask::SetServerSSLCertOption(CURL *curl)
255 {
256     auto hostname = CommonUtils::GetHostnameFromURL(request_.GetURL());
257     if (!NetManagerStandard::NetConnClient::GetInstance().IsPinOpenMode(hostname)) {
258         std::string pins;
259         auto ret = NetManagerStandard::NetConnClient::GetInstance().GetPinSetForHostName(hostname, pins);
260         if (ret != 0 || pins.empty()) {
261             NETSTACK_LOGD("Get no pin set by host name invalid");
262         } else {
263             NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PINNEDPUBLICKEY, pins.c_str());
264         }
265     }
266 
267     return true;
268 }
269 
SetUploadOptions(CURL * handle)270 bool HttpClientTask::SetUploadOptions(CURL *handle)
271 {
272     if (filePath_.empty()) {
273         NETSTACK_LOGE("HttpClientTask::SetUploadOptions() filePath_ is empty");
274         error_.SetErrorCode(HttpErrorCode::HTTP_UPLOAD_FAILED);
275         return false;
276     }
277 
278     std::string realPath;
279     if (!CheckFilePath(filePath_, realPath)) {
280         NETSTACK_LOGE("filePath_ does not exist! ");
281         error_.SetErrorCode(HttpErrorCode::HTTP_UPLOAD_FAILED);
282         return false;
283     }
284 
285     file_ = fopen(realPath.c_str(), "rb");
286     if (file_ == nullptr) {
287         NETSTACK_LOGE("HttpClientTask::SetUploadOptions() Failed to open file");
288         error_.SetErrorCode(HttpErrorCode::HTTP_UPLOAD_FAILED);
289         return false;
290     }
291 
292     fseek(file_, 0, SEEK_END);
293     long size = ftell(file_);
294     rewind(file_);
295 
296     // Set the file data and file size to upload
297     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_READDATA, file_);
298     NETSTACK_LOGD("CURLOPT_INFILESIZE=%{public}ld", size);
299     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_INFILESIZE, size);
300     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_UPLOAD, 1L);
301 
302     return true;
303 }
304 
SetTraceOptions(CURL * curl)305 bool HttpClientTask::SetTraceOptions(CURL *curl)
306 {
307     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_RESOLVER_START_DATA, this);
308     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_RESOLVER_START_FUNCTION,
309                                   +[](void *, void *, void *clientp) {
310         if (!clientp) {
311             NETSTACK_LOGE("resolver_start_function clientp pointer is null");
312             return 0;
313         }
314         HttpClientTask *task = static_cast<HttpClientTask *>(clientp);
315         task->GetTrace().Tracepoint(TraceEvents::DNS);
316         return 0;
317     });
318 
319     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SOCKOPTDATA, this);
320     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SOCKOPTFUNCTION,
321                                   +[](void *clientp, curl_socket_t, curlsocktype) {
322         if (!clientp) {
323             NETSTACK_LOGE("sockopt_functon clientp pointer is null");
324             return 0;
325         }
326         HttpClientTask *task = static_cast<HttpClientTask *>(clientp);
327         task->GetTrace().Tracepoint(TraceEvents::TCP);
328         return CURL_SOCKOPT_OK;
329     });
330 
331     //this option may be overriden if HTTP_MULTIPATH_CERT_ENABLE enabled
332     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_DATA, this);
333     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_FUNCTION,
334                                   +[](CURL *, void *, void *clientp) {
335         if (!clientp) {
336             NETSTACK_LOGE("ssl_ctx func clientp pointer is null");
337             return 0;
338         }
339         HttpClientTask *task = static_cast<HttpClientTask *>(clientp);
340         task->GetTrace().Tracepoint(TraceEvents::TLS);
341         return CURL_SOCKOPT_OK;
342     });
343 
344     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PREREQDATA, this);
345     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PREREQFUNCTION,
346                                   +[](void *clientp, char *, char *, int, int) {
347         if (!clientp) {
348             NETSTACK_LOGE("prereq_functon clientp pointer is null");
349             return CURL_PREREQFUNC_OK;
350         }
351         HttpClientTask *task = static_cast<HttpClientTask *>(clientp);
352         task->GetTrace().Tracepoint(TraceEvents::SENDING);
353         return CURL_PREREQFUNC_OK;
354     });
355     return true;
356 }
357 
SetCurlOptions()358 bool HttpClientTask::SetCurlOptions()
359 {
360     auto method = request_.GetMethod();
361     if (method == HttpConstant::HTTP_METHOD_HEAD) {
362         NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_NOBODY, 1L);
363     }
364     SetTraceOptions(curlHandle_);
365 
366     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_URL, request_.GetURL().c_str());
367 
368     if (type_ == TaskType::UPLOAD) {
369         if (!SetUploadOptions(curlHandle_)) {
370             return false;
371         }
372     } else {
373         if (!method.empty()) {
374             NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_CUSTOMREQUEST, request_.GetMethod().c_str());
375         }
376 
377         if ((method.empty() || method == HttpConstant::HTTP_METHOD_POST || method == HttpConstant::HTTP_METHOD_PUT) &&
378             !request_.GetBody().empty()) {
379             NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_POST, 1L);
380             NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_POSTFIELDS, request_.GetBody().c_str());
381             NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_POSTFIELDSIZE, request_.GetBody().size());
382         }
383     }
384 
385     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_XFERINFOFUNCTION, ProgressCallback);
386     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_XFERINFODATA, this);
387     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_NOPROGRESS, 0L);
388 
389     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_WRITEFUNCTION, DataReceiveCallback);
390     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_WRITEDATA, this);
391 
392     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_HEADERFUNCTION, HeaderReceiveCallback);
393     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_HEADERDATA, this);
394 
395     if (curlHeaderList_ != nullptr) {
396         curl_slist_free_all(curlHeaderList_);
397         curlHeaderList_ = nullptr;
398     }
399     for (const auto &header : request_.GetHeaders()) {
400         std::string headerStr = header.first + HttpConstant::HTTP_HEADER_SEPARATOR + header.second;
401         curlHeaderList_ = curl_slist_append(curlHeaderList_, headerStr.c_str());
402     }
403     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_HTTPHEADER, curlHeaderList_);
404 
405     // Some servers don't like requests that are made without a user-agent field, so we provide one
406     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_USERAGENT, HttpConstant::HTTP_DEFAULT_USER_AGENT);
407 
408     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_FOLLOWLOCATION, 1L);
409 
410     /* first #undef CURL_DISABLE_COOKIES in curl config */
411     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_COOKIEFILE, "");
412 
413     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_NOSIGNAL, 1L);
414 
415     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_TIMEOUT_MS, request_.GetTimeout());
416     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_CONNECTTIMEOUT_MS, request_.GetConnectTimeout());
417 
418     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_HTTP_VERSION, GetHttpVersion(request_.GetHttpProtocol()));
419 
420     if (!SetOtherCurlOption(curlHandle_)) {
421         return false;
422     }
423 
424     return true;
425 }
426 
Start()427 bool HttpClientTask::Start()
428 {
429     if (GetStatus() != TaskStatus::IDLE) {
430         NETSTACK_LOGD("task is running, taskId_=%{public}d", taskId_);
431         return false;
432     }
433 
434     if (!CommonUtils::HasInternetPermission()) {
435         NETSTACK_LOGE("Don't Has Internet Permission()");
436         error_.SetErrorCode(HttpErrorCode::HTTP_PERMISSION_DENIED_CODE);
437         return false;
438     }
439 
440     if (!CommonUtils::IsCleartextPermitted(request_.GetURL(), "http://")) {
441         NETSTACK_LOGE("Cleartext not permitted");
442         error_.SetErrorCode(HttpErrorCode::HTTP_CLEARTEXT_NOT_PERMITTED);
443         return false;
444     }
445 
446     if (error_.GetErrorCode() != HttpErrorCode::HTTP_NONE_ERR) {
447         NETSTACK_LOGE("error_.GetErrorCode()=%{public}d", error_.GetErrorCode());
448         return false;
449     }
450 
451     request_.SetRequestTime(HttpTime::GetNowTimeGMT());
452 
453     HttpSession &session = HttpSession::GetInstance();
454     NETSTACK_LOGD("taskId_=%{public}d", taskId_);
455     canceled_ = false;
456 
457     response_.SetRequestTime(HttpTime::GetNowTimeGMT());
458 
459     auto task = shared_from_this();
460     session.StartTask(task);
461     return true;
462 }
463 
Cancel()464 void HttpClientTask::Cancel()
465 {
466     canceled_ = true;
467 }
468 
SetStatus(TaskStatus status)469 void HttpClientTask::SetStatus(TaskStatus status)
470 {
471     status_ = status;
472 }
473 
GetStatus()474 TaskStatus HttpClientTask::GetStatus()
475 {
476     return status_;
477 }
478 
GetType()479 TaskType HttpClientTask::GetType()
480 {
481     return type_;
482 }
483 
GetFilePath()484 const std::string &HttpClientTask::GetFilePath()
485 {
486     return filePath_;
487 }
488 
GetTaskId()489 unsigned int HttpClientTask::GetTaskId()
490 {
491     return taskId_;
492 }
493 
OnSuccess(const std::function<void (const HttpClientRequest & request,const HttpClientResponse & response)> & onSucceeded)494 void HttpClientTask::OnSuccess(
495     const std::function<void(const HttpClientRequest &request, const HttpClientResponse &response)> &onSucceeded)
496 {
497     onSucceeded_ = onSucceeded;
498 }
499 
OnCancel(const std::function<void (const HttpClientRequest & request,const HttpClientResponse & response)> & onCanceled)500 void HttpClientTask::OnCancel(
501     const std::function<void(const HttpClientRequest &request, const HttpClientResponse &response)> &onCanceled)
502 {
503     onCanceled_ = onCanceled;
504 }
505 
OnFail(const std::function<void (const HttpClientRequest & request,const HttpClientResponse & response,const HttpClientError & error)> & onFailed)506 void HttpClientTask::OnFail(
507     const std::function<void(const HttpClientRequest &request, const HttpClientResponse &response,
508                              const HttpClientError &error)> &onFailed)
509 {
510     onFailed_ = onFailed;
511 }
512 
OnDataReceive(const std::function<void (const HttpClientRequest & request,const uint8_t * data,size_t length)> & onDataReceive)513 void HttpClientTask::OnDataReceive(
514     const std::function<void(const HttpClientRequest &request, const uint8_t *data, size_t length)> &onDataReceive)
515 {
516     onDataReceive_ = onDataReceive;
517 }
518 
OnProgress(const std::function<void (const HttpClientRequest & request,u_long dlTotal,u_long dlNow,u_long ulTotal,u_long ulNow)> & onProgress)519 void HttpClientTask::OnProgress(const std::function<void(const HttpClientRequest &request, u_long dlTotal, u_long dlNow,
520                                                          u_long ulTotal, u_long ulNow)> &onProgress)
521 {
522     onProgress_ = onProgress;
523 }
524 
DataReceiveCallback(const void * data,size_t size,size_t memBytes,void * userData)525 size_t HttpClientTask::DataReceiveCallback(const void *data, size_t size, size_t memBytes, void *userData)
526 {
527     auto task = static_cast<HttpClientTask *>(userData);
528     NETSTACK_LOGD("taskId=%{public}d size=%{public}zu memBytes=%{public}zu", task->taskId_, size, memBytes);
529 
530     if (task->canceled_) {
531         NETSTACK_LOGD("canceled");
532         return 0;
533     }
534 
535     if (task->onDataReceive_) {
536         HttpClientRequest request = task->request_;
537         task->onDataReceive_(request, static_cast<const uint8_t *>(data), size * memBytes);
538     }
539 
540     if (task->response_.GetResult().size() < MAX_LIMIT) {
541         task->response_.AppendResult(data, size * memBytes);
542     }
543 
544     return size * memBytes;
545 }
546 
ProgressCallback(void * userData,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)547 int HttpClientTask::ProgressCallback(void *userData, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
548                                      curl_off_t ulnow)
549 {
550     auto task = static_cast<HttpClientTask *>(userData);
551     NETSTACK_LOGD("taskId=%{public}d dltotal=%{public}" CURL_FORMAT_CURL_OFF_T " dlnow=%{public}" CURL_FORMAT_CURL_OFF_T
552                   " ultotal=%{public}" CURL_FORMAT_CURL_OFF_T " ulnow=%{public}" CURL_FORMAT_CURL_OFF_T,
553                   task->taskId_, dltotal, dlnow, ultotal, ulnow);
554 
555     if (task->canceled_) {
556         NETSTACK_LOGD("canceled");
557         return CURLE_ABORTED_BY_CALLBACK;
558     }
559 
560     if (task->onProgress_) {
561         task->onProgress_(task->request_, dltotal, dlnow, ultotal, ulnow);
562     }
563 
564     return 0;
565 }
566 
HeaderReceiveCallback(const void * data,size_t size,size_t memBytes,void * userData)567 size_t HttpClientTask::HeaderReceiveCallback(const void *data, size_t size, size_t memBytes, void *userData)
568 {
569     auto task = static_cast<HttpClientTask *>(userData);
570     task->GetTrace().Tracepoint(TraceEvents::RECEIVING);
571     NETSTACK_LOGD("taskId=%{public}d size=%{public}zu memBytes=%{public}zu", task->taskId_, size, memBytes);
572 
573     if (size * memBytes > MAX_LIMIT) {
574         NETSTACK_LOGE("size * memBytes(%{public}zu) > MAX_LIMIT(%{public}zu)", size * memBytes, MAX_LIMIT);
575         return 0;
576     }
577 
578     task->response_.AppendHeader(static_cast<const char *>(data), size * memBytes);
579 
580     return size * memBytes;
581 }
582 
ProcessCookie(CURL * handle)583 void HttpClientTask::ProcessCookie(CURL *handle)
584 {
585     struct curl_slist *cookies = nullptr;
586     if (handle == nullptr) {
587         NETSTACK_LOGE("HttpClientTask::ProcessCookie() handle == nullptr");
588         return;
589     }
590 
591     CURLcode res = curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &cookies);
592     if (res != CURLE_OK) {
593         NETSTACK_LOGE("HttpClientTask::ProcessCookie() curl_easy_getinfo() error! res = %{public}d", res);
594         return;
595     }
596 
597     while (cookies) {
598         response_.AppendCookies(cookies->data, strlen(cookies->data));
599         if (cookies->next != nullptr) {
600             response_.AppendCookies(HttpConstant::HTTP_LINE_SEPARATOR, strlen(HttpConstant::HTTP_LINE_SEPARATOR));
601         }
602         cookies = cookies->next;
603     }
604 }
605 
ProcessResponseCode()606 bool HttpClientTask::ProcessResponseCode()
607 {
608     int64_t result = 0;
609     CURLcode code = curl_easy_getinfo(curlHandle_, CURLINFO_RESPONSE_CODE, &result);
610     if (code != CURLE_OK) {
611         error_.SetCURLResult(code);
612         return false;
613     }
614     auto resultCode = static_cast<ResponseCode>(result);
615     NETSTACK_LOGD("id=%{public}d, code=%{public}d", taskId_, resultCode);
616     response_.SetResponseCode(resultCode);
617 
618     return true;
619 }
620 
GetTimingFromCurl(CURL * handle,CURLINFO info) const621 double HttpClientTask::GetTimingFromCurl(CURL *handle, CURLINFO info) const
622 {
623     curl_off_t timing;
624     CURLcode result = curl_easy_getinfo(handle, info, &timing);
625     if (result != CURLE_OK) {
626         NETSTACK_LOGE("Failed to get timing: %{public}d, %{public}s", info, curl_easy_strerror(result));
627         return 0;
628     }
629     return Timing::TimeUtils::Microseconds2Milliseconds(timing);
630 }
631 
GetSizeFromCurl(CURL * handle) const632 curl_off_t HttpClientTask::GetSizeFromCurl(CURL *handle) const
633 {
634     auto info = CURLINFO_SIZE_DOWNLOAD_T;
635     auto method = request_.GetMethod();
636     if (((method.empty() || method == HttpConstant::HTTP_METHOD_POST || method == HttpConstant::HTTP_METHOD_PUT) &&
637         !request_.GetBody().empty()) || type_ == TaskType::UPLOAD) {
638         info = CURLINFO_SIZE_UPLOAD_T;
639     }
640     curl_off_t size = 0;
641     CURLcode result = curl_easy_getinfo(handle, info, &size);
642     if (result != CURLE_OK) {
643         NETSTACK_LOGE("curl_easy_getinfo failed, %{public}d, %{public}s", info, curl_easy_strerror(result));
644         return 0;
645     }
646     return size;
647 }
648 
DumpHttpPerformance()649 void HttpClientTask::DumpHttpPerformance()
650 {
651     if (curlHandle_ == nullptr) {
652         NETSTACK_LOGE("Ignore dumping http performance, curlHandle_ == nullptr");
653         return;
654     }
655     auto dnsTime = GetTimingFromCurl(curlHandle_, CURLINFO_NAMELOOKUP_TIME_T);
656     auto connectTime = GetTimingFromCurl(curlHandle_, CURLINFO_CONNECT_TIME_T);
657     auto tlsTime = GetTimingFromCurl(curlHandle_, CURLINFO_APPCONNECT_TIME_T);
658     auto firstSendTime = GetTimingFromCurl(curlHandle_, CURLINFO_PRETRANSFER_TIME_T);
659     auto firstRecvTime = GetTimingFromCurl(curlHandle_, CURLINFO_STARTTRANSFER_TIME_T);
660     auto totalTime = GetTimingFromCurl(curlHandle_, CURLINFO_TOTAL_TIME_T);
661     auto redirectTime = GetTimingFromCurl(curlHandle_, CURLINFO_REDIRECT_TIME_T);
662 
663     response_.performanceInfo_.dnsTiming = dnsTime;
664     response_.performanceInfo_.connectTiming = connectTime;
665     response_.performanceInfo_.tlsTiming = tlsTime;
666     response_.performanceInfo_.firstSendTiming = firstSendTime;
667     response_.performanceInfo_.firstReceiveTiming = firstRecvTime;
668     response_.performanceInfo_.totalTiming = totalTime;
669     response_.performanceInfo_.redirectTiming = redirectTime;
670 
671     int64_t responseCode = 0;
672     (void)curl_easy_getinfo(curlHandle_,  CURLINFO_RESPONSE_CODE, &responseCode);
673 
674     /*
675     CURL_HTTP_VERSION_NONE         0
676     CURL_HTTP_VERSION_1_0          1
677     CURL_HTTP_VERSION_1_1          2
678     CURL_HTTP_VERSION_2            3
679     */
680     int64_t httpVer = CURL_HTTP_VERSION_NONE;
681     (void)curl_easy_getinfo(curlHandle_,  CURLINFO_HTTP_VERSION, &httpVer);
682     long osErr = 0;
683     (void)curl_easy_getinfo(curlHandle_,  CURLINFO_OS_ERRNO, &osErr);
684 
685     curl_off_t size = GetSizeFromCurl(curlHandle_);
686     char *ip = nullptr;
687     curl_easy_getinfo(curlHandle_, CURLINFO_PRIMARY_IP, &ip);
688     NETSTACK_LOGI(
689         "taskid=%{public}d"
690         ", size:%{public}" CURL_FORMAT_CURL_OFF_T
691         ", dns:%{public}.3f"
692         ", connect:%{public}.3f"
693         ", tls:%{public}.3f"
694         ", firstSend:%{public}.3f"
695         ", firstRecv:%{public}.3f"
696         ", total:%{public}.3f"
697         ", redirect:%{public}.3f"
698         ", errCode:%{public}d"
699         ", RespCode:%{public}s"
700         ", httpVer:%{public}s"
701         ", method:%{public}s"
702         ", osErr:%{public}ld",
703         taskId_, size, dnsTime, connectTime == 0 ? 0 : connectTime - dnsTime,
704         tlsTime == 0 ? 0 : tlsTime - connectTime,
705         firstSendTime == 0 ? 0 : firstSendTime - std::max({dnsTime, connectTime, tlsTime}),
706         firstRecvTime == 0 ? 0 : firstRecvTime - firstSendTime, totalTime, redirectTime,
707         error_.GetErrorCode(), std::to_string(responseCode).c_str(), std::to_string(httpVer).c_str(),
708         request_.GetMethod().c_str(), osErr);
709 
710     if (EventReport::GetInstance().IsValid()) {
711         HttpPerfInfo httpPerfInfo;
712         httpPerfInfo.totalTime = totalTime;
713         httpPerfInfo.size = size;
714         httpPerfInfo.dnsTime = dnsTime;
715         httpPerfInfo.tlsTime = tlsTime == 0 ? 0 : tlsTime - connectTime;
716         httpPerfInfo.tcpTime = connectTime == 0 ? 0 : connectTime - dnsTime;
717         httpPerfInfo.firstRecvTime = firstRecvTime == 0 ? 0 : firstRecvTime - firstSendTime;
718         httpPerfInfo.responseCode = responseCode;
719         httpPerfInfo.version = std::to_string(httpVer);
720         httpPerfInfo.osErr = static_cast<int64_t>(osErr);
721         httpPerfInfo.errCode = error_.GetErrorCode();
722         httpPerfInfo.ipType = CommonUtils::DetectIPType((ip != nullptr) ? ip : "");
723         EventReport::GetInstance().ProcessHttpPerfHiSysevent(httpPerfInfo);
724     }
725 }
726 
ProcessResponse(CURLMsg * msg)727 void HttpClientTask::ProcessResponse(CURLMsg *msg)
728 {
729     trace_->Finish();
730     CURLcode code = msg->data.result;
731     NETSTACK_LOGD("taskid=%{public}d code=%{public}d", taskId_, code);
732     error_.SetCURLResult(code);
733     response_.SetResponseTime(HttpTime::GetNowTimeGMT());
734 
735     DumpHttpPerformance();
736 
737     if (CURLE_ABORTED_BY_CALLBACK == code) {
738         (void)ProcessResponseCode();
739         if (onCanceled_) {
740             onCanceled_(request_, response_);
741         }
742         return;
743     }
744 
745     if (code != CURLE_OK) {
746         if (onFailed_) {
747             onFailed_(request_, response_, error_);
748         }
749         return;
750     }
751 
752     ProcessCookie(curlHandle_);
753     response_.ParseHeaders();
754 
755     if (ProcessResponseCode()) {
756         if (onSucceeded_) {
757             onSucceeded_(request_, response_);
758         }
759     } else if (onFailed_) {
760         onFailed_(request_, response_, error_);
761     }
762 #if HAS_NETMANAGER_BASE
763     HttpClientNetworkMessage httpClientNetworkMessage(std::to_string(GetTaskId()), request_, response_, curlHandle_);
764     networkProfilerUtils_->NetworkProfiling(httpClientNetworkMessage);
765 #endif
766 }
767 
SetResponse(const HttpClientResponse & response)768 void HttpClientTask::SetResponse(const HttpClientResponse &response)
769 {
770     response_ = response;
771 }
772 
GetTrace()773 RequestTracer::Trace &HttpClientTask::GetTrace()
774 {
775     return *trace_;
776 }
777 
778 
SetDnsCacheOption(CURL * handle)779 bool HttpClientTask::SetDnsCacheOption(CURL *handle)
780 {
781 #if HAS_NETMANAGER_BASE
782     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
783 #endif
784     return true;
785 }
786 } // namespace HttpClient
787 } // namespace NetStack
788 } // namespace OHOS
789