1 /*
2 * Copyright (c) 2022-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 #define HST_LOG_TAG "HttpCurlClient"
16
17 #include "http_curl_client.h"
18 #include <algorithm>
19 #include <regex>
20 #include <vector>
21 #include "foundation/log.h"
22 #include <foundation/osal/thread/scoped_lock.h>
23 #include "foundation/utils/hitrace_utils.h"
24 #include "securec.h"
25
26 #ifndef CA_DIR
27 #define CA_DIR "/etc/ssl/certs/"
28 #endif
29
30 namespace OHOS {
31 namespace Media {
32 namespace Plugin {
33 namespace HttpPlugin {
HttpCurlClient(RxHeader headCallback,RxBody bodyCallback,void * userParam)34 HttpCurlClient::HttpCurlClient(RxHeader headCallback, RxBody bodyCallback, void *userParam)
35 : rxHeader_(headCallback), rxBody_(bodyCallback), userParam_(userParam)
36 {
37 MEDIA_LOG_I("HttpCurlClient ctor");
38 }
39
~HttpCurlClient()40 HttpCurlClient::~HttpCurlClient()
41 {
42 MEDIA_LOG_I("~HttpCurlClient dtor");
43 }
44
Init()45 Status HttpCurlClient::Init()
46 {
47 FALSE_LOG(curl_global_init(CURL_GLOBAL_ALL) == CURLE_OK);
48 return Status::OK;
49 }
50
Open(const std::string & url)51 Status HttpCurlClient::Open(const std::string& url)
52 {
53 easyHandle_ = curl_easy_init();
54 FALSE_RETURN_V(easyHandle_ != nullptr, Status::ERROR_NULL_POINTER);
55 InitCurlEnvironment(url);
56 return Status::OK;
57 }
58
Close()59 Status HttpCurlClient::Close()
60 {
61 OSAL::ScopedLock lock(mutex_);
62 MEDIA_LOG_I("Close client");
63 curl_easy_setopt(easyHandle_, CURLOPT_TIMEOUT, 1);
64 if (easyHandle_) {
65 curl_easy_cleanup(easyHandle_);
66 easyHandle_ = nullptr;
67 }
68 return Status::OK;
69 }
70
Deinit()71 Status HttpCurlClient::Deinit()
72 {
73 if (easyHandle_) {
74 curl_easy_cleanup(easyHandle_);
75 easyHandle_ = nullptr;
76 }
77 curl_global_cleanup();
78 return Status::OK;
79 }
80
InitCurlEnvironment(const std::string & url)81 void HttpCurlClient::InitCurlEnvironment(const std::string& url)
82 {
83 curl_easy_setopt(easyHandle_, CURLOPT_URL, UrlParse(url).c_str());
84 curl_easy_setopt(easyHandle_, CURLOPT_CONNECTTIMEOUT, 5); // 5
85
86 curl_easy_setopt(easyHandle_, CURLOPT_SSL_VERIFYPEER, 0L);
87 curl_easy_setopt(easyHandle_, CURLOPT_SSL_VERIFYHOST, 0L);
88 curl_easy_setopt(easyHandle_, CURLOPT_CAINFO, CA_DIR "cacert.pem");
89
90 curl_easy_setopt(easyHandle_, CURLOPT_HTTPGET, 1L);
91
92 curl_easy_setopt(easyHandle_, CURLOPT_FORBID_REUSE, 0L);
93 curl_easy_setopt(easyHandle_, CURLOPT_FOLLOWLOCATION, 1L);
94
95 curl_easy_setopt(easyHandle_, CURLOPT_WRITEFUNCTION, rxBody_);
96 curl_easy_setopt(easyHandle_, CURLOPT_WRITEDATA, userParam_);
97 curl_easy_setopt(easyHandle_, CURLOPT_HEADERFUNCTION, rxHeader_);
98 curl_easy_setopt(easyHandle_, CURLOPT_HEADERDATA, userParam_);
99
100 curl_easy_setopt(easyHandle_, CURLOPT_LOW_SPEED_LIMIT, 2); // 2
101 curl_easy_setopt(easyHandle_, CURLOPT_LOW_SPEED_TIME, 5); // 连续5s下载速度低于2kb/s会触发timeout
102
103 curl_easy_setopt(easyHandle_, CURLOPT_TCP_KEEPALIVE, 1L);
104 curl_easy_setopt(easyHandle_, CURLOPT_TCP_KEEPINTVL, 5L); // 5 心跳
105 }
106
UrlParse(const std::string & url)107 std::string HttpCurlClient::UrlParse(const std::string& url)
108 {
109 std::string s;
110 std::regex_replace(std::back_inserter(s), url.begin(), url.end(), std::regex(" "), "%20");
111 return s;
112 }
113
114 // RequestData run in HttpDownload thread,
115 // Open, Close, Deinit run in other thread.
116 // Should call Open before start HttpDownload thread.
117 // Should Pause HttpDownload thread then Close, Deinit.
RequestData(long startPos,int len,NetworkServerErrorCode & serverCode,NetworkClientErrorCode & clientCode)118 Status HttpCurlClient::RequestData(long startPos, int len, NetworkServerErrorCode& serverCode,
119 NetworkClientErrorCode& clientCode)
120 {
121 SYNC_TRACER();
122 FALSE_RETURN_V(easyHandle_ != nullptr, Status::ERROR_NULL_POINTER);
123 if (startPos >= 0) {
124 char requestRange[128] = {0};
125 if (len > 0) {
126 snprintf_s(requestRange, sizeof(requestRange), sizeof(requestRange) - 1, "%ld-%ld",
127 startPos, startPos + len - 1);
128 } else {
129 snprintf_s(requestRange, sizeof(requestRange), sizeof(requestRange) - 1, "%ld-", startPos);
130 }
131 MEDIA_LOG_DD("RequestData: requestRange " PUBLIC_LOG_S, requestRange);
132 curl_easy_setopt(easyHandle_, CURLOPT_RANGE, requestRange);
133 }
134 curl_slist *headers {nullptr};
135 headers = curl_slist_append(headers, "Connection: Keep-alive");
136 headers = curl_slist_append(headers, "Keep-Alive: timeout=120");
137 curl_easy_setopt(easyHandle_, CURLOPT_HTTPHEADER, headers);
138
139 MEDIA_LOG_D("RequestData: startPos " PUBLIC_LOG_D32 ", len " PUBLIC_LOG_D32, static_cast<int>(startPos), len);
140 OSAL::ScopedLock lock(mutex_);
141 FALSE_RETURN_V(easyHandle_ != nullptr, Status::ERROR_NULL_POINTER);
142 CURLcode returnCode = curl_easy_perform(easyHandle_);
143 if (headers != nullptr) {
144 curl_slist_free_all(headers);
145 }
146 clientCode = NetworkClientErrorCode::ERROR_OK;
147 serverCode = 0;
148 if (returnCode != CURLE_OK) {
149 MEDIA_LOG_E("Curl error " PUBLIC_LOG_D32, returnCode);
150 if (returnCode == CURLE_WRITE_ERROR) {
151 clientCode = NetworkClientErrorCode::ERROR_NOT_RETRY;
152 } else if (returnCode == CURLE_COULDNT_CONNECT || returnCode == CURLE_OPERATION_TIMEDOUT) {
153 clientCode = NetworkClientErrorCode::ERROR_TIME_OUT;
154 } else {
155 clientCode = NetworkClientErrorCode::ERROR_UNKNOWN;
156 }
157 return Status::ERROR_CLIENT;
158 } else {
159 int64_t httpCode = 0;
160 curl_easy_getinfo(easyHandle_, CURLINFO_RESPONSE_CODE, &httpCode);
161 if (httpCode >= 400) { // 400
162 MEDIA_LOG_E("Http error " PUBLIC_LOG_D64, httpCode);
163 serverCode = httpCode;
164 return Status::ERROR_SERVER;
165 }
166 }
167 return Status::OK;
168 }
169 }
170 }
171 }
172 }