• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <cstdio>
19 #include <vector>
20 #include <string>
21 #include <climits>
22 #include <cinttypes>
23 #include "common_timer_errors.h"
24 #include "upload_task.h"
25 #include "upload_hilog_wrapper.h"
26 #include "hitrace_meter.h"
27 #include "hisysevent.h"
28 #include "curl_adp.h"
29 
30 namespace OHOS::Request::Upload {
CUrlAdp(std::vector<FileData> & fileDatas,std::shared_ptr<UploadConfig> & config)31 CUrlAdp::CUrlAdp(std::vector<FileData> &fileDatas, std::shared_ptr<UploadConfig> &config)
32     : fileDatas_(fileDatas), timer_("uploadTimer")
33 {
34     config_ = config;
35     isCurlGlobalInit_ = false;
36     isReadAbort_ = false;
37     curlMulti_ = nullptr;
38     timerId_ = 0;
39 }
40 
~CUrlAdp()41 CUrlAdp::~CUrlAdp()
42 {
43     UPLOAD_HILOGI(UPLOAD_MODULE_FRAMEWORK, "~CUrlAdp()");
44 }
45 
DoUpload(std::shared_ptr<IUploadTask> task)46 uint32_t CUrlAdp::DoUpload(std::shared_ptr<IUploadTask> task)
47 {
48     UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "upload start");
49     if (task != nullptr) {
50         uploadTask_ = task;
51     }
52 
53     uint32_t successCount = 0;
54     for (auto &vmem : fileDatas_) {
55         UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "read abort stat: %{public}d file index: %{public}u",
56                       IsReadAbort(), vmem.fileIndex);
57         if (IsReadAbort()) {
58             vmem.result = UPLOAD_TASK_REMOVED;
59             continue;
60         }
61 
62         mfileData_ = vmem;
63         mfileData_.adp = shared_from_this();
64         vmem.result = static_cast<uint32_t>(UploadOneFile());
65         if (vmem.result == UPLOAD_OK) {
66             successCount++;
67         }
68         mfileData_.responseHead.clear();
69         if (mfileData_.list) {
70             curl_slist_free_all(mfileData_.list);
71             mfileData_.list = nullptr;
72         }
73         ClearCurlResource();
74         usleep(FILE_UPLOAD_INTERVEL);
75     }
76     mfileData_.adp = nullptr;
77     uploadTask_ = nullptr;
78     return (IsSuccess(successCount, fileDatas_.size())) ? UPLOAD_OK : UPLOAD_ERRORCODE_UPLOAD_FAIL;
79 }
80 
IsSuccess(const uint32_t count,const uint32_t size)81 bool CUrlAdp::IsSuccess(const uint32_t count, const uint32_t size)
82 {
83     if (count == 0) {
84         return false;
85     }
86     return (count == size);
87 }
88 
MultiAddHandle(CURLM * curlMulti,std::vector<CURL * > & curlArray)89 bool CUrlAdp::MultiAddHandle(CURLM *curlMulti, std::vector<CURL *> &curlArray)
90 {
91     CURL *curl = curl_easy_init();
92     if (curl == nullptr) {
93         return false;
94     }
95 
96     SetCurlOpt(curl);
97     curlArray.push_back(curl);
98     curl_multi_add_handle(curlMulti, curl);
99     return true;
100 }
101 
SetHeadData(CURL * curl)102 void CUrlAdp::SetHeadData(CURL *curl)
103 {
104     bool hasContentType = false;
105     for (auto &headerData : config_->header) {
106         if (headerData.find("Content-Type:") != std::string::npos) {
107             hasContentType = true;
108         }
109         mfileData_.list = curl_slist_append(mfileData_.list, headerData.c_str());
110     }
111 
112     if (!hasContentType) {
113         std::string str = config_->method == PUT ? "Content-Type:application/octet-stream" :
114             "Content-Type:multipart/form-data";
115         mfileData_.list = curl_slist_append(mfileData_.list, str.c_str());
116     }
117     curl_easy_setopt(curl, CURLOPT_HTTPHEADER, mfileData_.list);
118 }
119 
SetBehaviorOpt(CURL * curl)120 void CUrlAdp::SetBehaviorOpt(CURL *curl)
121 {
122     curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
123     curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
124     curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
125 }
126 
SetCallbackOpt(CURL * curl)127 void CUrlAdp::SetCallbackOpt(CURL *curl)
128 {
129     curl_easy_setopt(curl, CURLOPT_HEADERDATA, &mfileData_);
130     curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback);
131     curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback);
132     curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &mfileData_);
133 }
134 
SetNetworkOpt(CURL * curl)135 void CUrlAdp::SetNetworkOpt(CURL *curl)
136 {
137     curl_easy_setopt(curl, CURLOPT_URL, config_->url.c_str());
138 }
139 
SetConnectionOpt(CURL * curl)140 void CUrlAdp::SetConnectionOpt(CURL *curl)
141 {
142     curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
143 }
144 
SetSslOpt(CURL * curl)145 void CUrlAdp::SetSslOpt(CURL *curl)
146 {
147     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
148     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
149 }
150 
SetCurlOpt(CURL * curl)151 void CUrlAdp::SetCurlOpt(CURL *curl)
152 {
153     SetHeadData(curl);
154     SetNetworkOpt(curl);
155     SetConnectionOpt(curl);
156     SetSslOpt(curl);
157     SetBehaviorOpt(curl);
158     SetCallbackOpt(curl);
159     if (config_->method == PUT) {
160         SetHttpPut(curl);
161     } else {
162         SetMimePost(curl);
163     }
164 }
165 
SetMimePost(CURL * curl)166 void CUrlAdp::SetMimePost(CURL *curl)
167 {
168     curl_mimepart *part;
169     curl_mime *mime = curl_mime_init(curl);
170     if (config_->data.size()) {
171         for (auto &vdata : config_->data) {
172             part = curl_mime_addpart(mime);
173             curl_mime_name(part, vdata.name.c_str());
174             curl_mime_data(part, vdata.value.c_str(), vdata.value.size());
175         }
176     }
177     part = curl_mime_addpart(mime);
178     if (!mfileData_.name.empty()) {
179         curl_mime_name(part, mfileData_.name.c_str());
180     } else {
181         curl_mime_name(part, "file");
182     }
183     curl_mime_type(part, mfileData_.type.c_str());
184     UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "===> MultiAddHandle mfileData_.type=%{public}s",
185         mfileData_.type.c_str());
186     curl_mime_filename(part, mfileData_.filename.c_str());
187     curl_mime_data_cb(part, mfileData_.totalsize, ReadCallback, NULL, NULL, &mfileData_);
188     curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
189 }
190 
SetHttpPut(CURL * curl)191 void CUrlAdp::SetHttpPut(CURL *curl)
192 {
193     curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
194     curl_easy_setopt(curl, CURLOPT_READFUNCTION, ReadCallback);
195     curl_easy_setopt(curl, CURLOPT_READDATA, &mfileData_);
196     curl_easy_setopt(curl, CURLOPT_INFILESIZE, mfileData_.totalsize);
197 }
198 
UploadOneFile()199 int32_t CUrlAdp::UploadOneFile()
200 {
201     std::string traceParam = "name:" + mfileData_.filename + "index" + std::to_string(mfileData_.fileIndex) +
202                              "size:" + std::to_string(mfileData_.totalsize);
203     HitraceScoped trace(HITRACE_TAG_MISC, "upload file " + traceParam);
204 
205     CurlGlobalInit();
206     curlMulti_ = curl_multi_init();
207     if (curlMulti_ == nullptr) {
208         CurlGlobalCleanup();
209         return UPLOAD_ERRORCODE_UPLOAD_LIB_ERROR;
210     }
211 
212     bool ret = MultiAddHandle(curlMulti_, curlArray_);
213     if (ret == false) {
214         return UPLOAD_ERRORCODE_UPLOAD_LIB_ERROR;
215     }
216 
217     int isRuning = 0;
218     curl_multi_perform(curlMulti_, &isRuning);
219     UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "isRuning = %{public}d", isRuning);
220     do {
221         int numfds = 0;
222         int res = curl_multi_wait(curlMulti_, NULL, 0, TRANS_TIMEOUT_MS, &numfds);
223         if (res != CURLM_OK) {
224             return res;
225         }
226         curl_multi_perform(curlMulti_, &isRuning);
227     } while (isRuning);
228 
229     return CheckUploadStatus(curlMulti_);
230 }
231 
CurlGlobalInit()232 void CUrlAdp::CurlGlobalInit()
233 {
234     std::lock_guard<std::mutex> guard(globalMutex_);
235     if (!isCurlGlobalInit_) {
236         isCurlGlobalInit_ = true;
237     }
238 }
239 
CurlGlobalCleanup()240 void CUrlAdp::CurlGlobalCleanup()
241 {
242     std::lock_guard<std::mutex> guard(globalMutex_);
243     if (isCurlGlobalInit_) {
244         isCurlGlobalInit_ = false;
245     }
246 }
247 
CheckUploadStatus(CURLM * curlMulti)248 int CUrlAdp::CheckUploadStatus(CURLM *curlMulti)
249 {
250     int msgsLeft = 0;
251     int returnCode = UPLOAD_ERRORCODE_UPLOAD_FAIL;
252     CURLMsg* msg = NULL;
253     if (IsReadAbort()) {
254         UPLOAD_HILOGE(UPLOAD_MODULE_FRAMEWORK, "CheckUploadStatus  IsReadAbort is %{public}d", IsReadAbort());
255         return returnCode;
256     }
257     while ((msg = curl_multi_info_read(curlMulti, &msgsLeft))) {
258         if (msg->msg != CURLMSG_DONE) {
259             continue;
260         }
261         CURL *eh = NULL;
262         eh = msg->easy_handle;
263         returnCode = msg->data.result;
264         if (returnCode != CURLE_OK) {
265             UPLOAD_HILOGE(UPLOAD_MODULE_FRAMEWORK, "upload fail curl error %{public}d", returnCode);
266             return UPLOAD_ERRORCODE_UPLOAD_LIB_ERROR;
267         }
268 
269         long respCode = 0;
270         curl_easy_getinfo(eh, CURLINFO_RESPONSE_CODE, &respCode);
271         UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "upload http code %{public}ld", respCode);
272         if (respCode != HTTP_SUCCESS) {
273             returnCode = respCode;
274             UPLOAD_HILOGE(UPLOAD_MODULE_FRAMEWORK, "upload fail http error %{public}d", returnCode);
275             return UPLOAD_ERRORCODE_UPLOAD_FAIL;
276         }
277         returnCode = UPLOAD_OK;
278     }
279     return returnCode;
280 }
281 
Remove()282 bool CUrlAdp::Remove()
283 {
284     UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "remove");
285     std::lock_guard<std::mutex> guard(curlMutex_);
286     isReadAbort_ = true;
287     return true;
288 }
289 
ClearCurlResource()290 bool CUrlAdp::ClearCurlResource()
291 {
292     std::lock_guard<std::mutex> guard(mutex_);
293     for (auto url : curlArray_) {
294         curl_multi_remove_handle(curlMulti_, url);
295         curl_easy_cleanup(url);
296     }
297     curlArray_.clear();
298     if (curlMulti_) {
299         curl_multi_cleanup(curlMulti_);
300         curlMulti_ = nullptr;
301     }
302     CurlGlobalCleanup();
303     return true;
304 }
305 
CheckCUrlAdp(FileData * fData)306 bool CUrlAdp::CheckCUrlAdp(FileData *fData)
307 {
308     if (fData == nullptr || fData->adp == nullptr) {
309         UPLOAD_HILOGE(UPLOAD_MODULE_FRAMEWORK, "CheckCUrlAdp url == nullptr");
310         return false;
311     }
312     std::lock_guard<std::mutex> lock(fData->adp->curlMutex_);
313     if (fData->adp->IsReadAbort()) {
314         UPLOAD_HILOGE(UPLOAD_MODULE_FRAMEWORK, "CheckCUrlAdp url->IsReadAbort()");
315         return false;
316     }
317     return true;
318 }
319 
ProgressCallback(void * clientp,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)320 int CUrlAdp::ProgressCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
321 {
322     FileData *fData = (FileData *)clientp;
323     if (!CheckCUrlAdp(fData)) {
324         return UPLOAD_ERRORCODE_UPLOAD_FAIL;
325     }
326 
327     std::shared_ptr<CUrlAdp> url = fData->adp;
328     std::lock_guard<std::mutex> lock(url->curlMutex_);
329     if (ulnow > 0) {
330         fData->upsize = fData->totalsize - (ultotal - ulnow);
331     } else {
332         fData->upsize = ulnow;
333     }
334 
335     UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "progress upload total: %{public}" PRIu64 " upload now: %{public}" PRIu64
336         " upload size: %{public}" PRIu64 " total size: %{public}" PRIu64 " thread:%{public}lu",
337         ultotal, ulnow, fData->upsize, fData->totalsize, pthread_self());
338 
339     if (url->uploadTask_) {
340         int64_t totalulnow = 0;
341         for (auto &vmem : url->fileDatas_) {
342             if (fData->filename == vmem.filename) {
343                 vmem.upsize = fData->upsize;
344             }
345             totalulnow += vmem.upsize;
346         }
347         UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "report progress total upload size: %{public}" PRIu64
348             " upload now: %{public}" PRIu64, totalulnow, ultotal);
349         url->uploadTask_->OnProgress(totalulnow);
350     }
351     return 0;
352 }
353 
HeaderCallback(char * buffer,size_t size,size_t nitems,void * userdata)354 size_t CUrlAdp::HeaderCallback(char *buffer, size_t size, size_t nitems, void *userdata)
355 {
356     FileData *fData = (FileData *)userdata;
357     if (!CheckCUrlAdp(fData)) {
358         return CURLE_WRITE_ERROR;
359     }
360 
361     std::shared_ptr<CUrlAdp> url = fData->adp;
362     std::lock_guard<std::mutex> lock(url->curlMutex_);
363     std::string stmp(buffer, size * nitems);
364     url->SplitHttpMessage(stmp, fData);
365 
366     if (url->uploadTask_ && fData->headSendFlag == COLLECT_END_FLAG) {
367         std::string headers = std::accumulate(fData->responseHead.begin(), fData->responseHead.end(), std::string(""));
368         UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "report head len: %{public}zu, content: %{public}s",
369             headers.length(), headers.c_str());
370         auto func = (url->config_->protocolVersion == API5) ? NotifyAPI5 : Notify;
371         func(fData, headers);
372         fData->responseHead.clear();
373         fData->httpCode = 0;
374     }
375     return size * nitems;
376 }
377 
SplitHttpMessage(const std::string & stmp,FileData * & fData)378 void CUrlAdp::SplitHttpMessage(const std::string &stmp, FileData* &fData)
379 {
380     const std::string headEndFlag = "\r\n";
381     if (std::string::npos != stmp.find("HTTP")) {
382         fData->headSendFlag = COLLECT_DO_FLAG;
383         const int codeLen = 3;
384         std::string::size_type position = stmp.find_first_of(" ");
385         std::string scode(stmp, position + 1, codeLen);
386         fData->httpCode = std::stol(scode);
387     } else if (stmp == headEndFlag) {
388         fData->headSendFlag = COLLECT_END_FLAG;
389     }
390     if (fData->headSendFlag == COLLECT_DO_FLAG || fData->headSendFlag == COLLECT_END_FLAG) {
391         fData->responseHead.push_back(stmp);
392     }
393 }
394 
Notify(FileData * fData,std::string & headers)395 void CUrlAdp::Notify(FileData *fData, std::string &headers)
396 {
397     if (fData->httpCode == HTTP_SUCCESS) {
398         if (fData->adp->fileDatas_.size() == fData->fileIndex) {
399             fData->adp->uploadTask_->OnHeaderReceive(headers);
400         }
401     } else {
402         fData->adp->uploadTask_->OnHeaderReceive(headers);
403     }
404 }
405 
NotifyAPI5(FileData * fData,std::string & headers)406 void CUrlAdp::NotifyAPI5(FileData *fData, std::string &headers)
407 {
408     if (fData->httpCode == HTTP_SUCCESS) {
409         if (fData->adp->fileDatas_.size() == fData->fileIndex && fData->adp->config_->fsuccess != nullptr) {
410             UploadResponse resData;
411             resData.headers = headers;
412             resData.code = fData->httpCode;
413             fData->adp->config_->fsuccess(resData);
414         }
415     } else {
416         if (fData->adp->config_->ffail) {
417             fData->adp->config_->ffail(headers, fData->httpCode);
418         }
419     }
420 }
421 
ReadCallback(char * buffer,size_t size,size_t nitems,void * arg)422 size_t CUrlAdp::ReadCallback(char *buffer, size_t size, size_t nitems, void *arg)
423 {
424     UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "size is %{public}zu, nitems is %{public}zu.", size, nitems);
425     FileData *fData = (FileData *)arg;
426     if (!CheckCUrlAdp(fData) || ferror(fData->fp)) {
427         return CURL_READFUNC_ABORT;
428     }
429 
430     std::shared_ptr<CUrlAdp> url = fData->adp;
431     std::lock_guard<std::mutex> lock(url->curlMutex_);
432     url->StartTimer();
433     size_t readSize = fread(buffer, size, nitems, fData->fp);
434     url->StopTimer();
435 
436     return readSize;
437 }
438 
StartTimer()439 void CUrlAdp::StartTimer()
440 {
441     uint32_t ret = timer_.Setup();
442     if (ret != Utils::TIMER_ERR_OK) {
443         UPLOAD_HILOGI(UPLOAD_MODULE_FRAMEWORK, "Create Timer error");
444         return;
445     }
446     auto TimeOutCallback = [this]() {
447         UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "OutTime error");
448         this->isReadAbort_ = true;
449     };
450     timerId_ = timer_.Register(TimeOutCallback, READFILE_TIMEOUT_MS, true);
451 }
452 
StopTimer()453 void CUrlAdp::StopTimer()
454 {
455     timer_.Unregister(timerId_);
456     timer_.Shutdown();
457 }
458 } // namespace OHOS::Request::Upload