1 /*
2  * Copyright (c) 2021-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 <memory>
17 
18 #include "http_constant.h"
19 #include "http_request_utils.h"
20 
21 #include "http_request.h"
22 
23 #define ACE_CURL_EASY_SET_OPTION(handle, opt, data, respData)              \
24     do {                                                                   \
25         CURLcode result = curl_easy_setopt(handle, opt, data);             \
26         if (result != CURLE_OK) {                                          \
27             const char *err = curl_easy_strerror(result);                  \
28             HTTP_REQUEST_ERROR("Failed to set option: %s, %s", #opt, err); \
29             (respData)->SetErrString(err);                                 \
30             (respData)->SetCode(result);                                   \
31             return false;                                                  \
32         }                                                                  \
33     } while (0)
34 
35 #define ACE_CURL_EASY_PERFORM(handle, reqData, respData)                                         \
36     do {                                                                                         \
37         CURLcode result = curl_easy_perform(handle);                                             \
38         if (result != CURLE_OK) {                                                                \
39             const char *err = curl_easy_strerror(result);                                        \
40             HTTP_REQUEST_ERROR("Failed to fetch, err: %s", err);                                 \
41             (respData)->SetErrString(err);                                                       \
42             (respData)->SetCode(result);                                                         \
43             return false;                                                                        \
44         }                                                                                        \
45     } while (0)
46 
47 #define ACE_CURL_EASY_GET_INFO(handle, opt, data, respData)              \
48     do {                                                                 \
49         CURLcode result = curl_easy_getinfo(handle, opt, data);          \
50         if (result != CURLE_OK) {                                        \
51             const char *err = curl_easy_strerror(result);                \
52             HTTP_REQUEST_ERROR("Failed to get info: %s, %s", #opt, err); \
53             (respData)->SetErrString(err);                               \
54             (respData)->SetCode(result);                                 \
55             return false;                                                \
56         }                                                                \
57     } while (0)
58 
59 namespace OHOS {
60 namespace ACELite {
61 
62 std::mutex HttpRequest::mutex;
63 
64 bool HttpRequest::initialized = false;
65 
Initialize()66 bool HttpRequest::Initialize()
67 {
68     std::lock_guard<std::mutex> lock(mutex);
69     if (initialized) {
70         return true;
71     }
72     if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
73         HTTP_REQUEST_ERROR("Failed to initialize 'curl'");
74         return false;
75     }
76     initialized = true;
77     return true;
78 }
79 
Request(RequestData * requestData,ResponseData * responseData)80 bool HttpRequest::Request(RequestData *requestData, ResponseData *responseData)
81 {
82     if (!Initialize()) {
83         return false;
84     }
85     std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> handle(curl_easy_init(), curl_easy_cleanup);
86     if (!handle) {
87         HTTP_REQUEST_ERROR("Failed to create fetch task");
88         return false;
89     }
90 
91 #if NO_SSL_CERTIFICATION
92     // in real life, you should buy a ssl certification and rename it to /etc/ssl/cert.pem
93     ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_SSL_VERIFYPEER, 0L, responseData);
94 #endif // NO_SSL_CERTIFICATION
95 
96     ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEFUNCTION, OnWritingMemoryBody, responseData);
97     ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEDATA, responseData, responseData);
98 
99     std::string responseHeader;
100     ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_HEADERFUNCTION, OnWritingMemoryHeader, responseData);
101     ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_HEADERDATA, &responseHeader, responseData);
102 
103     // Some servers don't like requests that are made without a user-agent field, so we provide one
104     ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_USERAGENT, HttpConstant::HTTP_DEFAULT_USER_AGENT, responseData);
105 
106     if (!SetOption(requestData, handle.get(), responseData)) {
107         HTTP_REQUEST_ERROR("set option failed");
108         return false;
109     }
110 
111     std::vector<std::string> vec(responseData->GetHeaders().size());
112     for (const auto &p : requestData->GetHeader()) {
113         vec.emplace_back(p.first + HttpConstant::HTTP_HEADER_SEPARATOR + p.second);
114     }
115     std::unique_ptr<struct curl_slist, decltype(&curl_slist_free_all)> header(MakeHeaders(vec), curl_slist_free_all);
116     if (header != nullptr) {
117         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_HTTPHEADER, header.get(), responseData);
118     }
119 
120     ACE_CURL_EASY_PERFORM(handle.get(), requestData, responseData);
121 
122     int32_t responseCode;
123     ACE_CURL_EASY_GET_INFO(handle.get(), CURLINFO_RESPONSE_CODE, &responseCode, responseData);
124 
125     responseData->SetCode(responseCode);
126     responseData->ParseHeaders(responseHeader);
127 
128     return true;
129 }
130 
SetOption(RequestData * requestData,CURL * curl,ResponseData * responseData)131 bool HttpRequest::SetOption(RequestData *requestData, CURL *curl, ResponseData *responseData)
132 {
133     const std::string &method = requestData->GetMethod();
134     if (!MethodForGet(method) && !MethodForPost(method)) {
135         HTTP_REQUEST_ERROR("method %s not supported", method.c_str());
136         return false;
137     }
138     ACE_CURL_EASY_SET_OPTION(curl, CURLOPT_CUSTOMREQUEST, method.c_str(), responseData);
139 
140     if (MethodForPost(method)) {
141         ACE_CURL_EASY_SET_OPTION(curl, CURLOPT_POST, 1L, responseData);
142     }
143 
144     if (!requestData->GetBody().empty()) {
145         ACE_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDS, requestData->GetBody().c_str(), responseData);
146         ACE_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDSIZE, requestData->GetBody().size(), responseData);
147     }
148 
149     if (EncodeUrlParam(curl, const_cast<std::string &>(requestData->GetUrl())) && MethodForGet(method)) {
150         requestData->SetHeader(HttpConstant::HTTP_HEADER_KEY_CONTENT_TYPE, HttpConstant::HTTP_CONTENT_TYPE_URL_ENCODE);
151     }
152 
153     HTTP_REQUEST_INFO("final url: ...");
154     ACE_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, requestData->GetUrl().c_str(), responseData);
155     return true;
156 }
157 
OnWritingMemoryBody(const void * data,size_t size,size_t memBytes,void * userData)158 size_t HttpRequest::OnWritingMemoryBody(const void *data, size_t size, size_t memBytes, void *userData)
159 {
160     static_cast<ResponseData *>(userData)->AppendData(static_cast<const char *>(data), size * memBytes);
161     return size * memBytes;
162 }
163 
OnWritingMemoryHeader(const void * data,size_t size,size_t memBytes,void * userData)164 size_t HttpRequest::OnWritingMemoryHeader(const void *data, size_t size, size_t memBytes, void *userData)
165 {
166     static_cast<std::string *>(userData)->append(static_cast<const char *>(data), size * memBytes);
167     return size * memBytes;
168 }
169 
MakeHeaders(const std::vector<std::string> & vec)170 struct curl_slist *HttpRequest::MakeHeaders(const std::vector<std::string> &vec)
171 {
172     struct curl_slist *header = nullptr;
173     for (const auto &s : vec) {
174         header = curl_slist_append(header, s.c_str());
175     }
176     return header;
177 }
178 
179 } // namespace ACELite
180 } // namespace OHOS
181