• 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     curl_mime_name(part, "file");
179     curl_mime_type(part, mfileData_.type.c_str());
180     UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "===> MultiAddHandle mfileData_.type=%{public}s",
181         mfileData_.type.c_str());
182     curl_mime_filename(part, mfileData_.filename.c_str());
183     curl_mime_data_cb(part, mfileData_.totalsize, ReadCallback, NULL, NULL, &mfileData_);
184     curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
185 }
186 
SetHttpPut(CURL * curl)187 void CUrlAdp::SetHttpPut(CURL *curl)
188 {
189     curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
190     curl_easy_setopt(curl, CURLOPT_READFUNCTION, ReadCallback);
191     curl_easy_setopt(curl, CURLOPT_READDATA, &mfileData_);
192     curl_easy_setopt(curl, CURLOPT_INFILESIZE, mfileData_.totalsize);
193 }
194 
UploadOneFile()195 int32_t CUrlAdp::UploadOneFile()
196 {
197     std::string traceParam = "name:" + mfileData_.filename + "index" + std::to_string(mfileData_.fileIndex) +
198                              "size:" + std::to_string(mfileData_.totalsize);
199     HitraceScoped trace(HITRACE_TAG_MISC, "upload file " + traceParam);
200 
201     CurlGlobalInit();
202     curlMulti_ = curl_multi_init();
203     if (curlMulti_ == nullptr) {
204         CurlGlobalCleanup();
205         return UPLOAD_ERRORCODE_UPLOAD_LIB_ERROR;
206     }
207 
208     bool ret = MultiAddHandle(curlMulti_, curlArray_);
209     if (ret == false) {
210         return UPLOAD_ERRORCODE_UPLOAD_LIB_ERROR;
211     }
212 
213     int isRuning = 0;
214     curl_multi_perform(curlMulti_, &isRuning);
215     UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "isRuning = %{public}d", isRuning);
216     do {
217         int numfds = 0;
218         int res = curl_multi_wait(curlMulti_, NULL, 0, TRANS_TIMEOUT_MS, &numfds);
219         if (res != CURLM_OK) {
220             return res;
221         }
222         curl_multi_perform(curlMulti_, &isRuning);
223     } while (isRuning);
224 
225     return CheckUploadStatus(curlMulti_);
226 }
227 
CurlGlobalInit()228 void CUrlAdp::CurlGlobalInit()
229 {
230     std::lock_guard<std::mutex> guard(globalMutex_);
231     if (!isCurlGlobalInit_) {
232         isCurlGlobalInit_ = true;
233     }
234 }
235 
CurlGlobalCleanup()236 void CUrlAdp::CurlGlobalCleanup()
237 {
238     std::lock_guard<std::mutex> guard(globalMutex_);
239     if (isCurlGlobalInit_) {
240         isCurlGlobalInit_ = false;
241     }
242 }
243 
CheckUploadStatus(CURLM * curlMulti)244 int CUrlAdp::CheckUploadStatus(CURLM *curlMulti)
245 {
246     int msgsLeft = 0;
247     int returnCode = UPLOAD_ERRORCODE_UPLOAD_FAIL;
248     CURLMsg* msg = NULL;
249     if (IsReadAbort()) {
250         UPLOAD_HILOGE(UPLOAD_MODULE_FRAMEWORK, "CheckUploadStatus  IsReadAbort is %{public}d", IsReadAbort());
251         return returnCode;
252     }
253     while ((msg = curl_multi_info_read(curlMulti, &msgsLeft))) {
254         if (msg->msg != CURLMSG_DONE) {
255             continue;
256         }
257         CURL *eh = NULL;
258         eh = msg->easy_handle;
259         returnCode = msg->data.result;
260         if (returnCode != CURLE_OK) {
261             UPLOAD_HILOGE(UPLOAD_MODULE_FRAMEWORK, "upload fail curl error %{public}d", returnCode);
262             return UPLOAD_ERRORCODE_UPLOAD_LIB_ERROR;
263         }
264 
265         long respCode = 0;
266         curl_easy_getinfo(eh, CURLINFO_RESPONSE_CODE, &respCode);
267         UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "upload http code %{public}ld", respCode);
268         if (respCode != HTTP_SUCCESS) {
269             returnCode = respCode;
270             UPLOAD_HILOGE(UPLOAD_MODULE_FRAMEWORK, "upload fail http error %{public}d", returnCode);
271             return UPLOAD_ERRORCODE_UPLOAD_FAIL;
272         }
273         returnCode = UPLOAD_OK;
274     }
275     return returnCode;
276 }
277 
Remove()278 bool CUrlAdp::Remove()
279 {
280     UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "remove");
281     std::lock_guard<std::mutex> guard(curlMutex_);
282     isReadAbort_ = true;
283     return true;
284 }
285 
ClearCurlResource()286 bool CUrlAdp::ClearCurlResource()
287 {
288     std::lock_guard<std::mutex> guard(mutex_);
289     for (auto url : curlArray_) {
290         curl_multi_remove_handle(curlMulti_, url);
291         curl_easy_cleanup(url);
292     }
293     curlArray_.clear();
294     if (curlMulti_) {
295         curl_multi_cleanup(curlMulti_);
296         curlMulti_ = nullptr;
297     }
298     CurlGlobalCleanup();
299     return true;
300 }
301 
CheckCUrlAdp(FileData * fData)302 bool CUrlAdp::CheckCUrlAdp(FileData *fData)
303 {
304     if (fData == nullptr || fData->adp == nullptr) {
305         UPLOAD_HILOGE(UPLOAD_MODULE_FRAMEWORK, "CheckCUrlAdp url == nullptr");
306         return false;
307     }
308     std::lock_guard<std::mutex> lock(fData->adp->curlMutex_);
309     if (fData->adp->IsReadAbort()) {
310         UPLOAD_HILOGE(UPLOAD_MODULE_FRAMEWORK, "CheckCUrlAdp url->IsReadAbort()");
311         return false;
312     }
313     return true;
314 }
315 
ProgressCallback(void * clientp,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)316 int CUrlAdp::ProgressCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
317 {
318     FileData *fData = (FileData *)clientp;
319     if (!CheckCUrlAdp(fData)) {
320         return UPLOAD_ERRORCODE_UPLOAD_FAIL;
321     }
322 
323     std::shared_ptr<CUrlAdp> url = fData->adp;
324     std::lock_guard<std::mutex> lock(url->curlMutex_);
325     if (ulnow > 0) {
326         fData->upsize = fData->totalsize - (ultotal - ulnow);
327     } else {
328         fData->upsize = ulnow;
329     }
330 
331     UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "progress upload total: %{public}" PRIu64 " upload now: %{public}" PRIu64
332         " upload size: %{public}" PRIu64 " total size: %{public}" PRIu64 " thread:%{public}lu",
333         ultotal, ulnow, fData->upsize, fData->totalsize, pthread_self());
334 
335     if (url->uploadTask_) {
336         int64_t totalulnow = 0;
337         for (auto &vmem : url->fileDatas_) {
338             if (fData->filename == vmem.filename) {
339                 vmem.upsize = fData->upsize;
340             }
341             totalulnow += vmem.upsize;
342         }
343         UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "report progress total upload size: %{public}" PRIu64
344             " upload now: %{public}" PRIu64, totalulnow, ultotal);
345         url->uploadTask_->OnProgress(totalulnow);
346     }
347     return 0;
348 }
349 
HeaderCallback(char * buffer,size_t size,size_t nitems,void * userdata)350 size_t CUrlAdp::HeaderCallback(char *buffer, size_t size, size_t nitems, void *userdata)
351 {
352     FileData *fData = (FileData *)userdata;
353     if (!CheckCUrlAdp(fData)) {
354         return CURLE_WRITE_ERROR;
355     }
356 
357     std::shared_ptr<CUrlAdp> url = fData->adp;
358     std::lock_guard<std::mutex> lock(url->curlMutex_);
359     std::string stmp(buffer, size * nitems);
360     url->SplitHttpMessage(stmp, fData);
361 
362     if (url->uploadTask_ && fData->headSendFlag == COLLECT_END_FLAG) {
363         std::string headers = std::accumulate(fData->responseHead.begin(), fData->responseHead.end(), std::string(""));
364         UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "report head len: %{public}zu, content: %{public}s",
365             headers.length(), headers.c_str());
366         auto func = (url->config_->protocolVersion == API5) ? NotifyAPI5 : Notify;
367         func(fData, headers);
368         fData->responseHead.clear();
369         fData->httpCode = 0;
370     }
371     return size * nitems;
372 }
373 
SplitHttpMessage(const std::string & stmp,FileData * & fData)374 void CUrlAdp::SplitHttpMessage(const std::string &stmp, FileData* &fData)
375 {
376     const std::string headEndFlag = "\r\n";
377     if (std::string::npos != stmp.find("HTTP")) {
378         fData->headSendFlag = COLLECT_DO_FLAG;
379         const int codeLen = 3;
380         std::string::size_type position = stmp.find_first_of(" ");
381         std::string scode(stmp, position + 1, codeLen);
382         fData->httpCode = std::stol(scode);
383     } else if (stmp == headEndFlag) {
384         fData->headSendFlag = COLLECT_END_FLAG;
385     }
386     if (fData->headSendFlag == COLLECT_DO_FLAG || fData->headSendFlag == COLLECT_END_FLAG) {
387         fData->responseHead.push_back(stmp);
388     }
389 }
390 
Notify(FileData * fData,std::string & headers)391 void CUrlAdp::Notify(FileData *fData, std::string &headers)
392 {
393     if (fData->httpCode == HTTP_SUCCESS) {
394         if (fData->adp->fileDatas_.size() == fData->fileIndex) {
395             fData->adp->uploadTask_->OnHeaderReceive(headers);
396         }
397     } else {
398         fData->adp->uploadTask_->OnHeaderReceive(headers);
399     }
400 }
401 
NotifyAPI5(FileData * fData,std::string & headers)402 void CUrlAdp::NotifyAPI5(FileData *fData, std::string &headers)
403 {
404     if (fData->httpCode == HTTP_SUCCESS) {
405         if (fData->adp->fileDatas_.size() == fData->fileIndex && fData->adp->config_->fsuccess != nullptr) {
406             UploadResponse resData;
407             resData.headers = headers;
408             resData.code = fData->httpCode;
409             fData->adp->config_->fsuccess(resData);
410         }
411     } else {
412         if (fData->adp->config_->ffail) {
413             fData->adp->config_->ffail(headers, fData->httpCode);
414         }
415     }
416 }
417 
ReadCallback(char * buffer,size_t size,size_t nitems,void * arg)418 size_t CUrlAdp::ReadCallback(char *buffer, size_t size, size_t nitems, void *arg)
419 {
420     UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "size is %{public}zu, nitems is %{public}zu.", size, nitems);
421     FileData *fData = (FileData *)arg;
422     if (!CheckCUrlAdp(fData) || ferror(fData->fp)) {
423         return CURL_READFUNC_ABORT;
424     }
425 
426     std::shared_ptr<CUrlAdp> url = fData->adp;
427     std::lock_guard<std::mutex> lock(url->curlMutex_);
428     url->StartTimer();
429     size_t readSize = fread(buffer, size, nitems, fData->fp);
430     url->StopTimer();
431 
432     return readSize;
433 }
434 
StartTimer()435 void CUrlAdp::StartTimer()
436 {
437     uint32_t ret = timer_.Setup();
438     if (ret != Utils::TIMER_ERR_OK) {
439         UPLOAD_HILOGI(UPLOAD_MODULE_FRAMEWORK, "Create Timer error");
440         return;
441     }
442     auto TimeOutCallback = [this]() {
443         UPLOAD_HILOGD(UPLOAD_MODULE_FRAMEWORK, "OutTime error");
444         this->isReadAbort_ = true;
445     };
446     timerId_ = timer_.Register(TimeOutCallback, READFILE_TIMEOUT_MS, true);
447 }
448 
StopTimer()449 void CUrlAdp::StopTimer()
450 {
451     timer_.Unregister(timerId_);
452     timer_.Shutdown();
453 }
454 } // namespace OHOS::Request::Upload