Просмотр исходного кода

Add HTTP1.1 chunked transfer method

xcbosa-itx 2 лет назад
Родитель
Сommit
9918f181c8

+ 3 - 3
CMakeLists.txt

@@ -4,13 +4,13 @@ project(FRPCWebUI)
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD 17)
 
 
 aux_source_directory(. DIR_SRCS)
 aux_source_directory(. DIR_SRCS)
+aux_source_directory(controller DIR_CONTROLLERS)
 
 
 add_subdirectory(utils)
 add_subdirectory(utils)
 add_subdirectory(httpserver)
 add_subdirectory(httpserver)
 add_subdirectory(processor)
 add_subdirectory(processor)
-add_subdirectory(controller)
 
 
-add_executable(FRPCWebUI ${DIR_SRCS})
+add_executable(FRPCWebUI ${DIR_SRCS} ${DIR_CONTROLLERS})
 
 
 find_package(Threads)
 find_package(Threads)
-target_link_libraries(FRPCWebUI HttpServerUtils HttpServer Processor Controller ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries(FRPCWebUI PRIVATE HttpServerUtils HttpServer Processor ${CMAKE_THREAD_LIBS_INIT})

+ 0 - 5
controller/CMakeLists.txt

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

+ 69 - 1
controller/StaticWebPageController.cpp

@@ -3,7 +3,7 @@
 //
 //
 
 
 #include "../processor/processor.h"
 #include "../processor/processor.h"
-#include "../utils/utils.h"
+#include "../webuiconf.h"
 #include <sys/stat.h>
 #include <sys/stat.h>
 
 
 using namespace std;
 using namespace std;
@@ -11,7 +11,75 @@ using namespace xc::processor;
 
 
 namespace xc::controller {
 namespace xc::controller {
 
 
+    ContentGeneratorDefine({
+                               if (request.getMethod() != "GET") { return false; }
+                               if (request.getURL().length() == 0) { return false; }
+                               struct stat buffer;
+                               string filePath = "html";
+                               if (request.getURL()[0] == '/') {
+                                   filePath += request.getURL();
+                               } else {
+                                   filePath += "/" + request.getURL();
+                               }
+                               if (stat(filePath.c_str(), &buffer) == 0) {
+                                   if (S_ISREG(buffer.st_mode)) {
+                                       return true;
+                                   } else {
+                                       for (auto file: conf::defaultFiles) {
+                                           string newFilePath = filePath;
+                                           if (filePath[filePath.length() - 1] == '/') {
+                                               newFilePath += file;
+                                           } else {
+                                               newFilePath += "/" + file;
+                                           }
+                                           if (stat(newFilePath.c_str(), &buffer) == 0) {
+                                               return S_ISREG(buffer.st_mode);
+                                           }
+                                       }
+                                   }
+                               }
+                               return false;
+                           }, {
+                               struct stat buffer;
+                               string filePath = "html";
+                               if (request.getURL()[0] == '/') {
+                                   filePath += request.getURL();
+                               } else {
+                                   filePath += "/" + request.getURL();
+                               }
+                               if (stat(filePath.c_str(), &buffer) == 0) {
+                                   if (S_ISREG(buffer.st_mode)) {
+                                       return (ResponseData *) new BinaryResponseData(200, filePath,
+                                                                                      mimeTypeOfFile(filePath));
+                                   } else {
+                                       for (auto file: conf::defaultFiles) {
+                                           string newFilePath = filePath;
+                                           if (filePath[filePath.length() - 1] == '/') {
+                                               newFilePath += file;
+                                           } else {
+                                               newFilePath += "/" + file;
+                                           }
+                                           if (stat(newFilePath.c_str(), &buffer) == 0) {
+                                               if (S_ISREG(buffer.st_mode)) {
+                                                   return (ResponseData *) new BinaryResponseData(200, newFilePath,
+                                                                                                  mimeTypeOfFile(
+                                                                                                          newFilePath));
+                                               }
+                                           }
+                                       }
+                                   }
+                               }
 
 
+                               return (ResponseData *) new FileResponseData(conf::errorPage404);
+                           })
+
+    ContentGeneratorDefine(return request.getURL() == "/test1", {
+        return new TextResponseData(200, "test1 controller response");
+    })
+
+    ContentGeneratorDefine(return request.getURL() == "/test2", {
+        return new TextResponseData(200, "test2 controller response");
+    })
 
 
 }
 }
 
 

+ 10 - 0
data/html/index.html

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Title</title>
+</head>
+<body>
+sadasd
+</body>
+</html>

+ 1 - 0
httpserver/ClientConnection.cpp

@@ -135,6 +135,7 @@ namespace xc {
                     if (task->isFinish()) {
                     if (task->isFinish()) {
                         ResponseData *taskResponse = task->getResponse();
                         ResponseData *taskResponse = task->getResponse();
                         taskResponse->writeTo(clWrite);
                         taskResponse->writeTo(clWrite);
+//                        taskResponse->writeTo(stdout);
                         delete taskResponse;
                         delete taskResponse;
                         break;
                         break;
                     }
                     }

+ 3 - 1
httpserver/HTTPServer.cpp

@@ -24,7 +24,9 @@ namespace xc {
             bzero(&(serverAddress.sin_zero), 8);
             bzero(&(serverAddress.sin_zero), 8);
             if (::bind(this->serverSocketFd, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr)) == -1) {
             if (::bind(this->serverSocketFd, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr)) == -1) {
                 cerr << "Unable to bind port " << this->bindPort << endl;
                 cerr << "Unable to bind port " << this->bindPort << endl;
-                assertx(0, "");
+                this->bindPort++;
+                this->serverLoop();
+                return;
             }
             }
             if (listen(this->serverSocketFd, 5) == -1) {
             if (listen(this->serverSocketFd, 5) == -1) {
                 cerr << "Unable to bind port " << this->bindPort << endl;
                 cerr << "Unable to bind port " << this->bindPort << endl;

+ 1 - 18
main.cpp

@@ -5,27 +5,10 @@
 #include "utils/utils.h"
 #include "utils/utils.h"
 #include "processor/processor.h"
 #include "processor/processor.h"
 
 
+using namespace xc;
 using namespace xc::httpserver;
 using namespace xc::httpserver;
 using namespace xc::processor;
 using namespace xc::processor;
 
 
-ContentGeneratorDefine({
-    if (request.getMethod() != "GET") { return false; }
-    struct stat buffer;
-    string filePath = "html/" + request.getURL();
-    return stat(filePath.c_str(), &buffer) == 0;
-}, {
-    string filePath = "html" + request.getURL();
-    return new BinaryResponseData(200, filePath, mimeTypeOfFile(filePath));
-})
-
-ContentGeneratorDefine(return request.getURL() == "/test1", {
-    return new TextResponseData(200, "test1 controller response");
-})
-
-ContentGeneratorDefine(return request.getURL() == "/test2", {
-    return new TextResponseData(200, "test2 controller response");
-})
-
 int main() {
 int main() {
     std::cout << "Hello, World!" << std::endl;
     std::cout << "Hello, World!" << std::endl;
     HTTPServer server(8192);
     HTTPServer server(8192);

+ 13 - 1
processor/ContentGenerator.cpp

@@ -2,6 +2,7 @@
 // Created by xcbosa on 2023/1/28.
 // Created by xcbosa on 2023/1/28.
 //
 //
 
 
+#include "../webui.h"
 #include "ContentGenerator.h"
 #include "ContentGenerator.h"
 
 
 namespace xc {
 namespace xc {
@@ -9,10 +10,17 @@ namespace xc {
         ContentGenerator *generators[1024];
         ContentGenerator *generators[1024];
         int generatorsCnt = 0;
         int generatorsCnt = 0;
 
 
-        ContentGenerator::ContentGenerator(RequestCheckBlock checker, ContentGenerateBlock generator) {
+        ContentGenerator::ContentGenerator(string name, RequestCheckBlock checker, ContentGenerateBlock generator) {
             this->checker = checker;
             this->checker = checker;
             this->generator = generator;
             this->generator = generator;
+            this->name = name;
+            int maxCnt = sizeof(generators) / sizeof(ContentGenerator *);
+            ostringstream oss;
+            oss << "ContentGenerator count must be less than ";
+            oss << maxCnt;
+            assertx(generatorsCnt < maxCnt, oss.str());
             generators[generatorsCnt++] = this;
             generators[generatorsCnt++] = this;
+            cout << "[ContentGenerator] Registered " << name << endl;
         }
         }
 
 
         bool ContentGenerator::matchRequest(RequestData request) {
         bool ContentGenerator::matchRequest(RequestData request) {
@@ -22,5 +30,9 @@ namespace xc {
         ResponseData *ContentGenerator::generateResponse(RequestData request) {
         ResponseData *ContentGenerator::generateResponse(RequestData request) {
             return this->generator(request);
             return this->generator(request);
         }
         }
+
+        string ContentGenerator::getName() {
+            return this->name;
+        }
     }
     }
 }
 }

+ 6 - 3
processor/ContentGenerator.h

@@ -10,9 +10,10 @@
 #define __merge_body(a, b) a ## b
 #define __merge_body(a, b) a ## b
 #define __merge(a, b) __merge_body(a, b)
 #define __merge(a, b) __merge_body(a, b)
 #define __uniqueVarName(name) __merge(name, __LINE__)
 #define __uniqueVarName(name) __merge(name, __LINE__)
+#define __string_fic(a) #a
+#define __string_fic_r(a) __string_fic(a)
 
 
-#define ContentGeneratorDefine(cond, eval) \
-const ContentGenerator __uniqueVarName(AutoContentGenerator)([] (auto request) { cond; }, [] (auto request) { eval; });
+#define ContentGeneratorDefine(cond, eval) const static ContentGenerator __uniqueVarName(AutoContentGenerator_Line_)(__string_fic_r(__uniqueVarName(AutoRegistered) __FILE_NAME__), [] (auto request) { cond; }, [] (auto request) { eval; });
 
 
 using namespace std;
 using namespace std;
 
 
@@ -28,10 +29,12 @@ namespace xc {
         /*请定义为static*/
         /*请定义为static*/
         class ContentGenerator {
         class ContentGenerator {
         public:
         public:
-            ContentGenerator(RequestCheckBlock checker, ContentGenerateBlock generator);
+            ContentGenerator(string name, RequestCheckBlock checker, ContentGenerateBlock generator);
             bool matchRequest(RequestData request);
             bool matchRequest(RequestData request);
             ResponseData *generateResponse(RequestData request);
             ResponseData *generateResponse(RequestData request);
+            string getName();
         private:
         private:
+            string name;
             RequestCheckBlock checker;
             RequestCheckBlock checker;
             ContentGenerateBlock generator;
             ContentGenerateBlock generator;
         };
         };

+ 11 - 3
utils/BinaryResponseData.cpp

@@ -14,6 +14,7 @@ namespace xc {
             this->headers["Server"] = "XCHttpServer";
             this->headers["Server"] = "XCHttpServer";
             this->headers["Transfer-Encoding"] = "chunked";
             this->headers["Transfer-Encoding"] = "chunked";
             this->headers["Content-Type"] = contentType;
             this->headers["Content-Type"] = contentType;
+            this->headers["keepalive"] = "false";
             this->filePath = nullptr;
             this->filePath = nullptr;
         }
         }
 
 
@@ -24,6 +25,7 @@ namespace xc {
             this->headers["Server"] = "XCHttpServer";
             this->headers["Server"] = "XCHttpServer";
             this->headers["Transfer-Encoding"] = "chunked";
             this->headers["Transfer-Encoding"] = "chunked";
             this->headers["Content-Type"] = contentType;
             this->headers["Content-Type"] = contentType;
+            this->headers["keepalive"] = "false";
             this->filePath = filePath;
             this->filePath = filePath;
         }
         }
 
 
@@ -32,7 +34,7 @@ namespace xc {
         }
         }
 
 
         void BinaryResponseData::writeTo(::FILE *fp) const {
         void BinaryResponseData::writeTo(::FILE *fp) const {
-            ::fprintf(fp, "HTTP/1.0 %d FRPCWebUI\r\n", this->statusCode);
+            ::fprintf(fp, "HTTP/1.1 %d FRPCWebUI\r\n", this->statusCode);
             for (auto item : this->headers) {
             for (auto item : this->headers) {
                 ::fprintf(fp, "%s: %s\r\n", item.first.c_str(), item.second.c_str());
                 ::fprintf(fp, "%s: %s\r\n", item.first.c_str(), item.second.c_str());
             }
             }
@@ -47,19 +49,25 @@ namespace xc {
                 ::uint8_t *endNextCursor = this->body + this->bodySize;
                 ::uint8_t *endNextCursor = this->body + this->bodySize;
                 for (int i = 0; i < writeTimes; i++) {
                 for (int i = 0; i < writeTimes; i++) {
                     long writeSize = min((long)mtu, endNextCursor - cursor);
                     long writeSize = min((long)mtu, endNextCursor - cursor);
-                    ::fwrite(cursor, writeSize, mtu, fp);
+                    ::fprintf(fp, "%x\r\n", writeSize);
+                    ::fwrite(cursor, 1, writeSize, fp);
+                    ::fprintf(fp, "\r\n");
                     cursor += writeSize;
                     cursor += writeSize;
                 }
                 }
+                ::fprintf(fp, "0\r\n\r\n");
             }
             }
             if (this->isWriteFromFile()) {
             if (this->isWriteFromFile()) {
                 string filePath = this->filePath;
                 string filePath = this->filePath;
                 ::FILE *inputFile = ::fopen(filePath.c_str(), "rb");
                 ::FILE *inputFile = ::fopen(filePath.c_str(), "rb");
                 if (inputFile) {
                 if (inputFile) {
                     ::uint8_t buff[mtu];
                     ::uint8_t buff[mtu];
-                    long readPerPack = 0;
+                    long readPerPack;
                     while ((readPerPack = ::fread(buff, 1, mtu, inputFile)) > 0) {
                     while ((readPerPack = ::fread(buff, 1, mtu, inputFile)) > 0) {
+                        ::fprintf(fp, "%x\r\n", readPerPack);
                         ::fwrite(buff, 1, readPerPack, fp);
                         ::fwrite(buff, 1, readPerPack, fp);
+                        ::fprintf(fp, "\r\n");
                     }
                     }
+                    ::fprintf(fp, "0\r\n\r\n");
                 } else {
                 } else {
                     cerr << "[FileIOError]: " << ::strerror(errno) << endl;
                     cerr << "[FileIOError]: " << ::strerror(errno) << endl;
                 }
                 }

+ 21 - 2
utils/TextResponseData.cpp

@@ -3,6 +3,7 @@
 //
 //
 
 
 #include "TextResponseData.h"
 #include "TextResponseData.h"
+#include "../webuiconf.h"
 
 
 using namespace std;
 using namespace std;
 
 
@@ -14,6 +15,7 @@ namespace xc {
             this->headers["Server"] = "XCHttpServer";
             this->headers["Server"] = "XCHttpServer";
             this->headers["Transfer-Encoding"] = "chunked";
             this->headers["Transfer-Encoding"] = "chunked";
             this->headers["Content-Type"] = "text/html";
             this->headers["Content-Type"] = "text/html";
+            this->headers["keepalive"] = "false";
         }
         }
 
 
         TextResponseData::TextResponseData(int statusCode, string body, string contentType):
         TextResponseData::TextResponseData(int statusCode, string body, string contentType):
@@ -54,12 +56,29 @@ namespace xc {
         }
         }
 
 
         void TextResponseData::writeTo(::FILE *fp) const {
         void TextResponseData::writeTo(::FILE *fp) const {
-            ::fprintf(fp, "HTTP/1.0 %d FRPCWebUI\r\n", this->statusCode);
+            ::fprintf(fp, "HTTP/1.1 %d FRPCWebUI\r\n", this->statusCode);
             for (auto item : this->headers) {
             for (auto item : this->headers) {
                 ::fprintf(fp, "%s: %s\r\n", item.first.c_str(), item.second.c_str());
                 ::fprintf(fp, "%s: %s\r\n", item.first.c_str(), item.second.c_str());
             }
             }
             ::fputs("\r\n", fp);
             ::fputs("\r\n", fp);
-            ::fputs(this->body.c_str(), fp);
+
+            long mtu = conf::mtu;
+            const char *data = this->body.c_str();
+            const char *cursor = data;
+            long dataSize = ::strlen(data);
+            const char *endNextCursor = data + ::strlen(data);
+            int writeTimes = dataSize / mtu;
+            if (dataSize % mtu) {
+                writeTimes++;
+            }
+            for (int i = 0; i < writeTimes; i++) {
+                long writeSize = min((long)mtu, endNextCursor - cursor);
+                ::fprintf(fp, "%x\r\n", writeSize);
+                ::fwrite(cursor, 1, writeSize, fp);
+                ::fprintf(fp, "\r\n");
+                cursor += writeSize;
+            }
+            ::fprintf(fp, "0\r\n\r\n");
             ::fflush(fp);
             ::fflush(fp);
         }
         }
     } // xc
     } // xc

+ 5 - 0
webuiconf.h

@@ -29,6 +29,11 @@ namespace xc::conf {
             { "default", "application/octet-stream" },
             { "default", "application/octet-stream" },
     };
     };
 
 
+    const vector<string> defaultFiles = {
+            "index.html",
+            "index.htm"
+    };
+
     const IncompleteFileResponseData errorPage(FileResponseData(500, "html/error.html", "text/html"));
     const IncompleteFileResponseData errorPage(FileResponseData(500, "html/error.html", "text/html"));
 
 
     const auto errorPage400 = errorPage.applyReplacements(400, {
     const auto errorPage400 = errorPage.applyReplacements(400, {