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