• 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 "network_security_config.h"
30 #include "netstack_common_utils.h"
31 #include "netstack_log.h"
32 #include "timing.h"
33 #if HAS_NETMANAGER_BASE
34 #include "http_client_network_message.h"
35 #include "netsys_client.h"
36 #endif
37 #include "netstack_hisysevent.h"
38 #ifdef HTTP_HANDOVER_FEATURE
39 #include "http_handover_info.h"
40 #endif
41 
42 #define NETSTACK_CURL_EASY_SET_OPTION(handle, opt, data)                                                 \
43     do {                                                                                                 \
44         CURLcode result = curl_easy_setopt(handle, opt, data);                                           \
45         if (result != CURLE_OK) {                                                                        \
46             const char *err = curl_easy_strerror(result);                                                \
47             error_.SetCURLResult(result);                                                                \
48             NETSTACK_LOGE("Failed to set option: %{public}s, %{public}s %{public}d", #opt, err, result); \
49             return false;                                                                                \
50         }                                                                                                \
51     } while (0)
52 
53 namespace OHOS {
54 namespace NetStack {
55 namespace HttpClient {
56 
57 static const size_t MAX_LIMIT = HttpConstant::MAX_DATA_LIMIT;
58 static constexpr const char *HTTP_AF_ONLYV4 = "ONLY_V4";
59 static constexpr const char *HTTP_AF_ONLYV6 = "ONLY_V6";
60 
61 std::atomic<uint32_t> HttpClientTask::nextTaskId_(0);
62 
CheckFilePath(const std::string & fileName,std::string & realPath)63 bool CheckFilePath(const std::string &fileName, std::string &realPath)
64 {
65     char tmpPath[PATH_MAX] = {0};
66     if (!realpath(static_cast<const char *>(fileName.c_str()), tmpPath)) {
67         NETSTACK_LOGE("file name is error");
68         return false;
69     }
70 
71     realPath = tmpPath;
72     return true;
73 }
74 
HttpClientTask(const HttpClientRequest & request)75 HttpClientTask::HttpClientTask(const HttpClientRequest &request)
76     : HttpClientTask(request, DEFAULT, std::string())
77 {
78 }
79 
HttpClientTask(const HttpClientRequest & request,TaskType type,const std::string & filePath)80 HttpClientTask::HttpClientTask(const HttpClientRequest &request, TaskType type, const std::string &filePath)
81     : request_(request),
82       type_(type),
83       status_(IDLE),
84       taskId_(nextTaskId_++),
85       curlHeaderList_(nullptr),
86       canceled_(false),
87       filePath_(filePath),
88       file_(nullptr),
89       trace_(std::make_unique<RequestTracer::Trace>("HttpClientTask" + std::to_string(taskId_)))
90 {
91     curlHandle_ = curl_easy_init();
92     if (!curlHandle_) {
93         NETSTACK_LOGE("Failed to create task!");
94         return;
95     }
96 
97     SetCurlOptions();
98 #if HAS_NETMANAGER_BASE
99     networkProfilerUtils_ = std::make_unique<NetworkProfilerUtils>();
100 #endif
101 }
102 
~HttpClientTask()103 HttpClientTask::~HttpClientTask()
104 {
105     NETSTACK_LOGD("Destroy: taskId_=%{public}d", taskId_);
106     if (curlHeaderList_ != nullptr) {
107         curl_slist_free_all(curlHeaderList_);
108         curlHeaderList_ = nullptr;
109     }
110 
111     if (curlHandle_) {
112         curl_easy_cleanup(curlHandle_);
113         curlHandle_ = nullptr;
114     }
115 
116     if (file_ != nullptr) {
117         fclose(file_);
118         file_ = nullptr;
119     }
120 }
121 
GetHttpVersion(HttpProtocol ptcl) const122 uint32_t HttpClientTask::GetHttpVersion(HttpProtocol ptcl) const
123 {
124     if (ptcl == HttpProtocol::HTTP1_1) {
125         NETSTACK_LOGD("CURL_HTTP_VERSION_1_1");
126         return CURL_HTTP_VERSION_1_1;
127     } else if (ptcl == HttpProtocol::HTTP2) {
128         NETSTACK_LOGD("CURL_HTTP_VERSION_2_0");
129         return CURL_HTTP_VERSION_2_0;
130     } else if (ptcl == HttpProtocol::HTTP3) {
131         NETSTACK_LOGD("CURL_HTTP_VERSION_3");
132         return CURL_HTTP_VERSION_3;
133     }
134     return CURL_HTTP_VERSION_NONE;
135 }
136 
GetHttpProxyInfo(std::string & host,int32_t & port,std::string & exclusions,bool & tunnel)137 void HttpClientTask::GetHttpProxyInfo(std::string &host, int32_t &port, std::string &exclusions, bool &tunnel)
138 {
139     if (request_.GetHttpProxyType() == HttpProxyType::USE_SPECIFIED) {
140         HttpProxy proxy = request_.GetHttpProxy();
141         host = proxy.host;
142         port = proxy.port;
143         exclusions = proxy.exclusions;
144         tunnel = proxy.tunnel;
145     } else {
146         using namespace NetManagerStandard;
147         NetManagerStandard::HttpProxy httpProxy;
148         NetConnClient::GetInstance().GetDefaultHttpProxy(httpProxy);
149         host = httpProxy.GetHost();
150         port = httpProxy.GetPort();
151         exclusions = CommonUtils::ToString(httpProxy.GetExclusionList());
152     }
153 }
154 
TrustUser0AndUserCa(std::vector<std::string> & certs)155 [[maybe_unused]] void TrustUser0AndUserCa(std::vector<std::string> &certs)
156 {
157 #ifdef HTTP_MULTIPATH_CERT_ENABLE
158     if (NetManagerStandard::NetworkSecurityConfig::GetInstance().TrustUser0Ca()) {
159         certs.emplace_back(HttpConstant::USER_CERT_ROOT_PATH);
160     }
161     if (NetManagerStandard::NetworkSecurityConfig::GetInstance().TrustUserCa()) {
162         certs.emplace_back(HttpConstant::USER_CERT_BASE_PATH +
163                            std::to_string(getuid() / HttpConstant::UID_TRANSFORM_DIVISOR));
164     }
165 #endif
166 }
167 
SslCtxFunction(CURL * curl,void * sslCtx)168 CURLcode HttpClientTask::SslCtxFunction(CURL *curl, void *sslCtx)
169 {
170 #ifdef HTTP_MULTIPATH_CERT_ENABLE
171     if (sslCtx == nullptr) {
172         NETSTACK_LOGE("sslCtx is null");
173         return CURLE_SSL_CERTPROBLEM;
174     }
175     std::vector<std::string> certs;
176     TrustUser0AndUserCa(certs);
177     certs.emplace_back(HttpConstant::HTTP_PREPARE_CA_PATH);
178 
179     for (const auto &path : certs) {
180         if (path.empty() || access(path.c_str(), F_OK) != 0) {
181             NETSTACK_LOGD("certificate directory path is not exist");
182             continue;
183         }
184         if (!SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(sslCtx), nullptr, path.c_str())) {
185             NETSTACK_LOGE("loading certificates from directory error.");
186             continue;
187         }
188     }
189 
190     if (access(request_.GetCaPath().c_str(), F_OK) != 0) {
191         NETSTACK_LOGD("certificate directory path is not exist");
192     } else if (!SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(sslCtx), request_.GetCaPath().c_str(), nullptr)) {
193         NETSTACK_LOGE("loading certificates from context cert error.");
194     }
195 #endif // HTTP_MULTIPATH_CERT_ENABLE
196     return CURLE_OK;
197 }
198 
SetSSLCertOption(CURL * handle)199 bool HttpClientTask::SetSSLCertOption(CURL *handle)
200 {
201 #ifndef WINDOWS_PLATFORM
202 #ifdef HTTP_MULTIPATH_CERT_ENABLE
203     curl_ssl_ctx_callback sslCtxFunc = [](CURL *curl, void *sslCtx, void *parm) -> CURLcode {
204         HttpClientTask *task = static_cast<HttpClientTask *>(parm);
205         if (!task) {
206             return CURLE_SSL_CERTPROBLEM;
207         }
208         task->GetTrace().Tracepoint(TraceEvents::TLS);
209         return task->SslCtxFunction(curl, sslCtx);
210     };
211     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_CTX_FUNCTION, sslCtxFunc);
212     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_CTX_DATA, this);
213     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_CAINFO, nullptr);
214     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYPEER, 1L);
215     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYHOST, 2L);
216 #else
217     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_CAINFO, nullptr);
218     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYPEER, 0L);
219     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYHOST, 0L);
220 #endif // HTTP_MULTIPATH_CERT_ENABLE
221 #else
222     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYPEER, 0L);
223     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSL_VERIFYHOST, 0L);
224 #endif // WINDOWS_PLATFORM
225     SetServerSSLCertOption(handle);
226 
227     HttpClientCert clientCert = request_.GetClientCert();
228     if (clientCert.certPath.empty()) {
229         NETSTACK_LOGD("SetSSLCertOption param is empty.");
230         return false;
231     }
232     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSLCERT, clientCert.certPath.c_str());
233     if (!clientCert.keyPath.empty()) {
234         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSLKEY, clientCert.keyPath.c_str());
235     }
236     if (!clientCert.certType.empty()) {
237         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_SSLCERTTYPE, clientCert.certType.c_str());
238     }
239     if (clientCert.keyPassword.length() > 0) {
240         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_KEYPASSWD, clientCert.keyPassword.c_str());
241     }
242     return true;
243 }
244 
SetRequestOption(CURL * handle)245 bool HttpClientTask::SetRequestOption(CURL *handle)
246 {
247     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_TIMEOUT_MS, request_.GetTimeout());
248     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_CONNECTTIMEOUT_MS, request_.GetConnectTimeout());
249     const std::string range = GetRangeString();
250     if (!range.empty()) {
251         if (request_.GetMethod() == HttpConstant::HTTP_METHOD_PUT) {
252             error_.SetErrorCode(HttpErrorCode::HTTP_CURLE_RANGE_ERROR);
253             NETSTACK_LOGE("For HTTP PUT uploads this option should not be used");
254             return false;
255         }
256         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_RANGE, range.c_str());
257     } else {
258         // Some servers don't like requests that are made without a user-agent field, so we provide one
259         NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_USERAGENT, HttpConstant::HTTP_DEFAULT_USER_AGENT);
260     }
261     SetSSLCertOption(handle);
262     SetDnsCacheOption(handle);
263     SetIpResolve(handle);
264     return true;
265 }
266 
SetOtherCurlOption(CURL * handle)267 bool HttpClientTask::SetOtherCurlOption(CURL *handle)
268 {
269     // set proxy
270     std::string host;
271     std::string exclusions;
272     int32_t port = 0;
273     bool tunnel = false;
274     std::string url = request_.GetURL();
275     GetHttpProxyInfo(host, port, exclusions, tunnel);
276     if (!host.empty() && !CommonUtils::IsHostNameExcluded(url, exclusions, ",")) {
277         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_PROXY, host.c_str());
278         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_PROXYPORT, port);
279         auto curlTunnelValue = (url.find("https://") != std::string::npos) ? 1L : 0L;
280         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_HTTPPROXYTUNNEL, curlTunnelValue);
281         auto proxyType = (host.find("https://") != std::string::npos) ? CURLPROXY_HTTPS : CURLPROXY_HTTP;
282         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_PROXYTYPE, proxyType);
283     }
284 
285     const std::string range = GetRangeString();
286     if (!range.empty()) {
287         if (request_.GetMethod() == HttpConstant::HTTP_METHOD_PUT) {
288             error_.SetErrorCode(HttpErrorCode::HTTP_CURLE_RANGE_ERROR);
289             NETSTACK_LOGE("For HTTP PUT uploads this option should not be used");
290             return false;
291         }
292         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_RANGE, range.c_str());
293     } else {
294         // Some servers don't like requests that are made without a user-agent field, so we provide one
295         NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_USERAGENT, HttpConstant::HTTP_DEFAULT_USER_AGENT);
296     }
297 
298     SetSSLCertOption(handle);
299     SetDnsCacheOption(handle);
300     SetIpResolve(handle);
301 
302 #ifdef HTTP_CURL_PRINT_VERBOSE
303     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_VERBOSE, 1L);
304 #endif
305 
306 #ifndef WINDOWS_PLATFORM
307     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_ACCEPT_ENCODING, "");
308 #endif
309 
310     return true;
311 }
312 
SetIpResolve(CURL * handle)313 bool HttpClientTask::SetIpResolve(CURL *handle)
314 {
315     std::string addressFamily = request_.GetAddressFamily();
316     if (addressFamily.empty()) {
317 #if HAS_NETMANAGER_BASE
318         if (!NetSysIsIpv6Enable(0)) {
319             NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
320         }
321 #endif
322         return true;
323     }
324     if (addressFamily.compare(HTTP_AF_ONLYV4) == 0) {
325         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
326     }
327     if (addressFamily.compare(HTTP_AF_ONLYV6) == 0) {
328         NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
329     }
330     return true;
331 }
332 
GetRangeString() const333 std::string HttpClientTask::GetRangeString() const
334 {
335     bool isSetFrom = request_.GetResumeFrom() >= MIN_RESUM_NUMBER;
336     bool isSetTo = request_.GetResumeTo() >= MIN_RESUM_NUMBER;
337     if (!isSetTo && !isSetFrom) {
338         return "";
339     } else if (!isSetTo && isSetFrom) {
340         return std::to_string(request_.GetResumeFrom()) + '-';
341     } else if (isSetTo && !isSetFrom) {
342         return '-' + std::to_string(request_.GetResumeTo());
343     } else if (request_.GetResumeTo() <= request_.GetResumeFrom()) {
344         return "";
345     } else {
346         return std::to_string(request_.GetResumeFrom()) + '-' + std::to_string(request_.GetResumeTo());
347     }
348 }
349 
SetServerSSLCertOption(CURL * curl)350 bool HttpClientTask::SetServerSSLCertOption(CURL *curl)
351 {
352     auto hostname = CommonUtils::GetHostnameFromURL(request_.GetURL());
353     if (!NetManagerStandard::NetworkSecurityConfig::GetInstance().IsPinOpenMode(hostname)) {
354         std::string pins;
355         auto ret = NetManagerStandard::NetworkSecurityConfig::GetInstance().GetPinSetForHostName(hostname, pins);
356         if (ret != 0 || pins.empty()) {
357             NETSTACK_LOGD("Get no pin set by host name invalid");
358         } else {
359             NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PINNEDPUBLICKEY, pins.c_str());
360         }
361     }
362 
363     return true;
364 }
365 
SetUploadOptions(CURL * handle)366 bool HttpClientTask::SetUploadOptions(CURL *handle)
367 {
368     if (filePath_.empty()) {
369         NETSTACK_LOGE("HttpClientTask::SetUploadOptions() filePath_ is empty");
370         error_.SetErrorCode(HttpErrorCode::HTTP_UPLOAD_FAILED);
371         return false;
372     }
373 
374     std::string realPath;
375     if (!CheckFilePath(filePath_, realPath)) {
376         NETSTACK_LOGE("filePath_ does not exist! ");
377         error_.SetErrorCode(HttpErrorCode::HTTP_UPLOAD_FAILED);
378         return false;
379     }
380 
381     file_ = fopen(realPath.c_str(), "rb");
382     if (file_ == nullptr) {
383         NETSTACK_LOGE("HttpClientTask::SetUploadOptions() Failed to open file");
384         error_.SetErrorCode(HttpErrorCode::HTTP_UPLOAD_FAILED);
385         return false;
386     }
387 
388     fseek(file_, 0, SEEK_END);
389     long size = ftell(file_);
390     rewind(file_);
391 
392     // Set the file data and file size to upload
393     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_READDATA, file_);
394     NETSTACK_LOGD("CURLOPT_INFILESIZE=%{public}ld", size);
395     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_INFILESIZE, size);
396     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_UPLOAD, 1L);
397 
398     return true;
399 }
400 
SetTraceOptions(CURL * curl)401 bool HttpClientTask::SetTraceOptions(CURL *curl)
402 {
403     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_RESOLVER_START_DATA, this);
404     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_RESOLVER_START_FUNCTION,
405                                   +[](void *, void *, void *clientp) {
406         if (!clientp) {
407             NETSTACK_LOGE("resolver_start_function clientp pointer is null");
408             return 0;
409         }
410         HttpClientTask *task = static_cast<HttpClientTask *>(clientp);
411         task->GetTrace().Tracepoint(TraceEvents::DNS);
412         return 0;
413     });
414 
415     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SOCKOPTDATA, this);
416     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SOCKOPTFUNCTION,
417                                   +[](void *clientp, curl_socket_t, curlsocktype) {
418         if (!clientp) {
419             NETSTACK_LOGE("sockopt_functon clientp pointer is null");
420             return 0;
421         }
422         HttpClientTask *task = static_cast<HttpClientTask *>(clientp);
423         task->GetTrace().Tracepoint(TraceEvents::TCP);
424         return CURL_SOCKOPT_OK;
425     });
426 
427     //this option may be overriden if HTTP_MULTIPATH_CERT_ENABLE enabled
428     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_DATA, this);
429     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_FUNCTION,
430                                   +[](CURL *, void *, void *clientp) {
431         if (!clientp) {
432             NETSTACK_LOGE("ssl_ctx func clientp pointer is null");
433             return 0;
434         }
435         HttpClientTask *task = static_cast<HttpClientTask *>(clientp);
436         task->GetTrace().Tracepoint(TraceEvents::TLS);
437         return CURL_SOCKOPT_OK;
438     });
439 
440     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PREREQDATA, this);
441     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PREREQFUNCTION,
442                                   +[](void *clientp, char *, char *, int, int) {
443         if (!clientp) {
444             NETSTACK_LOGE("prereq_functon clientp pointer is null");
445             return CURL_PREREQFUNC_OK;
446         }
447         HttpClientTask *task = static_cast<HttpClientTask *>(clientp);
448         task->GetTrace().Tracepoint(TraceEvents::SENDING);
449         return CURL_PREREQFUNC_OK;
450     });
451     return true;
452 }
453 
SetCurlOptions()454 bool HttpClientTask::SetCurlOptions()
455 {
456     auto method = request_.GetMethod();
457     if (method == HttpConstant::HTTP_METHOD_HEAD) {
458         NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_NOBODY, 1L);
459     }
460     SetTraceOptions(curlHandle_);
461 
462     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_URL, request_.GetURL().c_str());
463 
464     if (type_ == TaskType::UPLOAD) {
465         if (!SetUploadOptions(curlHandle_)) {
466             return false;
467         }
468     } else {
469         if (!method.empty()) {
470             NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_CUSTOMREQUEST, request_.GetMethod().c_str());
471         }
472 
473         if ((method.empty() || method == HttpConstant::HTTP_METHOD_POST || method == HttpConstant::HTTP_METHOD_PUT) &&
474             !request_.GetBody().empty()) {
475             NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_POST, 1L);
476             NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_POSTFIELDS, request_.GetBody().c_str());
477             NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_POSTFIELDSIZE, request_.GetBody().size());
478         }
479     }
480 
481     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_XFERINFOFUNCTION, ProgressCallback);
482     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_XFERINFODATA, this);
483     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_NOPROGRESS, 0L);
484 
485     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_WRITEFUNCTION, DataReceiveCallback);
486     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_WRITEDATA, this);
487 
488     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_HEADERFUNCTION, HeaderReceiveCallback);
489     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_HEADERDATA, this);
490 
491     if (curlHeaderList_ != nullptr) {
492         curl_slist_free_all(curlHeaderList_);
493         curlHeaderList_ = nullptr;
494     }
495     for (const auto &header : request_.GetHeaders()) {
496         std::string headerStr = header.first + HttpConstant::HTTP_HEADER_SEPARATOR + header.second;
497         curlHeaderList_ = curl_slist_append(curlHeaderList_, headerStr.c_str());
498     }
499     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_HTTPHEADER, curlHeaderList_);
500 
501     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_FOLLOWLOCATION, 1L);
502 
503     /* first #undef CURL_DISABLE_COOKIES in curl config */
504     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_COOKIEFILE, "");
505 
506     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_NOSIGNAL, 1L);
507 
508     NETSTACK_CURL_EASY_SET_OPTION(curlHandle_, CURLOPT_HTTP_VERSION, GetHttpVersion(request_.GetHttpProtocol()));
509 
510     if (!SetRequestOption(curlHandle_)) {
511         return false;
512     }
513 
514     if (!SetOtherCurlOption(curlHandle_)) {
515         return false;
516     }
517 
518     return true;
519 }
520 
Start()521 bool HttpClientTask::Start()
522 {
523     if (GetStatus() != TaskStatus::IDLE) {
524         NETSTACK_LOGD("task is running, taskId_=%{public}d", taskId_);
525         return false;
526     }
527 
528     if (!CommonUtils::HasInternetPermission()) {
529         NETSTACK_LOGE("Don't Has Internet Permission()");
530         error_.SetErrorCode(HttpErrorCode::HTTP_PERMISSION_DENIED_CODE);
531         return false;
532     }
533 
534     if (!CommonUtils::IsCleartextPermitted(request_.GetURL(), "http://")) {
535         NETSTACK_LOGE("Cleartext not permitted");
536         error_.SetErrorCode(HttpErrorCode::HTTP_CLEARTEXT_NOT_PERMITTED);
537         return false;
538     }
539 
540     if (error_.GetErrorCode() != HttpErrorCode::HTTP_NONE_ERR) {
541         NETSTACK_LOGE("error_.GetErrorCode()=%{public}d", error_.GetErrorCode());
542         return false;
543     }
544 
545     request_.SetRequestTime(HttpTime::GetNowTimeGMT());
546 
547     HttpSession &session = HttpSession::GetInstance();
548     NETSTACK_LOGD("taskId_=%{public}d", taskId_);
549     canceled_ = false;
550 
551     response_.SetRequestTime(HttpTime::GetNowTimeGMT());
552 
553     auto task = shared_from_this();
554     session.StartTask(task);
555     return true;
556 }
557 
Cancel()558 void HttpClientTask::Cancel()
559 {
560     canceled_ = true;
561 }
562 
SetStatus(TaskStatus status)563 void HttpClientTask::SetStatus(TaskStatus status)
564 {
565     status_ = status;
566 }
567 
GetStatus()568 TaskStatus HttpClientTask::GetStatus()
569 {
570     return status_;
571 }
572 
GetType()573 TaskType HttpClientTask::GetType()
574 {
575     return type_;
576 }
577 
GetFilePath()578 const std::string &HttpClientTask::GetFilePath()
579 {
580     return filePath_;
581 }
582 
GetTaskId()583 unsigned int HttpClientTask::GetTaskId()
584 {
585     return taskId_;
586 }
587 
OnSuccess(const std::function<void (const HttpClientRequest & request,const HttpClientResponse & response)> & onSucceeded)588 void HttpClientTask::OnSuccess(
589     const std::function<void(const HttpClientRequest &request, const HttpClientResponse &response)> &onSucceeded)
590 {
591     onSucceeded_ = onSucceeded;
592 }
593 
OnCancel(const std::function<void (const HttpClientRequest & request,const HttpClientResponse & response)> & onCanceled)594 void HttpClientTask::OnCancel(
595     const std::function<void(const HttpClientRequest &request, const HttpClientResponse &response)> &onCanceled)
596 {
597     onCanceled_ = onCanceled;
598 }
599 
OnFail(const std::function<void (const HttpClientRequest & request,const HttpClientResponse & response,const HttpClientError & error)> & onFailed)600 void HttpClientTask::OnFail(
601     const std::function<void(const HttpClientRequest &request, const HttpClientResponse &response,
602                              const HttpClientError &error)> &onFailed)
603 {
604     onFailed_ = onFailed;
605 }
606 
OnDataReceive(const std::function<void (const HttpClientRequest & request,const uint8_t * data,size_t length)> & onDataReceive)607 void HttpClientTask::OnDataReceive(
608     const std::function<void(const HttpClientRequest &request, const uint8_t *data, size_t length)> &onDataReceive)
609 {
610     onDataReceive_ = onDataReceive;
611 }
612 
OnProgress(const std::function<void (const HttpClientRequest & request,u_long dlTotal,u_long dlNow,u_long ulTotal,u_long ulNow)> & onProgress)613 void HttpClientTask::OnProgress(const std::function<void(const HttpClientRequest &request, u_long dlTotal, u_long dlNow,
614                                                          u_long ulTotal, u_long ulNow)> &onProgress)
615 {
616     onProgress_ = onProgress;
617 }
618 
OnHeadersReceive(const std::function<void (const HttpClientRequest & request,std::map<std::string,std::string> headersWithSetCookie)> & onHeadersReceive)619 void HttpClientTask::OnHeadersReceive(const std::function<void(const HttpClientRequest &request,
620         std::map<std::string, std::string> headersWithSetCookie)> &onHeadersReceive)
621 {
622     onHeadersReceive_ = onHeadersReceive;
623 }
624 
DataReceiveCallback(const void * data,size_t size,size_t memBytes,void * userData)625 size_t HttpClientTask::DataReceiveCallback(const void *data, size_t size, size_t memBytes, void *userData)
626 {
627     auto task = static_cast<HttpClientTask *>(userData);
628     NETSTACK_LOGD("taskId=%{public}d size=%{public}zu memBytes=%{public}zu", task->taskId_, size, memBytes);
629 
630     if (task->canceled_) {
631         NETSTACK_LOGD("canceled");
632         return 0;
633     }
634     if (task->onDataReceive_) {
635         HttpClientRequest request = task->request_;
636         task->onDataReceive_(request, static_cast<const uint8_t *>(data), size * memBytes);
637     }
638 
639     if (task->response_.GetResult().size() < MAX_LIMIT) {
640         task->response_.AppendResult(data, size * memBytes);
641     }
642 
643     return size * memBytes;
644 }
645 
ProgressCallback(void * userData,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)646 int HttpClientTask::ProgressCallback(void *userData, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
647                                      curl_off_t ulnow)
648 {
649     auto task = static_cast<HttpClientTask *>(userData);
650     NETSTACK_LOGD("taskId=%{public}d dltotal=%{public}" CURL_FORMAT_CURL_OFF_T " dlnow=%{public}" CURL_FORMAT_CURL_OFF_T
651                   " ultotal=%{public}" CURL_FORMAT_CURL_OFF_T " ulnow=%{public}" CURL_FORMAT_CURL_OFF_T,
652                   task->taskId_, dltotal, dlnow, ultotal, ulnow);
653 
654     if (task->canceled_) {
655         NETSTACK_LOGD("canceled");
656         return CURLE_ABORTED_BY_CALLBACK;
657     }
658 
659     if (task->onProgress_) {
660         task->onProgress_(task->request_, dltotal, dlnow, ultotal, ulnow);
661     }
662 
663     return 0;
664 }
665 
HeaderReceiveCallback(const void * data,size_t size,size_t memBytes,void * userData)666 size_t HttpClientTask::HeaderReceiveCallback(const void *data, size_t size, size_t memBytes, void *userData)
667 {
668     auto task = static_cast<HttpClientTask *>(userData);
669     task->GetTrace().Tracepoint(TraceEvents::RECEIVING);
670     NETSTACK_LOGD("taskId=%{public}d size=%{public}zu memBytes=%{public}zu", task->taskId_, size, memBytes);
671 
672     if (size * memBytes > MAX_LIMIT) {
673         NETSTACK_LOGE("size * memBytes(%{public}zu) > MAX_LIMIT(%{public}zu)", size * memBytes, MAX_LIMIT);
674         return 0;
675     }
676 
677     task->response_.AppendHeader(static_cast<const char *>(data), size * memBytes);
678     if (!task->canceled_ && task->onHeadersReceive_ &&
679         CommonUtils::EndsWith(task->response_.GetHeader(), HttpConstant::HTTP_RESPONSE_HEADER_SEPARATOR)) {
680         task->response_.ParseHeaders();
681         std::map<std::string, std::string> headerWithSetCookie = task->response_.GetHeaders();
682         std::string setCookies;
683         size_t loop = 0;
684         for (const auto &setCookie : task->response_.GetsetCookie()) {
685             setCookies += setCookie;
686             if (loop + 1 < task->response_.GetsetCookie().size()) {
687                 setCookies += HttpConstant::RESPONSE_KEY_SET_COOKIE_SEPARATOR;
688             }
689             ++loop;
690         }
691         headerWithSetCookie[HttpConstant::RESPONSE_KEY_SET_COOKIE] = setCookies;
692         task->onHeadersReceive_(task->request_, headerWithSetCookie);
693     }
694 
695     return size * memBytes;
696 }
697 
ProcessCookie(CURL * handle)698 void HttpClientTask::ProcessCookie(CURL *handle)
699 {
700     struct curl_slist *cookies = nullptr;
701     if (handle == nullptr) {
702         NETSTACK_LOGE("HttpClientTask::ProcessCookie() handle == nullptr");
703         return;
704     }
705 
706     CURLcode res = curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &cookies);
707     if (res != CURLE_OK) {
708         NETSTACK_LOGE("HttpClientTask::ProcessCookie() curl_easy_getinfo() error! res = %{public}d", res);
709         return;
710     }
711 
712     while (cookies) {
713         response_.AppendCookies(cookies->data, strlen(cookies->data));
714         if (cookies->next != nullptr) {
715             response_.AppendCookies(HttpConstant::HTTP_LINE_SEPARATOR, strlen(HttpConstant::HTTP_LINE_SEPARATOR));
716         }
717         cookies = cookies->next;
718     }
719 }
720 
ProcessResponseCode()721 bool HttpClientTask::ProcessResponseCode()
722 {
723     int64_t result = 0;
724     CURLcode code = curl_easy_getinfo(curlHandle_, CURLINFO_RESPONSE_CODE, &result);
725     if (code != CURLE_OK) {
726         error_.SetCURLResult(code);
727         return false;
728     }
729     auto resultCode = static_cast<ResponseCode>(result);
730     NETSTACK_LOGD("id=%{public}d, code=%{public}d", taskId_, resultCode);
731     response_.SetResponseCode(resultCode);
732 
733     return true;
734 }
735 
GetTimingFromCurl(CURL * handle,CURLINFO info) const736 double HttpClientTask::GetTimingFromCurl(CURL *handle, CURLINFO info) const
737 {
738     curl_off_t timing;
739     CURLcode result = curl_easy_getinfo(handle, info, &timing);
740     if (result != CURLE_OK) {
741         NETSTACK_LOGE("Failed to get timing: %{public}d, %{public}s", info, curl_easy_strerror(result));
742         return 0;
743     }
744     return Timing::TimeUtils::Microseconds2Milliseconds(timing);
745 }
746 
GetSizeFromCurl(CURL * handle) const747 curl_off_t HttpClientTask::GetSizeFromCurl(CURL *handle) const
748 {
749     auto info = CURLINFO_SIZE_DOWNLOAD_T;
750     auto method = request_.GetMethod();
751     if (((method.empty() || method == HttpConstant::HTTP_METHOD_POST || method == HttpConstant::HTTP_METHOD_PUT) &&
752         !request_.GetBody().empty()) || type_ == TaskType::UPLOAD) {
753         info = CURLINFO_SIZE_UPLOAD_T;
754     }
755     curl_off_t size = 0;
756     CURLcode result = curl_easy_getinfo(handle, info, &size);
757     if (result != CURLE_OK) {
758         NETSTACK_LOGE("curl_easy_getinfo failed, %{public}d, %{public}s", info, curl_easy_strerror(result));
759         return 0;
760     }
761     return size;
762 }
763 
DumpHttpPerformance()764 void HttpClientTask::DumpHttpPerformance()
765 {
766     if (curlHandle_ == nullptr) {
767         NETSTACK_LOGE("Ignore dumping http performance, curlHandle_ == nullptr");
768         return;
769     }
770     auto dnsTime = GetTimingFromCurl(curlHandle_, CURLINFO_NAMELOOKUP_TIME_T);
771     auto connectTime = GetTimingFromCurl(curlHandle_, CURLINFO_CONNECT_TIME_T);
772     auto tlsTime = GetTimingFromCurl(curlHandle_, CURLINFO_APPCONNECT_TIME_T);
773     auto firstSendTime = GetTimingFromCurl(curlHandle_, CURLINFO_PRETRANSFER_TIME_T);
774     auto firstRecvTime = GetTimingFromCurl(curlHandle_, CURLINFO_STARTTRANSFER_TIME_T);
775     auto totalTime = GetTimingFromCurl(curlHandle_, CURLINFO_TOTAL_TIME_T);
776     auto redirectTime = GetTimingFromCurl(curlHandle_, CURLINFO_REDIRECT_TIME_T);
777 
778     response_.performanceInfo_.dnsTiming = dnsTime;
779     response_.performanceInfo_.connectTiming = connectTime;
780     response_.performanceInfo_.tlsTiming = tlsTime;
781     response_.performanceInfo_.firstSendTiming = firstSendTime;
782     response_.performanceInfo_.firstReceiveTiming = firstRecvTime;
783     response_.performanceInfo_.totalTiming = totalTime;
784     response_.performanceInfo_.redirectTiming = redirectTime;
785 
786     int64_t responseCode = 0;
787     (void)curl_easy_getinfo(curlHandle_,  CURLINFO_RESPONSE_CODE, &responseCode);
788 
789     /*
790     CURL_HTTP_VERSION_NONE         0
791     CURL_HTTP_VERSION_1_0          1
792     CURL_HTTP_VERSION_1_1          2
793     CURL_HTTP_VERSION_2            3
794     */
795     int64_t httpVer = CURL_HTTP_VERSION_NONE;
796     (void)curl_easy_getinfo(curlHandle_,  CURLINFO_HTTP_VERSION, &httpVer);
797     long osErr = 0;
798     (void)curl_easy_getinfo(curlHandle_,  CURLINFO_OS_ERRNO, &osErr);
799 
800     curl_off_t size = GetSizeFromCurl(curlHandle_);
801     char *ip = nullptr;
802     curl_easy_getinfo(curlHandle_, CURLINFO_PRIMARY_IP, &ip);
803 #ifdef HTTP_HANDOVER_FEATURE
804     std::string handoverInfo = GetRequestHandoverInfo();
805 #endif
806     NETSTACK_LOGI(
807         "taskid=%{public}d"
808         ", size:%{public}" CURL_FORMAT_CURL_OFF_T
809         ", dns:%{public}.3f"
810         ", connect:%{public}.3f"
811         ", tls:%{public}.3f"
812         ", firstSend:%{public}.3f"
813         ", firstRecv:%{public}.3f"
814         ", total:%{public}.3f"
815         ", redirect:%{public}.3f"
816 #ifdef HTTP_HANDOVER_FEATURE
817         ", %{public}s"
818 #endif
819         ", errCode:%{public}d"
820         ", RespCode:%{public}s"
821         ", httpVer:%{public}s"
822         ", method:%{public}s"
823         ", osErr:%{public}ld",
824         taskId_, size, dnsTime, connectTime == 0 ? 0 : connectTime - dnsTime,
825         tlsTime == 0 ? 0 : tlsTime - connectTime,
826         firstSendTime == 0 ? 0 : firstSendTime - std::max({dnsTime, connectTime, tlsTime}),
827         firstRecvTime == 0 ? 0 : firstRecvTime - firstSendTime, totalTime, redirectTime,
828 #ifdef HTTP_HANDOVER_FEATURE
829         handoverInfo.c_str(),
830 #endif
831         error_.GetErrorCode(), std::to_string(responseCode).c_str(), std::to_string(httpVer).c_str(),
832         request_.GetMethod().c_str(), osErr);
833 
834     if (EventReport::GetInstance().IsValid()) {
835         HttpPerfInfo httpPerfInfo;
836         httpPerfInfo.totalTime = totalTime;
837         httpPerfInfo.size = static_cast<int64_t>(size);
838         httpPerfInfo.dnsTime = dnsTime;
839         httpPerfInfo.tlsTime = tlsTime == 0 ? 0 : tlsTime - connectTime;
840         httpPerfInfo.tcpTime = connectTime == 0 ? 0 : connectTime - dnsTime;
841         httpPerfInfo.firstRecvTime = firstRecvTime == 0 ? 0 : firstRecvTime - firstSendTime;
842         httpPerfInfo.responseCode = responseCode;
843         httpPerfInfo.version = std::to_string(httpVer);
844         httpPerfInfo.osErr = static_cast<int64_t>(osErr);
845         httpPerfInfo.errCode = error_.GetErrorCode();
846         httpPerfInfo.ipType = CommonUtils::DetectIPType((ip != nullptr) ? ip : "");
847         EventReport::GetInstance().ProcessHttpPerfHiSysevent(httpPerfInfo);
848     }
849 }
850 
ProcessResponse(CURLMsg * msg)851 void HttpClientTask::ProcessResponse(CURLMsg *msg)
852 {
853     trace_->Finish();
854     CURLcode code = msg->data.result;
855     NETSTACK_LOGD("taskid=%{public}d code=%{public}d", taskId_, code);
856     error_.SetCURLResult(code);
857     response_.SetResponseTime(HttpTime::GetNowTimeGMT());
858 
859     DumpHttpPerformance();
860     if (CURLE_ABORTED_BY_CALLBACK == code) {
861         (void)ProcessResponseCode();
862         if (onCanceled_) {
863             onCanceled_(request_, response_);
864         }
865 #ifdef HTTP_HANDOVER_FEATURE
866         SetSuccess(false);
867 #endif
868         return;
869     }
870 
871     if (code != CURLE_OK) {
872         if (onFailed_) {
873             onFailed_(request_, response_, error_);
874         }
875 #ifdef HTTP_HANDOVER_FEATURE
876         SetSuccess(false);
877 #endif
878         return;
879     }
880 
881     ProcessCookie(curlHandle_);
882     response_.ParseHeaders();
883 
884     if (ProcessResponseCode()) {
885         if (onSucceeded_) {
886             onSucceeded_(request_, response_);
887         }
888 #ifdef HTTP_HANDOVER_FEATURE
889         SetSuccess(true);
890 #endif
891     } else if (onFailed_) {
892         onFailed_(request_, response_, error_);
893 #ifdef HTTP_HANDOVER_FEATURE
894         SetSuccess(false);
895 #endif
896     }
897 #if HAS_NETMANAGER_BASE
898     HttpClientNetworkMessage httpClientNetworkMessage(std::to_string(GetTaskId()), request_, response_, curlHandle_);
899     networkProfilerUtils_->NetworkProfiling(httpClientNetworkMessage);
900 #endif
901 }
902 
SetResponse(const HttpClientResponse & response)903 void HttpClientTask::SetResponse(const HttpClientResponse &response)
904 {
905     response_ = response;
906 }
907 
GetTrace()908 RequestTracer::Trace &HttpClientTask::GetTrace()
909 {
910     return *trace_;
911 }
912 
913 
SetDnsCacheOption(CURL * handle)914 bool HttpClientTask::SetDnsCacheOption(CURL *handle)
915 {
916 #if HAS_NETMANAGER_BASE
917     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
918 #endif
919     return true;
920 }
921 
922 #ifdef HTTP_HANDOVER_FEATURE
SetSuccess(bool isSuccess)923 void HttpClientTask::SetSuccess(bool isSuccess)
924 {
925     isSuccess_ = isSuccess;
926 }
927 
SetRequestHandoverInfo(int32_t handoverNum,int32_t handoverReason,double flowControlTime,int32_t readFlag)928 void HttpClientTask::SetRequestHandoverInfo(
929     int32_t handoverNum, int32_t handoverReason, double flowControlTime, int32_t readFlag)
930 {
931     handoverNum_ = handoverNum;
932     handoverReason_ = handoverReason;
933     flowControlTime_ = flowControlTime;
934     readFlag_ = readFlag;
935 }
936 
GetRequestHandoverInfo()937 std::string HttpClientTask::GetRequestHandoverInfo()
938 {
939     std::string requestHandoverInfo;
940     if (handoverNum_ <= 0) {
941         requestHandoverInfo = "no handover";
942         return requestHandoverInfo;
943     }
944     requestHandoverInfo += "HandoverNum:";
945     requestHandoverInfo += std::to_string(handoverNum_);
946     requestHandoverInfo += ", handoverReason:";
947     switch (handoverReason_) {
948         case HandoverRequestType::INCOMING:
949             requestHandoverInfo += "flowControl, flowControlTime:";
950             break;
951         case HandoverRequestType::NETWORKERROR:
952             requestHandoverInfo += "netErr, retransTime:";
953             break;
954         case HandoverRequestType::UNDONE:
955             requestHandoverInfo += "undone, retransTime:";
956             break;
957         default:
958             requestHandoverInfo += "unkown type";
959             break;
960     }
961     requestHandoverInfo += std::to_string(flowControlTime_);
962     requestHandoverInfo += ", isRead:";
963     requestHandoverInfo += readFlag_ == 1 ? "true" : (readFlag_ == 0 ? "false" : "error");
964     requestHandoverInfo += ", isStream:";
965     requestHandoverInfo += onDataReceive_ ? "true" : "false";
966     return requestHandoverInfo;
967 }
968 #endif
969 } // namespace HttpClient
970 } // namespace NetStack
971 } // namespace OHOS
972