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