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