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