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