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 20namespace OHOS { 21namespace MediaAVCodec { 22namespace { 23constexpr int32_t SERVERPORT = 46666; 24constexpr int32_t BUFFER_LNE = 4096; 25constexpr int32_t DEFAULT_LISTEN = 16; 26constexpr int32_t START_INDEX = 1; 27constexpr int32_t END_INDEX = 2; 28constexpr int32_t THREAD_POOL_MAX_TASKS = 64; 29const std::string SERVER_FILE_PATH = "/data/test/media"; 30} // namespace 31 32HttpServerDemo::HttpServerDemo() {} 33 34HttpServerDemo::~HttpServerDemo() 35{ 36 StopServer(); 37} 38 39void HttpServerDemo::StartServer() 40{ 41 StartServer(SERVERPORT); 42} 43 44void 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 75void 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 92void 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 102void 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 117void 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 131void 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 144int32_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 185int32_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 204void 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 255void 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