Ver código fonte

http_server add index_of

ithewei 6 anos atrás
pai
commit
54036e5c90

+ 12 - 8
base/hdir.h

@@ -12,20 +12,24 @@ int main(int argc, char* argv[]) {
     listdir(dir, dirs);
     for (auto& item : dirs) {
         printf("%c\t", item.type);
-        float hsize = item.size / 1024.0f;
-        if (hsize < 1.0f) {
+        float hsize;
+        if (item.size < 1024) {
             printf("%lu\t", item.size);
         }
-        else if (hsize > 1024.0f) {
-            printf("%.02fM\t", hsize/1024.0f);
+        else if ((hsize = item.size/1024.0f) < 1024.0f) {
+            printf("%.1fK\t", hsize);
+        }
+        else if ((hsize /= 1024.0f) < 1024.0f) {
+            printf("%.1fM\t", hsize);
         }
         else {
-            printf("%.02fK\t", hsize);
+            hsize /= 1024.0f;
+            printf("%.1fG\t", hsize);
         }
         struct tm* tm = localtime(&item.mtime);
-        printf("%04d-%02d-%02d %02d:%02d:%02d\t%s\n",
-                tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
-                item.name);
+        printf("%04d-%02d-%02d %02d:%02d:%02d\t",
+                tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+        printf("%s%s\n", item.name, item.type == 'd' ? "/" : "");
     }
     return 0;
 }

+ 6 - 0
base/hplatform.h

@@ -129,6 +129,12 @@
     #include <io.h>         // for open,close,read,write,lseek,tell
 
     #define MKDIR(dir)      mkdir(dir)
+    #ifndef S_ISREG
+    #define S_ISREG(st_mode) (((st_mode) & S_IFMT) == S_IFREG)
+    #endif
+    #ifndef S_ISDIR
+    #define S_ISDIR(st_mode) (((st_mode) & S_IFMT) == S_IFDIR)
+    #endif
 #else
     #include <unistd.h>
     #include <dirent.h>     // for mkdir,rmdir,chdir,getcwd

+ 2 - 2
etc/httpd.conf

@@ -14,5 +14,5 @@ port = 8080
 base_url = /v1/api
 document_root = html
 home_page = index.html
-error_page = error.html
-
+#error_page = error.html
+index_of = /downloads/

+ 5 - 0
examples/httpd.cpp

@@ -134,6 +134,11 @@ int parse_confile(const char* confile) {
     if (str.size() != 0) {
         g_http_service.error_page = str;
     }
+    // index_of
+    str = g_conf_ctx.parser->GetValue("index_of");
+    if (str.size() != 0) {
+        g_http_service.index_of = str;
+    }
 
     return 0;
 }

+ 4 - 1
http/HttpRequest.h

@@ -161,7 +161,10 @@ append:
     }
 
     void fill_content_length() {
-        headers["Content-Length"] = std::to_string(body.size());
+        auto iter = headers.find("Content-Length");
+        if (iter == headers.end()) {
+            headers["Content-Length"] = std::to_string(body.size());
+        }
     }
 
     void dump_headers(std::string& str) {

+ 34 - 16
http/server/FileCache.h

@@ -6,15 +6,17 @@
 
 #include "hbuf.h"
 #include "hfile.h"
+#include "hstring.h"
+#include "hscope.h"
+#include "hdir.h"
+
 #include "md5.h"
 #include "HttpRequest.h" // for get_content_type_str_by_suffix
-
-#ifndef INVALID_FD
-#define INVALID_FD  -1
-#endif
+#include "http_page.h"
 
 #define HTTP_HEADER_MAX_LENGTH      1024 // 1k
 
+
 typedef struct file_cache_s {
     //std::string filepath;
     struct stat st;
@@ -70,9 +72,9 @@ public:
         cached_files.clear();
     }
 
-    file_cache_t* Open(const char* filepath) {
+    file_cache_t* Open(const char* filepath, void* ctx) {
         file_cache_t* fc = Get(filepath);
-        bool filechanged = false;
+        bool modified = false;
         if (fc) {
             time_t tt;
             time(&tt);
@@ -82,28 +84,48 @@ public:
                 fc->stat_time = tt;
                 fc->stat_cnt++;
                 if (mtime != fc->st.st_mtime) {
-                    filechanged = true;
+                    modified = true;
                     fc->stat_cnt = 1;
                 }
             }
         }
-        if (fc == NULL || filechanged) {
+        if (fc == NULL || modified) {
             int fd = open(filepath, O_RDONLY);
             if (fd < 0) {
                 return NULL;
             }
+            ScopeCleanup _(close, fd);
             if (fc == NULL) {
+                struct stat st;
+                fstat(fd, &st);
+                if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) {
+                    return NULL;
+                }
                 fc = new file_cache_t;
                 //fc->filepath = filepath;
-                fstat(fd, &fc->st);
+                fc->st = st;
                 time(&fc->open_time);
                 fc->stat_time = fc->open_time;
                 fc->stat_cnt = 1;
                 cached_files[filepath] = fc;
             }
-            fc->resize_buf(fc->st.st_size);
-            read(fd, fc->filebuf.base, fc->filebuf.len);
-            close(fd);
+            if (S_ISREG(fc->st.st_mode)) {
+                // FILE
+                fc->resize_buf(fc->st.st_size);
+                read(fd, fc->filebuf.base, fc->filebuf.len);
+                const char* suffix = strrchr(filepath, '.');
+                if (suffix) {
+                    fc->content_type = http_content_type_str_by_suffix(++suffix);
+                }
+            }
+            else if (S_ISDIR(fc->st.st_mode)) {
+                // DIR
+                std::string page;
+                make_index_of_page(filepath, page, (const char*)ctx);
+                fc->resize_buf(page.size());
+                memcpy(fc->filebuf.base, page.c_str(), page.size());
+                fc->content_type = http_content_type_str(TEXT_HTML);
+            }
             time_t tt = fc->st.st_mtime;
             strftime(fc->last_modified, sizeof(fc->last_modified), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&tt));
             MD5_CTX md5_ctx;
@@ -117,10 +139,6 @@ public:
                 md5 += 2;
             }
             fc->etag[32] = '\0';
-            const char* suffix = strrchr(filepath, '.');
-            if (suffix) {
-                fc->content_type = http_content_type_str_by_suffix(++suffix);
-            }
         }
         return fc;
     }

+ 10 - 35
http/server/HttpHandler.h

@@ -4,42 +4,11 @@
 #include "HttpService.h"
 #include "HttpParser.h"
 #include "FileCache.h"
+#include "http_page.h"
 #include "hloop.h"
 
 #define HTTP_KEEPALIVE_TIMEOUT  75 // s
 
-/*
-<!DOCTYPE html>
-<html>
-<head>
-  <title>404 Not Found</title>
-</head>
-<body>
-  <center><h1>404 Not Found</h1></center>
-  <hr>
-</body>
-</html>
- */
-static inline void make_http_status_page(http_status status_code, std::string& page) {
-    char szCode[8];
-    snprintf(szCode, sizeof(szCode), "%d ", status_code);
-    const char* status_message = http_status_str(status_code);
-    page += R"(<!DOCTYPE html>
-<html>
-<head>
-  <title>)";
-    page += szCode; page += status_message;
-    page += R"(</title>
-</head>
-<body>
-  <center><h1>)";
-    page += szCode; page += status_message;
-    page += R"(</h1></center>
-  <hr>
-</body>
-</html>)";
-}
-
 static inline void on_keepalive_timeout(htimer_t* timer) {
     hio_t* io = (hio_t*)timer->userdata;
     hclose(io);
@@ -121,9 +90,15 @@ public:
             if (strcmp(req.url.c_str(), "/") == 0) {
                 filepath += service->home_page;
             }
-            fc = files->Open(filepath.c_str());
-            // Not Found
+            if (filepath.c_str()[filepath.size()-1] != '/' ||
+                (service->index_of.size() != 0 &&
+                 req.url.size() >= service->index_of.size() &&
+                 strnicmp(req.url.c_str(), service->index_of.c_str(), service->index_of.size()) == 0)) {
+                fc = files->Open(filepath.c_str(), (void*)req.url.c_str());
+            }
+
             if (fc == NULL) {
+                // Not Found
                 res.status_code = HTTP_STATUS_NOT_FOUND;
             }
             else {
@@ -156,7 +131,7 @@ public:
                 std::string filepath = service->document_root;
                 filepath += '/';
                 filepath += service->error_page;
-                fc = files->Open(filepath.c_str());
+                fc = files->Open(filepath.c_str(), NULL);
             }
             // status page
             if (fc == NULL && res.body.size() == 0) {

+ 1 - 0
http/server/HttpService.h

@@ -41,6 +41,7 @@ struct HttpService {
     std::string document_root;
     std::string home_page;
     std::string error_page;
+    std::string index_of;
 
     HttpService() {
         preprocessor = NULL;

+ 87 - 0
http/server/http_page.cpp

@@ -0,0 +1,87 @@
+#include "http_page.h"
+#include "hdir.h"
+
+#define AUTOINDEX_FILENAME_MAXLEN       50
+
+void make_http_status_page(http_status status_code, std::string& page) {
+    char szCode[8];
+    snprintf(szCode, sizeof(szCode), "%d ", status_code);
+    const char* status_message = http_status_str(status_code);
+    page += R"(<!DOCTYPE html>
+<html>
+<head>
+  <title>)";
+    page += szCode; page += status_message;
+    page += R"(</title>
+</head>
+<body>
+  <center><h1>)";
+    page += szCode; page += status_message;
+    page += R"(</h1></center>
+  <hr>
+</body>
+</html>)";
+}
+
+void make_index_of_page(const char* dir, std::string& page, const char* url) {
+    std::list<hdir_t> dirs;
+    listdir(dir, dirs);
+    char c_str[1024] = {0};
+    snprintf(c_str, sizeof(c_str), R"(<!DOCTYPE html>
+<html>
+<head>
+  <title>Index of %s</title>
+</head>
+<body>
+  <h1>Index of %s</h1>
+  <hr>
+  <pre>
+)", url, url);
+    page += c_str;
+    for (auto& item : dirs) {
+        if (item.name[0] == '.' && item.name[1] == '\0') continue;
+        int len = strlen(item.name) + (item.type == 'd');
+        // name
+        snprintf(c_str, sizeof(c_str), "<a href=\"%s%s\">%s%s</a>",
+                item.name,
+                item.type == 'd' ? "/" : "",
+                len < AUTOINDEX_FILENAME_MAXLEN ? item.name : std::string(item.name, item.name+AUTOINDEX_FILENAME_MAXLEN-4).append("...").c_str(),
+                item.type == 'd' ? "/" : "");
+        page += c_str;
+        page += std::string(AUTOINDEX_FILENAME_MAXLEN - len, ' ');
+        if (strcmp(item.name, "..") != 0) {
+            // mtime
+            struct tm* tm = localtime(&item.mtime);
+            snprintf(c_str, sizeof(c_str), "%04d-%02d-%02d %02d:%02d:%02d        ",
+                    tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+            page += c_str;
+            // size
+            if (item.type == 'd') {
+                page += '-';
+            }
+            else {
+                float hsize;
+                if (item.size < 1024) {
+                    snprintf(c_str, sizeof(c_str), "%lu", item.size);
+                }
+                else if ((hsize = item.size/1024.0f) < 1024.0f) {
+                    snprintf(c_str, sizeof(c_str), "%.1fK", hsize);
+                }
+                else if ((hsize /= 1024.0f) < 1024.0f) {
+                    snprintf(c_str, sizeof(c_str), "%.1fM", hsize);
+                }
+                else {
+                    hsize /= 1024.0f;
+                    snprintf(c_str, sizeof(c_str), "%.1fG", hsize);
+                }
+                page += c_str;
+            }
+        }
+        page += "\r\n";
+    }
+    page += R"(
+  </pre>
+  <hr>
+</body>
+</html>)";
+}

+ 42 - 0
http/server/http_page.h

@@ -0,0 +1,42 @@
+#ifndef HTTP_PAGE_H_
+#define HTTP_PAGE_H_
+
+#include <string>
+
+#include "http_parser.h"
+
+/*
+<!DOCTYPE html>
+<html>
+<head>
+  <title>404 Not Found</title>
+</head>
+<body>
+  <center><h1>404 Not Found</h1></center>
+  <hr>
+</body>
+</html>
+ */
+void make_http_status_page(http_status status_code, std::string& page);
+
+/*
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Index of</title>
+</head>
+<body>
+  <h1>Index of</h1>
+  <hr>
+  <pre>
+  <a href="../">../</a>
+  <a href="images/">images/</a>                                  2019-08-22 19:06:05                 -
+  <a href="README.txt">README.txt</a>                                2019-08-22 19:06:05                 8.88K
+  </pre>
+  <hr>
+</body>
+</html>
+ */
+void make_index_of_page(const char* dir, std::string& page, const char* url = "");
+
+#endif // HTTP_PAGE_H_