diff --git a/CMakeLists.txt b/CMakeLists.txt index d2f92d1..ea6712c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${BUILD_DIR}/bin/release) option(WEBCC_ENABLE_AUTOTEST "Build automation test?" OFF) option(WEBCC_ENABLE_UNITTEST "Build unit test?" OFF) option(WEBCC_ENABLE_EXAMPLES "Build examples?" ON) +option(WEBCC_ENABLE_QT_EXAMPLES "Build Qt application examples?" OFF) set(WEBCC_ENABLE_LOG 1 CACHE STRING "Enable logging? (1:Yes, 0:No)") set(WEBCC_ENABLE_SSL 0 CACHE STRING "Enable SSL/HTTPS (need OpenSSL)? (1:Yes, 0:No)") diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d2e7c85..342b81b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -40,10 +40,14 @@ target_link_libraries(form_urlencoded_client ${EXAMPLE_LIBS}) add_executable(form_server form_server.cc) target_link_libraries(form_server ${EXAMPLE_LIBS}) -add_subdirectory(book_server) -add_subdirectory(book_client) - if(WIN32) add_executable(url_unicode url_unicode.cc encoding.cc encoding.h) target_link_libraries(url_unicode ${EXAMPLE_LIBS}) endif() + +add_subdirectory(book_server) +add_subdirectory(book_client) + +if(WEBCC_ENABLE_QT_EXAMPLES) + add_subdirectory(qt_app_server) +endif() diff --git a/examples/qt_app_server/CMakeLists.txt b/examples/qt_app_server/CMakeLists.txt new file mode 100644 index 0000000..f56cf9d --- /dev/null +++ b/examples/qt_app_server/CMakeLists.txt @@ -0,0 +1,11 @@ +project(qt_app_server) + +find_package(Qt5 CONFIG REQUIRED Core Gui Widgets) + +set(CMAKE_AUTOMOC ON) + +file(GLOB SRCS *.cpp *.h) + +add_executable(qt_app_server ${SRCS}) + +target_link_libraries(qt_app_server Qt5::Core Qt5::Gui Qt5::Widgets ${EXAMPLE_LIBS}) diff --git a/examples/qt_app_server/main.cpp b/examples/qt_app_server/main.cpp new file mode 100644 index 0000000..cfa1e72 --- /dev/null +++ b/examples/qt_app_server/main.cpp @@ -0,0 +1,56 @@ +#include + +#include + +#include "examples/qt_app_server/main_window.h" + +#include "webcc/logger.h" +#include "webcc/response_builder.h" +#include "webcc/server.h" +#include "webcc/view.h" + +class ApiView : public webcc::View { +public: + explicit ApiView(Context* context) : context_(context) { + } + + webcc::ResponsePtr Handle(webcc::RequestPtr request) override { + if (request->method() == "GET") { + emit context_->SomeApiSignal("A GET request has been handled"); + return webcc::ResponseBuilder{}.OK().Body("Hello, World!")(); + } + + return {}; + } + +private: + Context* context_; +}; + +int main(int argc, char** argv) { + QApplication app{ argc, argv }; + + WEBCC_LOG_INIT("", webcc::LOG_CONSOLE); + + Context context; + + // Create a server and bind view. + webcc::Server server{ boost::asio::ip::tcp::v4(), 8000 }; + server.Route("/", std::make_shared(&context)); + + // Run the server in a thread to avoid blocking the GUI. + std::thread server_thread{ [&server]() { server.Run(); } }; + + // Now show our Qt main window and execute the application loop. + MainWindow main_window{ &context }; + main_window.resize(400, 300); + main_window.showNormal(); + + int code = app.exec(); + + // Stop the server and wait for it to finish. + server.Stop(); + server_thread.join(); + + return code; +} diff --git a/examples/qt_app_server/main_window.cpp b/examples/qt_app_server/main_window.cpp new file mode 100644 index 0000000..0136ce9 --- /dev/null +++ b/examples/qt_app_server/main_window.cpp @@ -0,0 +1,21 @@ +#include "examples/qt_app_server/main_window.h" + +#include + +MainWindow::MainWindow(Context* context, QWidget* parent) + : QMainWindow(parent) { + setWindowTitle("Qt App Server"); + +#if 1 + connect(context, &Context::SomeApiSignal, this, &MainWindow::OnSomeApiSignal); +#else + // NOTE: Seems not necessary to use QueuedConnection (because we are using + // std::thread instead of QThread?). + connect(context, &Context::SomeApiSignal, this, &MainWindow::OnSomeApiSignal, + Qt::QueuedConnection); +#endif +} + +void MainWindow::OnSomeApiSignal(const QString& text) { + qDebug() << text; +} diff --git a/examples/qt_app_server/main_window.h b/examples/qt_app_server/main_window.h new file mode 100644 index 0000000..a02b210 --- /dev/null +++ b/examples/qt_app_server/main_window.h @@ -0,0 +1,34 @@ +#ifndef MAIN_WINDOW_H_ +#define MAIN_WINDOW_H_ + +#include + +// Some global QObject with public signals for ApiView to inform the handling +// of client requests. +// "Context" is a bad name, you should change it. +class Context : public QObject { + Q_OBJECT + +public: + explicit Context(QObject* parent = nullptr) : QObject(parent) { + } + + // Public signals emitted from ApiView: +signals: + void SomeApiSignal(const QString& text); + // Add other signals here... +}; + +class MainWindow : public QMainWindow { + Q_OBJECT + +public: + explicit MainWindow(Context* context, QWidget* parent = nullptr); + +private: + // You should encapsulate the required information as the signal parameters. + // I use QString just for demostrating how to transfer data from ApiView. + void OnSomeApiSignal(const QString& text); +}; + +#endif // MAIN_WINDOW_H_ diff --git a/webcc/request_parser.cc b/webcc/request_parser.cc index 3148a29..9bf30d4 100644 --- a/webcc/request_parser.cc +++ b/webcc/request_parser.cc @@ -118,6 +118,7 @@ bool RequestParser::ParseMultipartContent(const char* data, } // Next boundary found. + LOG_INFO("Next boundary found, off=%u", off); // This part has ended. if (off >= 2) { @@ -128,7 +129,7 @@ bool RequestParser::ParseMultipartContent(const char* data, // +2 for including the CRLF after the boundary. pending_data_.erase(0, off + count + 2); } else { - LOG_ERRO("Invalid part data."); + LOG_ERRO("Invalid part data. off=%u", off); return false; }