소스 검색

mp => form; HttpMessage Get<> Set

hewei 5 년 전
부모
커밋
85cdfef41e
8개의 변경된 파일288개의 추가작업 그리고 28개의 파일을 삭제
  1. 2 1
      Makefile
  2. 8 2
      README.md
  3. 1 1
      etc/httpd.conf
  4. 5 5
      examples/curl.cpp
  5. 41 12
      examples/httpd/http_api_test.h
  6. 2 2
      html/downloads/scripts/getting_started.sh
  7. 199 3
      http/HttpMessage.cpp
  8. 30 2
      http/HttpMessage.h

+ 2 - 1
Makefile

@@ -20,7 +20,7 @@ libhv:
 	$(CP) $(INSTALL_HEADERS) include/hv
 
 install:
-	$(MKDIR) -p $(INSTALL_INCDIR)
+	$(MKDIR) $(INSTALL_INCDIR)
 	$(CP) include/hv/* $(INSTALL_INCDIR)
 	$(CP) lib/libhv.a lib/libhv.so $(INSTALL_LIBDIR)
 
@@ -51,6 +51,7 @@ else
 endif
 
 httpd: prepare
+	$(RM) examples/httpd/*.o
 	$(MAKEF) TARGET=$@ SRCDIRS=". base utils event http http/server examples/httpd"
 
 curl: prepare

+ 8 - 2
README.md

@@ -76,9 +76,15 @@ bin/curl -v localhost:8080/downloads/
 bin/curl -v localhost:8080/v1/api/hello
 bin/curl -v localhost:8080/v1/api/echo -d "hello,world!"
 bin/curl -v localhost:8080/v1/api/query?page_no=1&page_size=10
-bin/curl -v localhost:8080/v1/api/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
 bin/curl -v localhost:8080/v1/api/kv   -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
-bin/curl -v localhost:8080/v1/api/mp   -F "file=@LICENSE"
+bin/curl -v localhost:8080/v1/api/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
+bin/curl -v localhost:8080/v1/api/form -F "file=@LICENSE"
+
+bin/curl -v localhost:8080/v1/api/test  -H "Content-Type:application/x-www-form-urlencoded" -d
+'bool=1&int=123&float=3.14&string=hello'
+bin/curl -v localhost:8080/v1/api/test  -H "Content-Type:application/json" -d
+'{"bool":true,"int":123,"float":3.14,"string":"hello"}'
+bin/curl -v localhost:8080/v1/api/test  -F 'bool=1 int=123 float=3.14 string=hello'
 # RESTful API: /group/:group_name/user/:user_id
 bin/curl -v -X DELETE localhost:8080/v1/api/group/test/user/123
 

+ 1 - 1
etc/httpd.conf

@@ -8,7 +8,7 @@ log_filesize = 64M
 
 # worker_processes = 4
 # auto = ncpu
-worker_processes = 1
+worker_processes = 4
 
 # http server
 ssl = off

+ 5 - 5
examples/curl.cpp

@@ -49,9 +49,9 @@ Examples:
     curl -v localhost:8080/v1/api/hello
     curl -v localhost:8080/v1/api/query?page_no=1&page_size=10
     curl -v localhost:8080/v1/api/echo  -d 'hello,world!'
-    curl -v localhost:8080/v1/api/json  -H "Content-Type:application/json"                  -d '{"user":"admin","pswd":"123456"}'
     curl -v localhost:8080/v1/api/kv    -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
-    curl -v localhost:8080/v1/api/mp    -F 'file=@filename'
+    curl -v localhost:8080/v1/api/json  -H "Content-Type:application/json"                  -d '{"user":"admin","pswd":"123456"}'
+    curl -v localhost:8080/v1/api/form  -F 'file=@filename'
 )";
 
 void print_usage() {
@@ -164,7 +164,7 @@ int main(int argc, char* argv[]) {
             int value_len = 0;
             state = s_key;
             while (*p != '\0') {
-                if (*p == ';') {
+                if (*p == ' ') {
                     if (key_len && value_len) {
                         FormData data;
                         if (*value == '@') {
@@ -173,7 +173,7 @@ int main(int argc, char* argv[]) {
                         else {
                             data.content = std::string(value, value_len);
                         }
-                        req.mp[std::string(key,key_len)] = data;
+                        req.form[std::string(key,key_len)] = data;
                         key_len = value_len = 0;
                     }
                     state = s_key;
@@ -197,7 +197,7 @@ int main(int argc, char* argv[]) {
                 else {
                     data.content = std::string(value, value_len);
                 }
-                req.mp[std::string(key,key_len)] = data;
+                req.form[std::string(key,key_len)] = data;
             }
         }
     }

+ 41 - 12
examples/httpd/http_api_test.h

@@ -8,17 +8,24 @@
     XXX("/hello",   GET,    http_api_hello)     \
     XXX("/query",   GET,    http_api_query)     \
     XXX("/echo",    POST,   http_api_echo)      \
-    XXX("/json",    POST,   http_api_json)      \
-    XXX("/mp",      POST,   http_api_mp)        \
     XXX("/kv",      POST,   http_api_kv)        \
+    XXX("/json",    POST,   http_api_json)      \
+    XXX("/form",    POST,   http_api_form)      \
     XXX("/grpc",    POST,   http_api_grpc)      \
+    \
+    XXX("/test",    POST,   http_api_test)      \
     XXX("/group/:group_name/user/:user_id", DELETE, http_api_restful)   \
 
+inline void response_status(HttpResponse* res, int code, const char* message) {
+    res->Set("code", code);
+    res->Set("message", message);
+}
 
 inline int http_api_preprocessor(HttpRequest* req, HttpResponse* res) {
     //printf("%s:%d\n", req->client_addr.ip.c_str(), req->client_addr.port);
     //printf("%s\n", req->Dump(true, true).c_str());
     req->ParseBody();
+    res->content_type = APPLICATION_JSON;
     return 0;
 }
 
@@ -44,30 +51,33 @@ inline int http_api_echo(HttpRequest* req, HttpResponse* res) {
     return 0;
 }
 
-inline int http_api_json(HttpRequest* req, HttpResponse* res) {
-    if (req->content_type != APPLICATION_JSON) {
+inline int http_api_kv(HttpRequest*req, HttpResponse* res) {
+    if (req->content_type != APPLICATION_URLENCODED) {
         res->status_code = HTTP_STATUS_BAD_REQUEST;
         return 0;
     }
-    res->json = req->json;
+    res->content_type = APPLICATION_URLENCODED;
+    res->kv = req->kv;
     return 0;
 }
 
-inline int http_api_mp(HttpRequest* req, HttpResponse* res) {
-    if (req->content_type != MULTIPART_FORM_DATA) {
+inline int http_api_json(HttpRequest* req, HttpResponse* res) {
+    if (req->content_type != APPLICATION_JSON) {
         res->status_code = HTTP_STATUS_BAD_REQUEST;
         return 0;
     }
-    res->mp = req->mp;
+    res->content_type = APPLICATION_JSON;
+    res->json = req->json;
     return 0;
 }
 
-inline int http_api_kv(HttpRequest*req, HttpResponse* res) {
-    if (req->content_type != APPLICATION_URLENCODED) {
+inline int http_api_form(HttpRequest* req, HttpResponse* res) {
+    if (req->content_type != MULTIPART_FORM_DATA) {
         res->status_code = HTTP_STATUS_BAD_REQUEST;
         return 0;
     }
-    res->kv = req->kv;
+    res->content_type = MULTIPART_FORM_DATA;
+    res->form = req->form;
     return 0;
 }
 
@@ -78,14 +88,33 @@ inline int http_api_grpc(HttpRequest* req, HttpResponse* res) {
     }
     // parse protobuf: ParseFromString
     // req->body;
+    // res->content_type = APPLICATION_GRPC;
     // serailize protobuf: SerializeAsString
     // res->body;
     return 0;
 }
 
+inline int http_api_test(HttpRequest* req, HttpResponse* res) {
+    string str = req->GetValue("string");
+    int64_t n = req->Get<int64_t>("int");
+    double f = req->Get<double>("float");
+    bool b = req->Get<bool>("bool");
+
+    res->content_type = req->content_type;
+    res->Set("string", str);
+    res->Set("int", n);
+    res->Set("float", f);
+    res->Set("bool", b);
+    response_status(res, 0, "OK");
+    return 0;
+}
+
 inline int http_api_restful(HttpRequest*req, HttpResponse* res) {
     // RESTful /:field/ => req->query_params
-    res->kv = req->query_params;
+    for (auto& param : req->query_params) {
+        res->Set(param.first.c_str(), param.second);
+    }
+    response_status(res, 0, "Operation completed.");
     return 0;
 }
 

+ 2 - 2
html/downloads/scripts/getting_started.sh

@@ -15,8 +15,8 @@ bin/curl -v localhost:8080/downloads/
 bin/curl -v localhost:8080/v1/api/hello
 bin/curl -v localhost:8080/v1/api/echo -d "hello,world!"
 bin/curl -v localhost:8080/v1/api/query?page_no=1&page_size=10
-bin/curl -v localhost:8080/v1/api/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
 bin/curl -v localhost:8080/v1/api/kv   -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
-bin/curl -v localhost:8080/v1/api/mp   -F "file=@LICENSE"
+bin/curl -v localhost:8080/v1/api/json -H "Content-Type:application/json" -d '{"user":"admin","pswd":"123456"}'
+bin/curl -v localhost:8080/v1/api/form -F "file=@LICENSE"
 # RESTful API: /group/:group_name/user/:user_id
 bin/curl -v -X DELETE localhost:8080/v1/api/group/test/user/123

+ 199 - 3
http/HttpMessage.cpp

@@ -5,6 +5,202 @@
 #include "htime.h"
 #include "http_parser.h" // for http_parser_url
 
+#ifndef WITHOUT_HTTP_CONTENT
+// NOTE: json ignore number/string, 123/"123"
+using nlohmann::detail::value_t;
+
+std::string HttpMessage::GetValue(const char* key, const std::string& defvalue) {
+    switch (content_type) {
+    case APPLICATION_JSON:
+    {
+        auto iter = json.find(key);
+        if (iter != json.end()) {
+            switch (iter->type()) {
+            case value_t::null:
+                return "null";
+            case value_t::string:
+                return *iter;
+            case value_t::boolean:
+            {
+                bool b = *iter;
+                return b ? "true" : "false";
+            }
+            case value_t::number_integer:
+            {
+                std::int64_t n = *iter;
+                return std::to_string(n);
+            }
+            case value_t::number_unsigned:
+            {
+                std::uint64_t n = *iter;
+                return std::to_string(n);
+            }
+            case value_t::number_float:
+            {
+                double f = *iter;
+                return std::to_string(f);
+            }
+            default:
+                return defvalue;
+            }
+        }
+    }
+        break;
+    case MULTIPART_FORM_DATA:
+    {
+        auto iter = form.find(key);
+        if (iter != form.end()) {
+            return iter->second.content;
+        }
+    }
+        break;
+    case APPLICATION_URLENCODED:
+    {
+        auto iter = kv.find(key);
+        if (iter != kv.end()) {
+            return iter->second;
+        }
+    }
+        break;
+    default:
+        break;
+    }
+    return defvalue;
+}
+
+template<>
+int64_t HttpMessage::Get(const char* key, int64_t defvalue) {
+    if (content_type == APPLICATION_JSON) {
+        auto iter = json.find(key);
+        if (iter != json.end()) {
+            switch (iter->type()) {
+                return *iter;
+            case value_t::boolean:
+            {
+                bool b = *iter;
+                return b;
+            }
+            case value_t::number_integer:
+            {
+                std::int64_t n = *iter;
+                return n;
+            }
+            case value_t::number_unsigned:
+            {
+                std::uint64_t n = *iter;
+                return n;
+            }
+            case value_t::number_float:
+            {
+                double f = *iter;
+                return f;
+            }
+            case value_t::string:
+            {
+                std::string str = *iter;
+                return atoll(str.c_str());
+            }
+            default:
+                return defvalue;
+            }
+        }
+        return defvalue;
+    }
+    else {
+        std::string str = GetValue(key);
+        return str.empty() ? defvalue : atoi(str.c_str());
+    }
+}
+
+template<>
+double HttpMessage::Get(const char* key, double defvalue) {
+    if (content_type == APPLICATION_JSON) {
+        auto iter = json.find(key);
+        if (iter != json.end()) {
+            switch (iter->type()) {
+                return *iter;
+            case value_t::boolean:
+            {
+                bool b = *iter;
+                return b;
+            }
+            case value_t::number_integer:
+            {
+                std::int64_t n = *iter;
+                return n;
+            }
+            case value_t::number_unsigned:
+            {
+                std::uint64_t n = *iter;
+                return n;
+            }
+            case value_t::number_float:
+            {
+                double f = *iter;
+                return f;
+            }
+            case value_t::string:
+            {
+                std::string str = *iter;
+                return atof(str.c_str());
+            }
+            default:
+                return defvalue;
+            }
+        }
+        return defvalue;
+    }
+    else {
+        std::string str = GetValue(key);
+        return str.empty() ? defvalue : atoi(str.c_str());
+    }
+}
+
+template<>
+bool HttpMessage::Get(const char* key, bool defvalue) {
+    if (content_type == APPLICATION_JSON) {
+        auto iter = json.find(key);
+        if (iter != json.end()) {
+            switch (iter->type()) {
+                return *iter;
+            case value_t::boolean:
+            {
+                bool b = *iter;
+                return b;
+            }
+            case value_t::number_integer:
+            {
+                std::int64_t n = *iter;
+                return n;
+            }
+            case value_t::number_unsigned:
+            {
+                std::uint64_t n = *iter;
+                return n;
+            }
+            case value_t::number_float:
+            {
+                double f = *iter;
+                return f;
+            }
+            case value_t::string:
+            {
+                std::string str = *iter;
+                return atof(str.c_str());
+            }
+            default:
+                return defvalue;
+            }
+        }
+        return defvalue;
+    }
+    else {
+        std::string str = GetValue(key);
+        return str.empty() ? defvalue : atoi(str.c_str());
+    }
+}
+#endif
+
 void HttpMessage::FillContentType() {
     auto iter = headers.find("Content-Type");
     if (iter != headers.end()) {
@@ -17,7 +213,7 @@ void HttpMessage::FillContentType() {
         if (json.size() != 0) {
             content_type = APPLICATION_JSON;
         }
-        else if (mp.size() != 0) {
+        else if (form.size() != 0) {
             content_type = MULTIPART_FORM_DATA;
         }
         else if (kv.size() != 0) {
@@ -106,7 +302,7 @@ void HttpMessage::DumpBody() {
             return;
         }
         boundary += strlen("boundary=");
-        body = dump_multipart(mp, boundary);
+        body = dump_multipart(form, boundary);
     }
         break;
     case X_WWW_FORM_URLENCODED:
@@ -139,7 +335,7 @@ int HttpMessage::ParseBody() {
             return false;
         }
         boundary += strlen("boundary=");
-        return parse_multipart(body, mp, boundary);
+        return parse_multipart(body, form, boundary);
     }
     case X_WWW_FORM_URLENCODED:
         return parse_query_params(body.c_str(), kv);

+ 30 - 2
http/HttpMessage.h

@@ -3,6 +3,7 @@
 
 #include <string>
 #include <map>
+#include <sstream>
 
 #include "hstring.h"
 #include "httpdef.h"
@@ -35,8 +36,35 @@ public:
     http_content_type   content_type;
 #ifndef WITHOUT_HTTP_CONTENT
     Json                json;       // APPLICATION_JSON
-    MultiPart           mp;         // MULTIPART_FORM_DATA
+    MultiPart           form;       // MULTIPART_FORM_DATA
     KeyValue            kv;         // X_WWW_FORM_URLENCODED
+
+    std::string GetValue(const char* key, const std::string& = std::string(""));
+    // T=[bool, int64_t, double]
+    template<typename T>
+    T Get(const char* key, T defvalue = 0);
+
+    // T=[string, bool, int64_t, double]
+    template<typename T>
+    void Set(const char* key, const T& value) {
+        switch (content_type) {
+        case APPLICATION_JSON:
+            json[key] = value;
+            break;
+        case MULTIPART_FORM_DATA:
+            form[key] = FormData(value);
+            break;
+        case X_WWW_FORM_URLENCODED:
+        {
+            std::ostringstream os;
+            os << value;
+            kv[key] = os.str();
+        }
+            break;
+        default:
+            break;
+        }
+    }
 #endif
 
     HttpMessage() {
@@ -60,7 +88,7 @@ public:
         body.clear();
 #ifndef WITHOUT_HTTP_CONTENT
         json.clear();
-        mp.clear();
+        form.clear();
         kv.clear();
 #endif
     }