diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index c5396d3..2862043 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -17,4 +17,4 @@ jobs: - name: build run: cmake --build build --config Debug --parallel 10 - name: test - run: cd build ; ctest -j 10 -C Debug --output-on-failure + run: cd build ; ctest -j 10 --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index c591bf1..b351415 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.8) ## ## PROJECT @@ -68,7 +68,7 @@ add_library(${PROJECT_NAME}::${NADJIEB_MJPEG_STREAMER_TARGET_NAME} ALIAS ${NADJI if (${CMAKE_VERSION} VERSION_LESS "3.8.0") target_compile_features(${NADJIEB_MJPEG_STREAMER_TARGET_NAME} INTERFACE cxx_range_for) else() - target_compile_features(${NADJIEB_MJPEG_STREAMER_TARGET_NAME} INTERFACE cxx_std_11) + target_compile_features(${NADJIEB_MJPEG_STREAMER_TARGET_NAME} INTERFACE cxx_std_17) endif() target_include_directories( diff --git a/Makefile b/Makefile index 633bf30..8694453 100644 --- a/Makefile +++ b/Makefile @@ -82,6 +82,6 @@ check-single-includes: @for x in $(SRCS); do \ echo "Checking self-sufficiency of $$x..." ; \ echo "#include <$$x>\nint main() {}\n" | $(SED) 's|include/||' > single_include_test.cpp; \ - $(CXX) $(CXXFLAGS) -Iinclude -std=c++11 single_include_test.cpp -o single_include_test; \ + $(CXX) $(CXXFLAGS) -Iinclude -std=c++17 single_include_test.cpp -o single_include_test; \ rm -f single_include_test.cpp single_include_test; \ done diff --git a/README.md b/README.md index c2e36ed..722228f 100644 --- a/README.md +++ b/README.md @@ -57,11 +57,11 @@ int main() { // By default "/shutdown" is the target to graceful shutdown the streamer // if you want to change the target to graceful shutdown: - // streamer.setShutdownTarget("/stop"); + // streamer.setShutdownTarget("/stop"); - // By default 1 worker is used for streaming - // if you want to use 4 workers: - // streamer.start(8080, 4); + // By default std::thread::hardware_concurrency() workers is used for streaming + // if you want to use 4 workers instead: + // streamer.start(8080, 4); streamer.start(8080); // Visit /shutdown or another defined target to stop the loop and graceful shutdown diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index afb46ee..81b699f 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,11 +1,11 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.8) project(nadjieb_mjpeg_streamer_examples LANGUAGES CXX) find_package(nadjieb_mjpeg_streamer REQUIRED) find_package(OpenCV 4.2 REQUIRED) add_executable(example ./example.cpp) -target_compile_features(example PRIVATE cxx_std_11) +target_compile_features(example PRIVATE cxx_std_17) target_link_libraries(example PRIVATE nadjieb_mjpeg_streamer::nadjieb_mjpeg_streamer diff --git a/examples/example.cpp b/examples/example.cpp index f95d324..063f2b7 100644 --- a/examples/example.cpp +++ b/examples/example.cpp @@ -20,8 +20,8 @@ int main() { // if you want to change the target to graceful shutdown: // streamer.setShutdownTarget("/stop"); - // By default 1 worker is used for streaming - // if you want to use 4 workers: + // By default std::thread::hardware_concurrency() workers is used for streaming + // if you want to use 4 workers instead: // streamer.start(8080, 4); streamer.start(8080); diff --git a/include/nadjieb/mjpeg_streamer.hpp b/include/nadjieb/mjpeg_streamer.hpp index 2bcf4c4..c58547f 100644 --- a/include/nadjieb/mjpeg_streamer.hpp +++ b/include/nadjieb/mjpeg_streamer.hpp @@ -43,7 +43,7 @@ class MJPEGStreamer : public nadjieb::utils::NonCopyable { public: virtual ~MJPEGStreamer() { stop(); } - void start(int port, int num_workers = 1) { + void start(int port, int num_workers = std::thread::hardware_concurrency()) { publisher_.start(num_workers); listener_.withOnMessageCallback(on_message_cb_).withOnBeforeCloseCallback(on_before_close_cb_).runAsync(port); @@ -104,6 +104,19 @@ class MJPEGStreamer : public nadjieb::utils::NonCopyable { return cb_res; } + if (!publisher_.pathExists(req.getTarget())) { + nadjieb::net::HTTPResponse not_found_res; + not_found_res.setVersion(req.getVersion()); + not_found_res.setStatusCode(404); + not_found_res.setStatusText("Not Found"); + auto not_found_res_str = not_found_res.serialize(); + + nadjieb::net::sendViaSocket(sockfd, not_found_res_str.c_str(), not_found_res_str.size(), 0); + + cb_res.close_conn = true; + return cb_res; + } + nadjieb::net::HTTPResponse init_res; init_res.setVersion(req.getVersion()); init_res.setStatusCode(200); @@ -116,7 +129,7 @@ class MJPEGStreamer : public nadjieb::utils::NonCopyable { nadjieb::net::sendViaSocket(sockfd, init_res_str.c_str(), init_res_str.size(), 0); - publisher_.add(req.getTarget(), sockfd); + publisher_.add(sockfd, req.getTarget()); return cb_res; }; diff --git a/include/nadjieb/net/publisher.hpp b/include/nadjieb/net/publisher.hpp index 93e1f20..73ad909 100644 --- a/include/nadjieb/net/publisher.hpp +++ b/include/nadjieb/net/publisher.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -20,7 +21,7 @@ class Publisher : public nadjieb::utils::NonCopyable, public nadjieb::utils::Run public: virtual ~Publisher() { stop(); } - void start(int num_workers = 1) { + void start(int num_workers = std::thread::hardware_concurrency()) { state_ = nadjieb::utils::State::BOOTING; end_publisher_ = false; workers_.reserve(num_workers); @@ -44,7 +45,8 @@ class Publisher : public nadjieb::utils::NonCopyable, public nadjieb::utils::Run workers_.clear(); } - path2clients_.clear(); + topics_.clear(); + path_by_client_.clear(); while (!payloads_.empty()) { payloads_.pop(); @@ -52,30 +54,24 @@ class Publisher : public nadjieb::utils::NonCopyable, public nadjieb::utils::Run state_ = nadjieb::utils::State::TERMINATED; } - void add(const std::string& path, const SocketFD& sockfd) { + void add(const SocketFD& sockfd, const std::string& path) { if (end_publisher_) { return; } - const std::lock_guard lock(p2c_mtx_); - path2clients_[path].emplace_back(NADJIEB_MJPEG_STREAMER_POLLFD{sockfd, POLLWRNORM, 0}); + topics_[path].addClient(sockfd); + + std::unique_lock lock(path_by_client_mtx_); + path_by_client_[sockfd] = path; } + bool pathExists(const std::string& path) { return (topics_.find(path) != topics_.end()); } + void removeClient(const SocketFD& sockfd) { - const std::lock_guard lock(p2c_mtx_); - for (auto it = path2clients_.begin(); it != path2clients_.end();) { - it->second.erase( - std::remove_if( - it->second.begin(), it->second.end(), - [&](const NADJIEB_MJPEG_STREAMER_POLLFD& pfd) { return pfd.fd == sockfd; }), - it->second.end()); - - if (it->second.empty()) { - it = path2clients_.erase(it); - } else { - ++it; - } - } + std::unique_lock lock(path_by_client_mtx_); + topics_[path_by_client_[sockfd]].removeClient(sockfd); + + path_by_client_.erase(sockfd); } void enqueue(const std::string& path, const std::string& buffer) { @@ -83,33 +79,39 @@ class Publisher : public nadjieb::utils::NonCopyable, public nadjieb::utils::Run return; } - if (path2clients_.find(path) == path2clients_.end()) { - return; - } + topics_[path].setBuffer(buffer); - std::unique_lock payloads_lock(payloads_mtx_); - payloads_.emplace(path, buffer); - payloads_lock.unlock(); - condition_.notify_one(); - } + for (const auto& client : topics_[path].getClients()) { + if (topics_[path].getQueueSize(client.fd) > LIMIT_QUEUE_PER_CLIENT) { + continue; + } + + std::unique_lock payloads_lock(payloads_mtx_); + payloads_.emplace(path, client); + topics_[path].increaseQueue(client.fd); + payloads_lock.unlock(); - bool hasClient(const std::string& path) { - const std::lock_guard lock(p2c_mtx_); - return path2clients_.find(path) != path2clients_.end() && !path2clients_[path].empty(); + condition_.notify_one(); + } } + bool hasClient(const std::string& path) { return topics_[path].hasClient(); } + private: - typedef std::pair Payload; + typedef std::pair Payload; std::condition_variable condition_; std::vector workers_; std::queue payloads_; - std::unordered_map> path2clients_; + std::unordered_map path_by_client_; + std::unordered_map topics_; std::mutex cv_mtx_; - std::mutex p2c_mtx_; + std::mutex path_by_client_mtx_; std::mutex payloads_mtx_; bool end_publisher_ = true; + const static int LIMIT_QUEUE_PER_CLIENT = 5; + void worker() { while (!end_publisher_) { std::unique_lock cv_lock(cv_mtx_); @@ -123,34 +125,33 @@ class Publisher : public nadjieb::utils::NonCopyable, public nadjieb::utils::Run Payload payload = std::move(payloads_.front()); payloads_.pop(); + topics_[payload.first].decreaseQueue(payload.second.fd); payloads_lock.unlock(); cv_lock.unlock(); + auto buffer = topics_[payload.first].getBuffer(); std::string res_str = "--nadjiebmjpegstreamer\r\n" "Content-Type: image/jpeg\r\n" "Content-Length: " - + std::to_string(payload.second.size()) + "\r\n\r\n" + payload.second; - - const std::lock_guard lock(p2c_mtx_); - for (auto& client : path2clients_[payload.first]) { - auto socket_count = pollSockets(&client, 1, 1); + + std::to_string(buffer.size()) + "\r\n\r\n" + buffer; - if (socket_count == NADJIEB_MJPEG_STREAMER_SOCKET_ERROR) { - throw std::runtime_error("pollSockets() failed\n"); - } + auto socket_count = pollSockets(&payload.second, 1, 1); - if (socket_count == 0) { - continue; - } + if (socket_count == NADJIEB_MJPEG_STREAMER_SOCKET_ERROR) { + throw std::runtime_error("pollSockets() failed\n"); + } - if (client.revents != POLLWRNORM) { - throw std::runtime_error("revents != POLLWRNORM\n"); - } + if (socket_count == 0) { + continue; + } - sendViaSocket(client.fd, res_str.c_str(), res_str.size(), 0); + if (payload.second.revents != POLLWRNORM) { + throw std::runtime_error("revents != POLLWRNORM\n"); } + + sendViaSocket(payload.second.fd, res_str.c_str(), res_str.size(), 0); } } }; diff --git a/include/nadjieb/net/topic.hpp b/include/nadjieb/net/topic.hpp new file mode 100644 index 0000000..5db4367 --- /dev/null +++ b/include/nadjieb/net/topic.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include + +#include +#include +#include +#include + +namespace nadjieb { +namespace net { +class Topic { + public: + void setBuffer(const std::string& buffer) { + std::unique_lock lock(buffer_mtx_); + buffer_ = buffer; + } + + std::string getBuffer() { + std::shared_lock lock(buffer_mtx_); + return buffer_; + } + + void addClient(const SocketFD& sockfd) { + std::unique_lock client_lock(client_by_sockfd_mtx_); + client_by_sockfd_[sockfd] = NADJIEB_MJPEG_STREAMER_POLLFD{sockfd, POLLWRNORM, 0}; + + std::unique_lock queue_size_lock(queue_size_by_sockfd__mtx_); + queue_size_by_sockfd_[sockfd] = 0; + } + + void removeClient(const SocketFD& sockfd) { + std::unique_lock lock(client_by_sockfd_mtx_); + client_by_sockfd_.erase(sockfd); + + std::unique_lock queue_size_lock(queue_size_by_sockfd__mtx_); + queue_size_by_sockfd_.erase(sockfd); + } + + bool hasClient() { + std::shared_lock lock(client_by_sockfd_mtx_); + return !client_by_sockfd_.empty(); + } + + std::vector getClients() { + std::shared_lock lock(client_by_sockfd_mtx_); + + std::vector clients; + for (const auto& client : client_by_sockfd_) { + clients.push_back(client.second); + } + + return clients; + } + + int getQueueSize(const SocketFD& sockfd) { + std::shared_lock queue_size_lock(queue_size_by_sockfd__mtx_); + return queue_size_by_sockfd_[sockfd]; + } + + void increaseQueue(const SocketFD& sockfd) { + std::unique_lock queue_size_lock(queue_size_by_sockfd__mtx_); + ++queue_size_by_sockfd_[sockfd]; + } + + void decreaseQueue(const SocketFD& sockfd) { + std::unique_lock queue_size_lock(queue_size_by_sockfd__mtx_); + --queue_size_by_sockfd_[sockfd]; + } + + private: + std::string buffer_; + std::shared_mutex buffer_mtx_; + + std::unordered_map client_by_sockfd_; + std::shared_mutex client_by_sockfd_mtx_; + + std::unordered_map queue_size_by_sockfd_; + std::shared_mutex queue_size_by_sockfd__mtx_; +}; +} // namespace net +} // namespace nadjieb diff --git a/single_include/nadjieb/mjpeg_streamer.hpp b/single_include/nadjieb/mjpeg_streamer.hpp index 56a3ea1..1d079ef 100644 --- a/single_include/nadjieb/mjpeg_streamer.hpp +++ b/single_include/nadjieb/mjpeg_streamer.hpp @@ -567,6 +567,91 @@ class Listener : public nadjieb::utils::NonCopyable, public nadjieb::utils::Runn // #include +// #include + + +// #include + + +#include +#include +#include +#include + +namespace nadjieb { +namespace net { +class Topic { + public: + void setBuffer(const std::string& buffer) { + std::unique_lock lock(buffer_mtx_); + buffer_ = buffer; + } + + std::string getBuffer() { + std::shared_lock lock(buffer_mtx_); + return buffer_; + } + + void addClient(const SocketFD& sockfd) { + std::unique_lock client_lock(client_by_sockfd_mtx_); + client_by_sockfd_[sockfd] = NADJIEB_MJPEG_STREAMER_POLLFD{sockfd, POLLWRNORM, 0}; + + std::unique_lock queue_size_lock(queue_size_by_sockfd__mtx_); + queue_size_by_sockfd_[sockfd] = 0; + } + + void removeClient(const SocketFD& sockfd) { + std::unique_lock lock(client_by_sockfd_mtx_); + client_by_sockfd_.erase(sockfd); + + std::unique_lock queue_size_lock(queue_size_by_sockfd__mtx_); + queue_size_by_sockfd_.erase(sockfd); + } + + bool hasClient() { + std::shared_lock lock(client_by_sockfd_mtx_); + return !client_by_sockfd_.empty(); + } + + std::vector getClients() { + std::shared_lock lock(client_by_sockfd_mtx_); + + std::vector clients; + for (const auto& client : client_by_sockfd_) { + clients.push_back(client.second); + } + + return clients; + } + + int getQueueSize(const SocketFD& sockfd) { + std::shared_lock queue_size_lock(queue_size_by_sockfd__mtx_); + return queue_size_by_sockfd_[sockfd]; + } + + void increaseQueue(const SocketFD& sockfd) { + std::unique_lock queue_size_lock(queue_size_by_sockfd__mtx_); + ++queue_size_by_sockfd_[sockfd]; + } + + void decreaseQueue(const SocketFD& sockfd) { + std::unique_lock queue_size_lock(queue_size_by_sockfd__mtx_); + --queue_size_by_sockfd_[sockfd]; + } + + private: + std::string buffer_; + std::shared_mutex buffer_mtx_; + + std::unordered_map client_by_sockfd_; + std::shared_mutex client_by_sockfd_mtx_; + + std::unordered_map queue_size_by_sockfd_; + std::shared_mutex queue_size_by_sockfd__mtx_; +}; +} // namespace net +} // namespace nadjieb + // #include // #include @@ -588,7 +673,7 @@ class Publisher : public nadjieb::utils::NonCopyable, public nadjieb::utils::Run public: virtual ~Publisher() { stop(); } - void start(int num_workers = 1) { + void start(int num_workers = std::thread::hardware_concurrency()) { state_ = nadjieb::utils::State::BOOTING; end_publisher_ = false; workers_.reserve(num_workers); @@ -612,7 +697,8 @@ class Publisher : public nadjieb::utils::NonCopyable, public nadjieb::utils::Run workers_.clear(); } - path2clients_.clear(); + topics_.clear(); + path_by_client_.clear(); while (!payloads_.empty()) { payloads_.pop(); @@ -620,30 +706,24 @@ class Publisher : public nadjieb::utils::NonCopyable, public nadjieb::utils::Run state_ = nadjieb::utils::State::TERMINATED; } - void add(const std::string& path, const SocketFD& sockfd) { + void add(const SocketFD& sockfd, const std::string& path) { if (end_publisher_) { return; } - const std::lock_guard lock(p2c_mtx_); - path2clients_[path].emplace_back(NADJIEB_MJPEG_STREAMER_POLLFD{sockfd, POLLWRNORM, 0}); + topics_[path].addClient(sockfd); + + std::unique_lock lock(path_by_client_mtx_); + path_by_client_[sockfd] = path; } + bool pathExists(const std::string& path) { return (topics_.find(path) != topics_.end()); } + void removeClient(const SocketFD& sockfd) { - const std::lock_guard lock(p2c_mtx_); - for (auto it = path2clients_.begin(); it != path2clients_.end();) { - it->second.erase( - std::remove_if( - it->second.begin(), it->second.end(), - [&](const NADJIEB_MJPEG_STREAMER_POLLFD& pfd) { return pfd.fd == sockfd; }), - it->second.end()); - - if (it->second.empty()) { - it = path2clients_.erase(it); - } else { - ++it; - } - } + std::unique_lock lock(path_by_client_mtx_); + topics_[path_by_client_[sockfd]].removeClient(sockfd); + + path_by_client_.erase(sockfd); } void enqueue(const std::string& path, const std::string& buffer) { @@ -651,33 +731,39 @@ class Publisher : public nadjieb::utils::NonCopyable, public nadjieb::utils::Run return; } - if (path2clients_.find(path) == path2clients_.end()) { - return; - } + topics_[path].setBuffer(buffer); - std::unique_lock payloads_lock(payloads_mtx_); - payloads_.emplace(path, buffer); - payloads_lock.unlock(); - condition_.notify_one(); - } + for (const auto& client : topics_[path].getClients()) { + if (topics_[path].getQueueSize(client.fd) > LIMIT_QUEUE_PER_CLIENT) { + continue; + } + + std::unique_lock payloads_lock(payloads_mtx_); + payloads_.emplace(path, client); + topics_[path].increaseQueue(client.fd); + payloads_lock.unlock(); - bool hasClient(const std::string& path) { - const std::lock_guard lock(p2c_mtx_); - return path2clients_.find(path) != path2clients_.end() && !path2clients_[path].empty(); + condition_.notify_one(); + } } + bool hasClient(const std::string& path) { return topics_[path].hasClient(); } + private: - typedef std::pair Payload; + typedef std::pair Payload; std::condition_variable condition_; std::vector workers_; std::queue payloads_; - std::unordered_map> path2clients_; + std::unordered_map path_by_client_; + std::unordered_map topics_; std::mutex cv_mtx_; - std::mutex p2c_mtx_; + std::mutex path_by_client_mtx_; std::mutex payloads_mtx_; bool end_publisher_ = true; + const static int LIMIT_QUEUE_PER_CLIENT = 5; + void worker() { while (!end_publisher_) { std::unique_lock cv_lock(cv_mtx_); @@ -691,34 +777,33 @@ class Publisher : public nadjieb::utils::NonCopyable, public nadjieb::utils::Run Payload payload = std::move(payloads_.front()); payloads_.pop(); + topics_[payload.first].decreaseQueue(payload.second.fd); payloads_lock.unlock(); cv_lock.unlock(); + auto buffer = topics_[payload.first].getBuffer(); std::string res_str = "--nadjiebmjpegstreamer\r\n" "Content-Type: image/jpeg\r\n" "Content-Length: " - + std::to_string(payload.second.size()) + "\r\n\r\n" + payload.second; + + std::to_string(buffer.size()) + "\r\n\r\n" + buffer; - const std::lock_guard lock(p2c_mtx_); - for (auto& client : path2clients_[payload.first]) { - auto socket_count = pollSockets(&client, 1, 1); - - if (socket_count == NADJIEB_MJPEG_STREAMER_SOCKET_ERROR) { - throw std::runtime_error("pollSockets() failed\n"); - } + auto socket_count = pollSockets(&payload.second, 1, 1); - if (socket_count == 0) { - continue; - } + if (socket_count == NADJIEB_MJPEG_STREAMER_SOCKET_ERROR) { + throw std::runtime_error("pollSockets() failed\n"); + } - if (client.revents != POLLWRNORM) { - throw std::runtime_error("revents != POLLWRNORM\n"); - } + if (socket_count == 0) { + continue; + } - sendViaSocket(client.fd, res_str.c_str(), res_str.size(), 0); + if (payload.second.revents != POLLWRNORM) { + throw std::runtime_error("revents != POLLWRNORM\n"); } + + sendViaSocket(payload.second.fd, res_str.c_str(), res_str.size(), 0); } } }; @@ -737,7 +822,7 @@ class MJPEGStreamer : public nadjieb::utils::NonCopyable { public: virtual ~MJPEGStreamer() { stop(); } - void start(int port, int num_workers = 1) { + void start(int port, int num_workers = std::thread::hardware_concurrency()) { publisher_.start(num_workers); listener_.withOnMessageCallback(on_message_cb_).withOnBeforeCloseCallback(on_before_close_cb_).runAsync(port); @@ -798,6 +883,19 @@ class MJPEGStreamer : public nadjieb::utils::NonCopyable { return cb_res; } + if (!publisher_.pathExists(req.getTarget())) { + nadjieb::net::HTTPResponse not_found_res; + not_found_res.setVersion(req.getVersion()); + not_found_res.setStatusCode(404); + not_found_res.setStatusText("Not Found"); + auto not_found_res_str = not_found_res.serialize(); + + nadjieb::net::sendViaSocket(sockfd, not_found_res_str.c_str(), not_found_res_str.size(), 0); + + cb_res.close_conn = true; + return cb_res; + } + nadjieb::net::HTTPResponse init_res; init_res.setVersion(req.getVersion()); init_res.setStatusCode(200); @@ -810,7 +908,7 @@ class MJPEGStreamer : public nadjieb::utils::NonCopyable { nadjieb::net::sendViaSocket(sockfd, init_res_str.c_str(), init_res_str.size(), 0); - publisher_.add(req.getTarget(), sockfd); + publisher_.add(sockfd, req.getTarget()); return cb_res; }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 68a1cfb..b72afbc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -33,7 +33,7 @@ add_library(doctest_main OBJECT src/unit.cpp) if (${CMAKE_VERSION} VERSION_LESS "3.8.0") target_compile_features(doctest_main PUBLIC cxx_range_for) else() - target_compile_features(doctest_main PUBLIC cxx_std_11) + target_compile_features(doctest_main PUBLIC cxx_std_17) endif() target_include_directories(doctest_main PRIVATE include) diff --git a/test/cmake_add_subdirectory/project/CMakeLists.txt b/test/cmake_add_subdirectory/project/CMakeLists.txt index ef9d973..5a865f8 100644 --- a/test/cmake_add_subdirectory/project/CMakeLists.txt +++ b/test/cmake_add_subdirectory/project/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.8) project(DummyImport CXX) diff --git a/test/cmake_import/project/CMakeLists.txt b/test/cmake_import/project/CMakeLists.txt index 4ab6595..beb4677 100644 --- a/test/cmake_import/project/CMakeLists.txt +++ b/test/cmake_import/project/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.8) project(DummyImport CXX) diff --git a/test/cmake_import_minver/project/CMakeLists.txt b/test/cmake_import_minver/project/CMakeLists.txt index eaa65d3..95e5453 100644 --- a/test/cmake_import_minver/project/CMakeLists.txt +++ b/test/cmake_import_minver/project/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.8) project(DummyImportMinVer CXX) diff --git a/test/cmake_target_include_directories/project/CMakeLists.txt b/test/cmake_target_include_directories/project/CMakeLists.txt index 4e238c1..dce539f 100644 --- a/test/cmake_target_include_directories/project/CMakeLists.txt +++ b/test/cmake_target_include_directories/project/CMakeLists.txt @@ -1,11 +1,11 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.8) project(DummyImport CXX) add_executable(with_private_target main.cpp) target_include_directories(with_private_target PRIVATE ${nadjieb_mjpeg_streamer_source}/include) -set_target_properties(with_private_target PROPERTIES CXX_STANDARD 11) +set_target_properties(with_private_target PROPERTIES CXX_STANDARD 17) add_executable(with_private_system_target main.cpp) target_include_directories(with_private_system_target PRIVATE SYSTEM ${nadjieb_mjpeg_streamer_source}/include) -set_target_properties(with_private_system_target PROPERTIES CXX_STANDARD 11) +set_target_properties(with_private_system_target PROPERTIES CXX_STANDARD 17) diff --git a/test/src/unit-streamer.cpp b/test/src/unit-streamer.cpp index 27a11cd..b46c1d8 100644 --- a/test/src/unit-streamer.cpp +++ b/test/src/unit-streamer.cpp @@ -32,7 +32,9 @@ TEST_SUITE("streamer") { } TEST_CASE("publish image stream") { - GIVEN("A client ready to receive image streams") { + GIVEN("The streamer streams buffers") { + const std::string buffer1 = "buffer1"; + const std::string buffer2 = "buffer2"; std::string received_buffer1; std::string received_buffer2; bool ready = false; @@ -41,6 +43,15 @@ TEST_SUITE("streamer") { streamer.start(1235); auto task = std::async(std::launch::async, [&]() { + while (streamer.isRunning()) { + streamer.publish("/buffer1", buffer1); + streamer.publish("/buffer2", buffer2); + ready = true; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }); + + WHEN("A client ready to receive image streams") { const std::string delimiter = "\r\n\r\n"; httplib::Client cli("localhost", 1235); bool stop1 = false; @@ -69,19 +80,6 @@ TEST_SUITE("streamer") { } streamer.stop(); - }); - - WHEN("The streamer streams buffers") { - const std::string buffer1 = "buffer1"; - const std::string buffer2 = "buffer2"; - - ready = true; - - while (streamer.isRunning()) { - streamer.publish("/buffer1", buffer1); - streamer.publish("/buffer2", buffer2); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } task.wait(); @@ -135,6 +133,23 @@ TEST_SUITE("streamer") { } } + TEST_CASE("Not Found") { + GIVEN("A streamer initialize") { + nadjieb::MJPEGStreamer streamer; + streamer.start(1240); + + CHECK(streamer.isRunning() == true); + + WHEN("Client request a non exist path") { + httplib::Client cli("localhost", 1240); + + auto res = cli.Get("/foo"); + + THEN("Connection closed") { CHECK(res->status == 404); } + } + } + } + TEST_CASE("Client disconnect when streamer publish buffer") { WHEN("A client request image stream and disconnect it") { nadjieb::MJPEGStreamer streamer;