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