xcbosa-itx 2 жил өмнө
parent
commit
57b6318286

+ 2 - 1
CMakeLists.txt

@@ -8,8 +8,9 @@ aux_source_directory(. DIR_SRCS)
 add_subdirectory(utils)
 add_subdirectory(httpserver)
 add_subdirectory(processor)
+add_subdirectory(controller)
 
 add_executable(FRPCWebUI ${DIR_SRCS})
 
 find_package(Threads)
-target_link_libraries(FRPCWebUI HttpServerUtils HttpServer ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries(FRPCWebUI HttpServerUtils HttpServer Processor Controller ${CMAKE_THREAD_LIBS_INIT})

+ 5 - 0
controller/CMakeLists.txt

@@ -0,0 +1,5 @@
+set(CMAKE_CXX_STANDARD 17)
+
+aux_source_directory(. DIR_SRC)
+
+add_library(Controller ${DIR_SRC})

+ 25 - 0
controller/StaticWebPageController.cpp

@@ -0,0 +1,25 @@
+//
+// Created by xcbosa on 2023/1/28.
+//
+
+#include "../processor/processor.h"
+#include "../utils/utils.h"
+#include <sys/stat.h>
+
+using namespace std;
+using namespace xc::processor;
+
+namespace xc::controller {
+
+    static ContentGenerator staticWebPageController([] (auto p) {
+        if (p.getMethod() != "GET") { return false; }
+        struct stat buffer;
+        string filePath = "html/" + p.getURL();
+        return stat(filePath.c_str(), &buffer) == 0;
+    }, [] (auto p) {
+        string filePath = "html/" + p.getURL();
+        return new BinaryResponseData(200, filePath, "");
+    });
+
+}
+

+ 11 - 5
httpserver/ClientConnection.cpp

@@ -103,14 +103,19 @@ namespace xc {
 
                 RequestData requestData(url, method, headers, body.str());
                 cout << "[HTTPServer] Received " << method << " Request URL = " << url << endl;
-                RequestProcessTask task(requestData);
-                processor::enqueueTask(&task);
+                if (url.find("..") != string::npos) {
+                    conf::errorPage400.writeTo(clWrite);
+                    cleanUpAndDestroy();
+                    return;
+                }
+                RequestProcessTask *task = new RequestProcessTask(requestData);
+                processor::enqueueTask(task);
                 ::time_t start, now;
                 ::time(&start);
                 while (true) {
                     usleep(1000 * 10);
-                    if (task.isFinish()) {
-                        ResponseData *taskResponse = task.getResponse();
+                    if (task->isFinish()) {
+                        ResponseData *taskResponse = task->getResponse();
                         taskResponse->writeTo(clWrite);
                         delete taskResponse;
                         break;
@@ -119,7 +124,8 @@ namespace xc {
                     if (::difftime(now, start) > conf::taskProcessTimeoutSeconds) {
                         cout << "[HTTPServer-Warn] Task failed because time out" << endl;
                         conf::errorPageTimeout.writeTo(clWrite);
-                        processor::deleteTask(&task);
+                        processor::deleteTask(task);
+                        task->setHttpDiscarded(true);
                         break;
                     }
                 }

+ 6 - 0
main.cpp

@@ -3,8 +3,10 @@
 
 #include "httpserver/http-server.h"
 #include "utils/utils.h"
+#include "processor/processor.h"
 
 using namespace xc::httpserver;
+using namespace xc::processor;
 
 int main() {
     std::cout << "Hello, World!" << std::endl;
@@ -13,6 +15,10 @@ int main() {
         server.serverLoop();
     });
     serverThread.detach();
+    RequestProcessWorker worker;
+    thread workerThread([&worker] {
+        worker.workerLoop();
+    });
     while (true) ;
     return 0;
 }

+ 25 - 0
processor/ContentGenerator.cpp

@@ -0,0 +1,25 @@
+//
+// Created by xcbosa on 2023/1/28.
+//
+
+#include "ContentGenerator.h"
+
+namespace xc {
+    namespace processor {
+        extern vector<ContentGenerator *> generators;
+
+        ContentGenerator::ContentGenerator(RequestCheckBlock checker, ContentGenerateBlock generator) {
+            this->checker = checker;
+            this->generator = generator;
+            generators.push_back(this);
+        }
+
+        bool ContentGenerator::matchRequest(RequestData request) {
+            return this->checker(request);
+        }
+
+        ResponseData *ContentGenerator::generateResponse(RequestData request) {
+            return this->generator(request);
+        }
+    }
+}

+ 33 - 0
processor/ContentGenerator.h

@@ -0,0 +1,33 @@
+//
+// Created by xcbosa on 2023/1/28.
+//
+
+#pragma once
+
+#include "processor-private.h"
+#include "../utils/utils.h"
+
+using namespace std;
+
+namespace xc {
+    namespace processor {
+
+        /*输入一个请求,判断此生成器是否响应此请求*/
+        typedef function<bool (RequestData)> RequestCheckBlock;
+
+        /*为此请求生成响应,响应需要使用new,在发送完成或HTTP服务器不再需要时自动调用delete销毁*/
+        typedef function<ResponseData* (RequestData)> ContentGenerateBlock;
+
+        /*请定义为static*/
+        class ContentGenerator {
+        public:
+            ContentGenerator(RequestCheckBlock checker, ContentGenerateBlock generator);
+            bool matchRequest(RequestData request);
+            ResponseData *generateResponse(RequestData request);
+        private:
+            RequestCheckBlock checker;
+            ContentGenerateBlock generator;
+        };
+
+    }
+}

+ 19 - 2
processor/RequestProcessTask.cpp

@@ -9,20 +9,37 @@ namespace xc {
         RequestProcessTask::RequestProcessTask(RequestData requestData): request(requestData) {
             this->response = nullptr;
             this->finish = false;
+            this->httpDiscarded = false;
         }
 
         void RequestProcessTask::processFinish(ResponseData *responseData) {
             this->response = responseData;
             this->finish = true;
-            // Todo-Fix: 当Task已脱离
+            if (this->httpDiscarded) {
+                delete responseData;
+                this->response = nullptr;
+                //delete this; // Todo: Memory Lake
+            }
+        }
+
+        bool RequestProcessTask::isHttpDiscarded() {
+            return this->httpDiscarded;
+        }
+
+        void RequestProcessTask::setHttpDiscarded(bool value) {
+            this->httpDiscarded = value;
         }
 
         bool RequestProcessTask::isFinish() {
-            return this->isFinish();
+            return this->finish;
         }
 
         ResponseData *RequestProcessTask::getResponse() {
             return this->response;
         }
+
+        RequestData RequestProcessTask::getRequest() const {
+            return this->request;
+        }
     } // xc
 } // processor

+ 7 - 0
processor/RequestProcessTask.h

@@ -12,11 +12,18 @@ namespace xc {
         class RequestProcessTask {
         public:
             RequestProcessTask(RequestData requestData);
+
+            /*仅当Controller处理完成后调用,如果HTTPServer已经丢弃了这个任务,则delete自己*/
             void processFinish(ResponseData *responseData);
+
             bool isFinish();
+            RequestData getRequest() const;
             ResponseData *getResponse();
+            bool isHttpDiscarded();
+            void setHttpDiscarded(bool value);
         private:
             bool finish;
+            bool httpDiscarded;
             RequestData request;
             ResponseData *response; // 由处理程序创建,由 ClientConnection 在发送后使用 delete 释放
         };

+ 34 - 0
processor/RequestProcessWorker.cpp

@@ -0,0 +1,34 @@
+//
+// Created by xcbosa on 2023/1/28.
+//
+
+#include "../webuiconf.h"
+#include "RequestProcessWorker.h"
+#include "processor.h"
+
+namespace xc {
+    namespace processor {
+
+        vector<ContentGenerator *> generators;
+
+        void RequestProcessWorker::workerLoop() {
+            while (true) {
+                RequestProcessTask task = processor::dequeueTaskSync();
+                ContentGenerator *generator = nullptr;
+                for (auto it : generators) {
+                    if (it->matchRequest(task.getRequest())) {
+                        generator = it;
+                    }
+                }
+                ResponseData *resp = nullptr;
+                if (generator == nullptr) {
+                    resp = new FileResponseData(conf::errorPage404);
+                } else {
+                    resp = generator->generateResponse(task.getRequest());
+                }
+                task.processFinish(resp);
+            }
+        }
+
+    } // xc
+} // processor

+ 18 - 0
processor/RequestProcessWorker.h

@@ -0,0 +1,18 @@
+//
+// Created by xcbosa on 2023/1/28.
+//
+
+#pragma once
+#include "processor-private.h"
+#include "ContentGenerator.h"
+
+namespace xc {
+    namespace processor {
+
+        class RequestProcessWorker {
+        public:
+            void workerLoop();
+        };
+
+    } // xc
+} // processor

+ 2 - 0
processor/processor.h

@@ -6,3 +6,5 @@
 
 #include "RequestProcessorManager.h"
 #include "RequestProcessTask.h"
+#include "ContentGenerator.h"
+#include "RequestProcessWorker.h"

+ 2 - 2
utils/BinaryResponseData.cpp

@@ -17,7 +17,7 @@ namespace xc {
             this->filePath = nullptr;
         }
 
-        BinaryResponseData::BinaryResponseData(int statusCode, char *filePath, string contentType): headers() {
+        BinaryResponseData::BinaryResponseData(int statusCode, string filePath, string contentType): headers() {
             this->statusCode = statusCode;
             this->body = nullptr;
             this->bodySize = 0;
@@ -68,7 +68,7 @@ namespace xc {
         }
 
         bool BinaryResponseData::isWriteFromFile() const {
-            return this->filePath != nullptr;
+            return !this->isWriteFromMemory();
         }
 
         bool BinaryResponseData::isWriteFromMemory() const {

+ 2 - 2
utils/BinaryResponseData.h

@@ -13,7 +13,7 @@ namespace xc {
         class BinaryResponseData: public ResponseData {
         public:
             BinaryResponseData(int statusCode, uint8_t *body, int bodySize, string contentType);
-            BinaryResponseData(int statusCode, char *filePath, string contentType);
+            BinaryResponseData(int statusCode, string filePath, string contentType);
             void writeTo(::FILE *fp) const;
             void setHeader(string headerName, string value);
             bool isWriteFromFile() const;
@@ -22,7 +22,7 @@ namespace xc {
             int statusCode;
             map<string, string> headers;
             uint8_t *body;
-            char *filePath;
+            string filePath;
             int bodySize;
         };
 

+ 1 - 1
webui.h

@@ -15,7 +15,7 @@
 using namespace std;
 
 #define assertx(expr, message) \
-    if (!(expr)) { std::cerr << message << std::endl; exit(-1); }
+    if (!(expr)) { std::cerr << (message) << std::endl; exit(-1); }
 
 namespace xc {
     constexpr int urlRequestBuffSize = 65536;

+ 2 - 2
webuiconf.h

@@ -47,7 +47,7 @@ namespace xc::conf {
     });
 
     const auto errorPageTimeout = errorPage.applyReplacements(550, {
-            Replacement("errorMessage", "服务器任务处理已超时,可能服务器访问量过大,请稍后重试"),
-            Replacement("errorCode", "550")
+        Replacement("errorMessage", "服务器任务处理已超时,可能服务器访问量过大,请稍后重试"),
+        Replacement("errorCode", "550")
     });
 }