• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }