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