1 /*
2  * Copyright (C) 2023 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_server_demo.h"
17 #include "unittest_log.h"
18 
19 namespace OHOS {
20 namespace MediaAVCodec {
21 namespace {
22 constexpr int32_t SERVERPORT = 46666;
23 constexpr int32_t BUFFER_LNE = 4096;
24 constexpr int32_t DEFAULT_LISTEN = 16;
25 constexpr int32_t START_INDEX = 1;
26 constexpr int32_t END_INDEX = 2;
27 constexpr int32_t THREAD_POOL_MAX_TASKS = 64;
28 const std::string SERVER_FILE_PATH = "/data/test/media";
29 } // namespace
30 
HttpServerDemo()31 HttpServerDemo::HttpServerDemo() {}
32 
~HttpServerDemo()33 HttpServerDemo::~HttpServerDemo()
34 {
35     StopServer();
36 }
37 
StartServer()38 void HttpServerDemo::StartServer()
39 {
40     StartServer(SERVERPORT);
41 }
42 
StartServer(int32_t port)43 void HttpServerDemo::StartServer(int32_t port)
44 {
45     threadPool_ = std::make_unique<ThreadPool>("httpServerThreadPool");
46     threadPool_->SetMaxTaskNum(THREAD_POOL_MAX_TASKS);
47     listenFd_ = socket(AF_INET, SOCK_STREAM, 0);
48     if (listenFd_ == -1) {
49         std::cout << "listenFd error" << std::endl;
50         return;
51     }
52     struct sockaddr_in servaddr;
53     (void)memset_s(&servaddr, sizeof(servaddr), 0, sizeof(servaddr));
54     servaddr.sin_family = AF_INET;
55     inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
56     servaddr.sin_port = htons(port);
57     int32_t reuseAddr = 1;
58     setsockopt(listenFd_, SOL_SOCKET, SO_REUSEADDR, static_cast<void *>(&reuseAddr), sizeof(int32_t));
59     setsockopt(listenFd_, SOL_SOCKET, SO_REUSEPORT, static_cast<void *>(&reuseAddr), sizeof(int32_t));
60     int32_t flags = fcntl(listenFd_, F_GETFL, 0);
61     fcntl(listenFd_, F_SETFL, flags | O_NONBLOCK);
62 
63     int32_t ret = bind(listenFd_, reinterpret_cast<struct sockaddr *>(&servaddr), sizeof(servaddr));
64     if (ret == -1) {
65         std::cout << "bind error" << std::endl;
66         return;
67     }
68     listen(listenFd_, DEFAULT_LISTEN);
69     isRunning_.store(true);
70     serverLoop_ = std::make_unique<std::thread>(&HttpServerDemo::ServerLoopFunc, this);
71 }
72 
StopServer()73 void HttpServerDemo::StopServer()
74 {
75     if (!isRunning_.load()) {
76         return;
77     }
78     isRunning_.store(false);
79     std::string stopMsg = "Stop Server";
80     std::cout << stopMsg << std::endl;
81     if (serverLoop_ != nullptr && serverLoop_->joinable()) {
82         serverLoop_->join();
83         serverLoop_.reset();
84     }
85     threadPool_->Stop();
86     close(listenFd_);
87     listenFd_ = 0;
88 }
89 
CloseFd(int32_t & connFd,int32_t & fileFd,bool connCond,bool fileCond)90 void HttpServerDemo::CloseFd(int32_t &connFd, int32_t &fileFd, bool connCond, bool fileCond)
91 {
92     if (connCond) {
93         close(connFd);
94     }
95     if (fileCond) {
96         close(fileFd);
97     }
98 }
99 
GetRange(const std::string & recvStr,int32_t & startPos,int32_t & endPos)100 void HttpServerDemo::GetRange(const std::string &recvStr, int32_t &startPos, int32_t &endPos)
101 {
102     std::regex regexRange("Range:\\sbytes=(\\d+)-(\\d+)?");
103     std::regex regexDigital("\\d+");
104     std::smatch matchVals;
105     if (std::regex_search(recvStr, matchVals, regexRange)) {
106         std::string startStr = matchVals[START_INDEX].str();
107         std::string endStr = matchVals[END_INDEX].str();
108         startPos = std::regex_match(startStr, regexDigital) ? std::stoi(startStr) : 0;
109         endPos = std::regex_match(endStr, regexDigital) ? std::stoi(endStr) : INT32_MAX;
110     } else {
111         endPos = 0;
112     }
113 }
114 
GetKeepAlive(const std::string & recvStr,int32_t & keep)115 void HttpServerDemo::GetKeepAlive(const std::string &recvStr, int32_t &keep)
116 {
117     std::regex regexRange("Keep-(A|a)live:\\stimeout=(\\d+)");
118     std::regex regexDigital("\\d+");
119     std::smatch matchVals;
120     if (std::regex_search(recvStr, matchVals, regexRange)) {
121         std::string keepStr = matchVals[END_INDEX].str();
122         keep = std::regex_match(keepStr, regexDigital) ? std::stoi(keepStr) : 0;
123     } else {
124         std::cout << "Keep-Alive not found" << std::endl;
125         keep = 0;
126     }
127 }
128 
GetFilePath(const std::string & recvStr,std::string & path)129 void HttpServerDemo::GetFilePath(const std::string &recvStr, std::string &path)
130 {
131     std::regex regexRange("GET\\s(.+)\\sHTTP");
132     std::smatch matchVals;
133     if (std::regex_search(recvStr, matchVals, regexRange)) {
134         path = matchVals[1].str();
135     } else {
136         std::cout << "path not found" << std::endl;
137         path = "";
138     }
139     path = SERVER_FILE_PATH + path;
140 }
141 
SendRequestSize(int32_t & connFd,int32_t & fileFd,const std::string & recvStr)142 int32_t HttpServerDemo::SendRequestSize(int32_t &connFd, int32_t &fileFd, const std::string &recvStr)
143 {
144     int32_t startPos = 0;
145     int32_t endPos = 0;
146     int32_t ret = 0;
147     int32_t fileSize = lseek(fileFd, 0, SEEK_END);
148     GetRange(recvStr, startPos, endPos);
149     if (endPos <= 0) {
150         endPos = fileSize - 1;
151     }
152     int32_t size = std::min(endPos, fileSize) - std::max(startPos, 0) + 1;
153     if (endPos < startPos) {
154         size = 0;
155     }
156     if (startPos > 0) {
157         ret = lseek(fileFd, startPos, SEEK_SET);
158     } else {
159         ret = lseek(fileFd, 0, SEEK_SET);
160     }
161     if (ret < 0) {
162         std::cout << "lseek is failed, ret=" << ret << std::endl;
163         CloseFd(connFd, fileFd, true, true);
164         return -1;
165     }
166     startPos = std::max(startPos, 0);
167     endPos = std::min(endPos, fileSize);
168     std::stringstream sstr;
169     sstr << "HTTP/2 206 Partial Content\r\n";
170     sstr << "Server:demohttp\r\n";
171     sstr << "Content-Length: " << size << "\r\n";
172     sstr << "Content-Range: bytes " << startPos << "-" << endPos << "/" << fileSize << "\r\n\r\n";
173     std::string httpContext = sstr.str();
174     ret = send(connFd, httpContext.c_str(), httpContext.size(), MSG_NOSIGNAL);
175     if (ret <= 0) {
176         std::cout << "send httpContext failed, ret=" << ret << std::endl;
177         CloseFd(connFd, fileFd, true, true);
178         return -1;
179     }
180     return size;
181 }
182 
SetKeepAlive(int32_t & connFd,int32_t & keepAlive,int32_t & keepIdle)183 int32_t HttpServerDemo::SetKeepAlive(int32_t &connFd, int32_t &keepAlive, int32_t &keepIdle)
184 {
185     int ret = 0;
186     if (keepIdle <= 0) {
187         return ret;
188     }
189     int32_t keepInterval = 1;
190     int32_t keepCount = 1;
191     ret = setsockopt(connFd, SOL_SOCKET, SO_KEEPALIVE, static_cast<void *>(&keepAlive), sizeof(keepAlive));
192     UNITTEST_CHECK_AND_RETURN_RET_LOG(ret == 0, ret, "set SO_KEEPALIVE failed, ret=%d", ret);
193     ret = setsockopt(connFd, SOL_TCP, TCP_KEEPIDLE, static_cast<void *>(&keepIdle), sizeof(keepIdle));
194     UNITTEST_CHECK_AND_RETURN_RET_LOG(ret == 0, ret, "set TCP_KEEPIDLE failed, ret=%d", ret);
195     ret = setsockopt(connFd, SOL_TCP, TCP_KEEPINTVL, static_cast<void *>(&keepInterval), sizeof(keepInterval));
196     UNITTEST_CHECK_AND_RETURN_RET_LOG(ret == 0, ret, "set TCP_KEEPINTVL failed, ret=%d", ret);
197     ret = setsockopt(connFd, SOL_TCP, TCP_KEEPCNT, static_cast<void *>(&keepCount), sizeof(keepCount));
198     UNITTEST_CHECK_AND_RETURN_RET_LOG(ret == 0, ret, "set TCP_KEEPCNT failed, ret=%d", ret);
199     return ret;
200 }
201 
FileReadFunc(int32_t connFd)202 void HttpServerDemo::FileReadFunc(int32_t connFd)
203 {
204     char recvBuff[BUFFER_LNE] = {0};
205     int32_t ret = recv(connFd, recvBuff, BUFFER_LNE - 1, 0);
206     int32_t fileFd = -1;
207     int32_t keepAlive = 1;
208     int32_t keepIdle = 10;
209     std::string recvStr = std::string(recvBuff);
210     std::cout << "recv recvStr=" << recvStr << std::endl;
211     std::string path = "";
212     if (ret <= 0) {
213         std::cout << "recv error, ret=" << ret << std::endl;
214         CloseFd(connFd, fileFd, true, false);
215         return;
216     }
217     GetKeepAlive(recvStr, keepIdle);
218     (void)SetKeepAlive(connFd, keepAlive, keepIdle);
219     GetFilePath(recvStr, path);
220     if (path == "") {
221         std::cout << "Path error, path:" << path << std::endl;
222         CloseFd(connFd, fileFd, true, false);
223         return;
224     }
225     fileFd = open(path.c_str(), O_RDONLY);
226     if (fileFd == -1) {
227         std::cout << "File does not exist, path:" << path << std::endl;
228         CloseFd(connFd, fileFd, true, true);
229         return;
230     }
231     int32_t size = SendRequestSize(connFd, fileFd, recvStr);
232     while (size > 0) {
233         int32_t sendSize = std::min(BUFFER_LNE, size);
234         std::vector<uint8_t> fileBuff(sendSize);
235         ret = read(fileFd, fileBuff.data(), sendSize);
236         UNITTEST_CHECK_AND_BREAK_LOG(ret > 0, "read file failed, ret=%d", ret);
237         size -= ret;
238         ret = send(connFd, fileBuff.data(), std::min(ret, sendSize), MSG_NOSIGNAL);
239         if (ret <= 0) {
240             std::cout << "send file buffer failed, ret=" << ret << std::endl;
241             break;
242         }
243     }
244     if (ret > 0) {
245         std::string httpContext = "HTTP/2 200 OK\r\nServer:demohttp\r\n";
246         send(connFd, httpContext.c_str(), httpContext.size(), MSG_NOSIGNAL);
247     }
248     CloseFd(connFd, fileFd, true, true);
249 }
250 
ServerLoopFunc()251 void HttpServerDemo::ServerLoopFunc()
252 {
253     while (isRunning_.load()) {
254         struct sockaddr_in caddr;
255         int32_t len = sizeof(caddr);
256         int32_t connFd =
257             accept(listenFd_, reinterpret_cast<struct sockaddr *>(&caddr), reinterpret_cast<socklen_t *>(&len));
258         if (connFd < 0) {
259             continue;
260         }
261         threadPool_->AddTask([connFd]() { FileReadFunc(connFd); });
262     }
263 }
264 } // namespace MediaAVCodec
265 } // namespace OHOS