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