• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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