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