1 /*
2 * Copyright (C) 2021 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 "http_request.h"
17 #include <thread>
18 #include <unordered_set>
19 #include <sys/time.h>
20 #include "securec.h"
21 #include "wifi_log.h"
22 #include "wifi_common_util.h"
23 #include <netdb.h>
24 #undef LOG_TAG
25 #define LOG_TAG "OHWIFI_UTILS_HTTP_REQ"
26
27 namespace OHOS {
28 namespace Wifi {
HttpRequest()29 HttpRequest::HttpRequest()
30 : mISocketFd(INVALID_SOCKET), iPort(0), strHost(""), strIp(""), strRes(""), strParam(""), httpHead("")
31 {}
32
~HttpRequest()33 HttpRequest::~HttpRequest()
34 {
35 if (mISocketFd > 0) {
36 close(mISocketFd);
37 }
38 mISocketFd = INVALID_SOCKET;
39 }
40
HttpGet(const std::string & strUrl,std::string & strResponse)41 int HttpRequest::HttpGet(const std::string &strUrl, std::string &strResponse)
42 {
43 return HttpRequestExec("GET", strUrl, "", strResponse);
44 }
45
HttpPost(const std::string & strUrl,const std::string & strData,std::string & strResponse)46 int HttpRequest::HttpPost(const std::string &strUrl, const std::string &strData, std::string &strResponse)
47 {
48 return HttpRequestExec("POST", strUrl, strData, strResponse);
49 }
50
HttpRequestExec(const std::string & strMethod,const std::string & strUrl,const std::string & strData,std::string & strResponse)51 int HttpRequest::HttpRequestExec(
52 const std::string &strMethod, const std::string &strUrl, const std::string &strData, std::string &strResponse)
53 {
54 int iRet = 0;
55 /* Check whether the URL is valid. */
56 if (strUrl.empty()) {
57 LOGE("HttpRequest::HttpRequestExec URL is null\n");
58 return -1;
59 }
60
61 /* Limit the URL length. */
62 if (strUrl.length() > URLSIZE) {
63 LOGE("HttpRequest::HttpRequestExec URL length > %{public}d, error\n", URLSIZE);
64 return -1;
65 }
66
67 iRet = GetHostAddrFromUrl(strUrl);
68 if (iRet != 0) {
69 LOGE("HttpRequest::HttpRequestExec get HostAddr failed.\n");
70 return -1;
71 }
72
73 /* Create an HTTP protocol header. */
74 HttpHeadCreate(strMethod, strData);
75
76 /* Create socket */
77 mISocketFd = socket(AF_INET, SOCK_STREAM, 0);
78 if (mISocketFd < 0) {
79 LOGE("HttpRequest::HttpRequestExec socket error! Error code: %{public}d.", errno);
80 return -1;
81 }
82
83 /* Bind address and port */
84 GetPortFromUrl();
85 if (iPort < 0) {
86 LOGE("HttpRequest::HttpRequestExec get port failed from URL!\n");
87 close(mISocketFd);
88 mISocketFd = INVALID_SOCKET;
89 return -1;
90 }
91
92 iRet = GetIPFromUrl();
93 if (iRet != 0) {
94 LOGE("HttpRequest::HttpRequestExec get ip failed from URL!\n");
95 close(mISocketFd);
96 mISocketFd = INVALID_SOCKET;
97 return -1;
98 }
99
100 LOGI("iPort= [%{public}d],strIP= [%s]\n", iPort, strIp.c_str());
101
102 iRet = HttpConnect(strResponse);
103 if (iRet != 0) {
104 LOGE("HttpRequest::HttpConnect failed!\n");
105 close(mISocketFd);
106 mISocketFd = INVALID_SOCKET;
107 return -1;
108 }
109 close(mISocketFd);
110 mISocketFd = INVALID_SOCKET;
111 LOGD("HttpRequest::HttpConnect success!\n");
112 return 0;
113 }
114
HttpConnect(std::string & strResponse)115 int HttpRequest::HttpConnect(std::string &strResponse)
116 {
117 int flags = 0;
118 struct sockaddr_in servaddr = {0};
119 bzero(&servaddr, sizeof(servaddr));
120 servaddr.sin_family = AF_INET;
121 servaddr.sin_port = htons(iPort);
122 if (inet_pton(AF_INET, strIp.c_str(), &servaddr.sin_addr) <= 0) {
123 LOGE("HttpConnect inet_pton error! errno: %{public}d", errno);
124 return -1;
125 }
126
127 /* Set non-blocking */
128 flags = fcntl(mISocketFd, F_GETFL, 0);
129 if (fcntl(mISocketFd, F_SETFL, static_cast<size_t>(flags) | O_NONBLOCK) == -1) {
130 LOGE("HttpConnect fcntl error! Error code: %{public}d", errno);
131 return -1;
132 }
133
134 /* Non-blocking connection */
135 int iRet = connect(mISocketFd, reinterpret_cast<struct sockaddr *>(&servaddr), sizeof(servaddr));
136 if (iRet == 0) {
137 iRet = HttpDataTransmit(mISocketFd);
138 if (iRet != 0) {
139 LOGE("HttpConnect HttpDataTransmit error\n");
140 return -1;
141 }
142 strResponse = strRes;
143 return 0;
144 } else if (iRet < 0) {
145 if (errno != EINPROGRESS) {
146 LOGE("HttpDataTransmit connect error! Error code: %{public}d", errno);
147 return -1;
148 }
149 }
150
151 iRet = SocketFdCheck(mISocketFd);
152 if (iRet > 0) {
153 iRet = HttpDataTransmit(mISocketFd);
154 if (iRet != 0) {
155 LOGE("HttpRequest::HttpDataTransmit HttpDataTransmit error!");
156 return -1;
157 }
158 strResponse = strRes;
159 return 0;
160 } else {
161 LOGE("HttpRequest::HttpDataTransmit SocketFdCheck error!");
162 return -1;
163 }
164 return 0;
165 }
166
HttpHeadCreate(const std::string & strMethod,const std::string & strData)167 void HttpRequest::HttpHeadCreate(const std::string &strMethod, const std::string &strData)
168 {
169 std::string strHttpHead;
170 strHttpHead += strMethod;
171 strHttpHead += " /";
172 strHttpHead += strParam;
173 strHttpHead += " HTTP/1.1\r\n";
174 strHttpHead += "Accept: */*\r\n";
175 strHttpHead += "Accept-Language: cn\r\n";
176 strHttpHead += "User-Agent: Mozilla/4.0\r\n";
177 strHttpHead += "Host: ";
178 strHttpHead += strHost;
179 strHttpHead += "\r\n";
180 strHttpHead += "Cache-Control: no-cache\r\n";
181 strHttpHead += "Connection: Keep-Alive\r\n";
182
183 if (strMethod == "POST") {
184 std::size_t uLen = strData.length();
185 strHttpHead += "Content-Type: application/x-www-form-urlencoded\r\n";
186 strHttpHead += "Content-Length: ";
187 strHttpHead += std::to_string(uLen);
188 strHttpHead += "\r\n\r\n";
189 strHttpHead += strData;
190 }
191 strHttpHead += "\r\n\r\n";
192 httpHead = strHttpHead;
193 }
194
HttpDataTransmit(const int & iSockFd)195 int HttpRequest::HttpDataTransmit(const int &iSockFd)
196 {
197 int ret = send(iSockFd, httpHead.c_str(), httpHead.length() + 1, 0);
198 if (ret < 0) {
199 LOGE("HttpRequest::HttpDataTransmit send error! Error code: %{public}d", errno);
200 return -1;
201 }
202 char *buf = new char[BUFSIZE]();
203 if (buf == nullptr) {
204 return -1;
205 }
206 constexpr int timeoutMs = 500;
207 constexpr int timeRate = 1000;
208 struct timeval tv;
209 struct timeval tvEnd;
210 gettimeofday(&tv, nullptr);
211 long long tvTime = tv.tv_sec * timeRate + tv.tv_usec / timeRate;
212 long long tvEndTime;
213 bool bDataRec = false;
214 while (1) {
215 gettimeofday(&tvEnd, nullptr);
216 tvEndTime = tvEnd.tv_sec * timeRate + tvEnd.tv_usec / timeRate;
217 if (tvEndTime - tvTime > timeoutMs) {
218 LOGE("HttpRequest::HttpDataTransmit recv timeout\n");
219 delete[] buf;
220 buf = nullptr;
221 return -1;
222 }
223 (void)memset_s(buf, BUFSIZE, 0, BUFSIZE);
224 ret = recv(iSockFd, buf, BUFSIZE, 0);
225 if (ret == 0) {
226 /* The connection is closed. */
227 if (!bDataRec) {
228 LOGE("HttpRequest::HttpDataTransmit recv error! Error code: %{public}d", errno);
229 delete[] buf;
230 buf = nullptr;
231 return -1;
232 } else {
233 LOGD("HttpRequest::HttpDataTransmit recv success\n");
234 delete[] buf;
235 buf = nullptr;
236 return 0;
237 }
238 } else if (ret > 0) {
239 bDataRec = true;
240 strRes += buf;
241 } else if (ret < 0) {
242 /* Error */
243 if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
244 LOGD("HttpRequest::HttpDataTransmit recv not finish!\n");
245 continue;
246 } else {
247 LOGE("HttpRequest::HttpDataTransmit recv error! Error code: %{public}d", errno);
248 delete[] buf;
249 buf = nullptr;
250 return -1;
251 }
252 }
253 }
254 }
255
GetHostAddrFromUrl(const std::string & strUrl)256 int HttpRequest::GetHostAddrFromUrl(const std::string &strUrl)
257 {
258 std::string urlTmp(strUrl);
259 std::string urlTmp2;
260 if (urlTmp.find("https://") != std::string::npos) {
261 urlTmp2 = urlTmp.substr(HTTPS_HEADER_LENGTH);
262 } else if (urlTmp.find("http://") != std::string::npos) {
263 urlTmp2 = urlTmp.substr(HTTP_HEADER_LENGTH);
264 } else {
265 urlTmp2 = urlTmp;
266 }
267 LOGI("GetHostAddrFromUrl urlTmp2 = [%s].\n", urlTmp2.c_str());
268
269 if (!urlTmp2.empty()) {
270 strHost = urlTmp2.substr(0, urlTmp2.find("/"));
271 strParam = urlTmp2.substr(urlTmp2.find("/") + 1);
272 } else {
273 LOGE("GetHostAddrFromUrl url is wrong\n");
274 return -1;
275 }
276
277 return 0;
278 }
279
GetPortFromUrl()280 void HttpRequest::GetPortFromUrl()
281 {
282 if (strHost.find(":") != std::string::npos) {
283 std::string strPort = strHost.substr(strHost.find(":") + 1);
284 iPort = CheckDataLegal(strPort);
285 } else {
286 iPort = DEFAULT_PORT;
287 }
288 }
289
290 std::mutex g_mutex;
291 std::unordered_set<HostData*> g_HostDataSet;
GetHostThread(HostData * pThreadData)292 void GetHostThread(HostData* pThreadData)
293 {
294 std::string ipOrDomain;
295 {
296 std::unique_lock<std::mutex> lck(g_mutex);
297 if (g_HostDataSet.find(pThreadData) == g_HostDataSet.end()) {
298 LOGE("GetHostThread Error.");
299 return;
300 }
301 ipOrDomain = pThreadData->strIpOrDomain;
302 }
303 LOGE("GetHostThread ipOrDomain is %{public}s.", ipOrDomain.c_str());
304 addrinfo *res = nullptr;
305 int status = getaddrinfo(ipOrDomain.c_str(), nullptr, nullptr, &res);
306 if (status < 0) {
307 LOGE("getaddrinfo errno %{public}d %{public}s", errno, strerror(errno));
308 return;
309 }
310
311 for (addrinfo *tmp = res; tmp != nullptr; tmp = tmp->ai_next) {
312 if (tmp->ai_family == AF_INET) {
313 auto addr = reinterpret_cast<sockaddr_in *>(tmp->ai_addr);
314 char ip[50] = {0};
315 inet_ntop(AF_INET, &addr->sin_addr, ip, sizeof(ip));
316 pThreadData->strIp = ip;
317 } else if (tmp->ai_family == AF_INET6) {
318 auto addr = reinterpret_cast<sockaddr_in6 *>(tmp->ai_addr);
319 char ip[200] = {0};
320 inet_ntop(AF_INET6, &addr->sin6_addr, ip, sizeof(ip));
321 pThreadData->strIp = ip;
322 }
323 }
324 freeaddrinfo(res);
325 LOGE("GetHostThread ipaddr is %{public}s.", pThreadData->strIp.c_str());
326
327 pThreadData->isIp = true;
328 pThreadData->waitTimeout.notify_one();
329 return;
330 }
331
GetIPFromUrl()332 int HttpRequest::GetIPFromUrl()
333 {
334 std::string strIpOrDomain;
335 if (strHost.find(":") != std::string::npos) {
336 strIpOrDomain = strHost.substr(0, strHost.find(":"));
337 } else {
338 strIpOrDomain = strHost;
339 }
340 if (strIpOrDomain.empty()) {
341 LOGE("GetIPFromUrl strIpOrDomain is null.\n");
342 return -1;
343 }
344
345 if (inet_addr(strIpOrDomain.c_str()) == INADDR_NONE) {
346 LOGI("GetIPFromUrl Url maybe contain Domain.");
347 HostData* pData = nullptr;
348 {
349 std::unique_lock<std::mutex> lck(g_mutex);
350 pData = new HostData;
351 if (pData == nullptr) {
352 LOGE("GetIPFromUrl new HostData error.\n");
353 return -1;
354 }
355 g_HostDataSet.emplace(pData);
356 pData->strIpOrDomain = strIpOrDomain;
357 }
358
359 int iRlt = -1;
360 std::thread getHost = std::thread(&GetHostThread, pData);
361 pthread_setname_np(getHost.native_handle(), "GetHostThread");
362 getHost.detach();
363
364 bool bTimeOut = false;
365 const int timeoutMs = 150;
366 {
367 std::unique_lock<std::mutex> lck(g_mutex);
368 if (pData->waitTimeout.wait_for(lck, std::chrono::milliseconds(timeoutMs)) == std::cv_status::timeout) {
369 bTimeOut = true;
370 }
371 }
372
373 if (!bTimeOut) {
374 std::unique_lock<std::mutex> lck(g_mutex);
375 if (g_HostDataSet.find(pData) == g_HostDataSet.end()) {
376 if (pData != nullptr) {
377 delete pData;
378 pData = nullptr;
379 }
380 LOGD("GetHostThread None.");
381 return -1;
382 }
383 if (pData->isIp) {
384 iRlt = 0;
385 strIp = pData->strIp;
386 g_HostDataSet.erase(pData);
387 delete pData;
388 pData = nullptr;
389 LOGD("Get ip ok.");
390 }
391 }
392 return iRlt;
393 } else {
394 LOGI("GetIPFromUrl Url contain ip\n");
395 strIp = strIpOrDomain;
396 return 0;
397 }
398 }
399
SocketFdCheck(const int & iSockFd) const400 int HttpRequest::SocketFdCheck(const int &iSockFd) const
401 {
402 struct timeval timeout = {0};
403 fd_set rSet, wSet;
404 FD_ZERO(&rSet);
405 FD_ZERO(&wSet);
406 FD_SET(iSockFd, &rSet);
407 FD_SET(iSockFd, &wSet);
408 timeout.tv_sec = SEND_HTTP_DELAY_TIME;
409 timeout.tv_usec = 0;
410 int iRet = select(iSockFd + 1, &rSet, &wSet, nullptr, &timeout);
411 if (iRet < 0) {
412 LOGE("HttpRequest::SocketFdCheck select failed");
413 /* An error occurs during the selection. All descriptor sets are cleared. */
414 return -1;
415 } else if (iRet == 0) {
416 LOGE("HttpRequest::SocketFdCheck time out");
417 /* Timeout */
418 return -1;
419 }
420 /* Check whether SocketFd is writable and unreadable. */
421 int iW = FD_ISSET(iSockFd, &wSet);
422 int iR = FD_ISSET(iSockFd, &rSet);
423 if (iW && !iR) {
424 char error[4] = "";
425 socklen_t len = sizeof(error);
426 int ret = getsockopt(iSockFd, SOL_SOCKET, SO_ERROR, error, &len);
427 if (ret == 0) {
428 if (!strcmp(error, "")) {
429 /* Indicates the number of prepared descriptors. */
430 return iRet;
431 } else {
432 LOGE("HttpRequest::SocketFdCheck getsockopt failed. error code:%{public}d", errno);
433 return -1;
434 }
435 } else {
436 LOGE("HttpRequest::SocketFdCheck getsockopt failed. error code:%{public}d", errno);
437 return -1;
438 }
439 } else {
440 LOGE("HttpRequest::SocketFdCheck "
441 "Whether sockFd is in the writable character set: %{public}d, is it in the "
442 "readable character set: %{public}d\t (0 means not in)\n",
443 iW,
444 iR);
445 return -1;
446 }
447 }
448 } // namespace Wifi
449 } // namespace OHOS