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