Răsfoiți Sursa

Add examples/wget to test HTTP HEAD and Range

hewei.it 4 ani în urmă
părinte
comite
06cfb13ddb
6 a modificat fișierele cu 133 adăugiri și 27 ștergeri
  1. 5 2
      Makefile
  2. 5 1
      examples/CMakeLists.txt
  3. 31 22
      examples/curl.cpp
  4. 87 0
      examples/wget.cpp
  5. 2 2
      http/client/requests.h
  6. 3 0
      http/server/HttpHandler.cpp

+ 5 - 2
Makefile

@@ -33,7 +33,7 @@ endif
 default: all
 all: libhv examples
 examples: hmain_test htimer_test hloop_test \
-	nc nmap httpd curl \
+	nc nmap httpd curl wget \
 	udp_echo_server \
 	tcp_echo_server \
 	tcp_chat_server \
@@ -102,6 +102,9 @@ curl: prepare
 	$(MAKEF) TARGET=$@ SRCDIRS=". base utils event evpp http http/client" SRCS="examples/curl.cpp"
 	# $(MAKEF) TARGET=$@ SRCDIRS=". base utils event http http/client" SRCS="examples/curl.cpp" WITH_CURL=yes DEFINES="CURL_STATICLIB"
 
+wget: prepare
+	$(MAKEF) TARGET=$@ SRCDIRS=". base utils event evpp http http/client" SRCS="examples/wget.cpp"
+
 http_server_test: prepare
 	$(MAKEF) TARGET=$@ SRCDIRS=". base utils event evpp http http/server" SRCS="examples/http_server_test.cpp"
 
@@ -161,4 +164,4 @@ echo-servers:
 	$(CXX) -g -Wall -std=c++11 -o bin/poco_echo     echo-servers/poco_echo.cpp   -lPocoNet -lPocoUtil -lPocoFoundation
 	$(CXX) -g -Wall -std=c++11 -o bin/muduo_echo    echo-servers/muduo_echo.cpp  -lmuduo_net -lmuduo_base -lpthread
 
-.PHONY: clean prepare libhv install examples nc nmap httpd curl consul_cli unittest evpp webbench echo-servers
+.PHONY: clean prepare libhv install examples nc nmap httpd curl wget consul_cli unittest evpp webbench echo-servers

+ 5 - 1
examples/CMakeLists.txt

@@ -75,6 +75,10 @@ if(WITH_HTTP_CLIENT)
     endif()
     target_link_libraries(${CURL_TARGET_NAME} hv)
 
+    # wget
+    add_executable(wget wget.cpp)
+    target_link_libraries(wget hv)
+
     # http_client_test
     add_executable(http_client_test http_client_test.cpp)
     target_link_libraries(http_client_test hv)
@@ -83,7 +87,7 @@ if(WITH_HTTP_CLIENT)
     add_executable(websocket_client_test websocket_client_test.cpp)
     target_link_libraries(websocket_client_test hv)
 
-    list(APPEND EXAMPLES ${CURL_TARGET_NAME} http_client_test websocket_client_test)
+    list(APPEND EXAMPLES ${CURL_TARGET_NAME} wget http_client_test websocket_client_test)
 endif()
 
 if(WITH_CONSUL)

+ 31 - 22
examples/curl.cpp

@@ -15,23 +15,25 @@
 #include <getopt.h>
 #endif
 
-static int  http_version = 1;
-static int  grpc         = 0;
-static bool verbose = false;
-static const char* url = NULL;
-static const char* method = NULL;
-static const char* headers = NULL;
-static const char* data = NULL;
-static const char* form = NULL;
-static int  send_count   = 1;
+static int  http_version    = 1;
+static int  grpc            = 0;
+static bool verbose         = false;
+static const char* url      = NULL;
+static const char* method   = NULL;
+static const char* headers  = NULL;
+static const char* range    = NULL;
+static const char* data     = NULL;
+static const char* form     = NULL;
+static int send_count       = 1;
 
-static const char* options = "hVvX:H:d:F:n:";
+static const char* options = "hVvX:H:r:d:F:n:";
 static const struct option long_options[] = {
     {"help",    no_argument,        NULL,   'h'},
     {"verion",  no_argument,        NULL,   'V'},
     {"verbose", no_argument,        NULL,   'v'},
     {"method",  required_argument,  NULL,   'X'},
     {"header",  required_argument,  NULL,   'H'},
+    {"range",   required_argument,  NULL,   'r'},
     {"data",    required_argument,  NULL,   'd'},
     {"form",    required_argument,  NULL,   'F'},
     {"http2",   no_argument,        &http_version, 2},
@@ -45,6 +47,7 @@ static const char* help = R"(Options:
     -v|--verbose        Show verbose infomation.
     -X|--method         Set http method.
     -H|--header         Add http headers, -H "Content-Type:application/json Accept:*/*"
+    -r|--range          Add http header Range: bytes=0-1023
     -d|--data           Set http body.
     -F|--form           Set http form, -F "name1=content;name2=@filename"
     -n|--count          Send request count, used for test keep-alive
@@ -52,12 +55,14 @@ static const char* help = R"(Options:
        --grpc           Use grpc over http2
 Examples:
     curl -v localhost:8080
-    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/kv    -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
-    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'
+    curl -v localhost:8080 -X HEAD
+    curl -v localhost:8080 -r 0-9
+    curl -v localhost:8080/ping
+    curl -v localhost:8080/query?page_no=1&page_size=10
+    curl -v localhost:8080/echo  -d 'hello,world!'
+    curl -v localhost:8080/kv    -H "Content-Type:application/x-www-form-urlencoded" -d 'user=admin&pswd=123456'
+    curl -v localhost:8080/json  -H "Content-Type:application/json"                  -d '{"user":"admin","pswd":"123456"}'
+    curl -v localhost:8080/form  -F 'file=@filename'
 )";
 
 void print_usage() {
@@ -77,13 +82,14 @@ int parse_cmdline(int argc, char* argv[]) {
     int opt_idx;
     while ((opt = getopt_long(argc, argv, options, long_options, &opt_idx)) != EOF) {
         switch(opt) {
-        case 'h': print_help(); exit(0);
-        case 'V': print_version(); exit(0);
-        case 'v': verbose = true; break;
-        case 'X': method = optarg; break;
+        case 'h': print_help();     exit(0);
+        case 'V': print_version();  exit(0);
+        case 'v': verbose = true;   break;
+        case 'X': method = optarg;  break;
         case 'H': headers = optarg; break;
-        case 'd': data = optarg; break;
-        case 'F': form = optarg; break;
+        case 'r': range = optarg;   break;
+        case 'd': data = optarg;    break;
+        case 'F': form = optarg;    break;
         case 'n': send_count = atoi(optarg); break;
         default: break;
         }
@@ -155,6 +161,9 @@ int main(int argc, char* argv[]) {
             key_len = value_len = 0;
         }
     }
+    if (range) {
+        req.headers["Range"] = std::string("bytes=").append(range);
+    }
     if (data || form) {
         if (method == NULL) {
             req.method = HTTP_POST;

+ 87 - 0
examples/wget.cpp

@@ -0,0 +1,87 @@
+/*
+ * @build: make examples
+ * @server bin/httpd -s restart -d
+ * @client bin/wget 127.0.0.1:8080/
+ */
+
+#include "requests.h"
+
+int main(int argc, char** argv) {
+    if (argc < 2) {
+        printf("Usage: %s url\n", argv[0]);
+    }
+    const char* url = argv[1];
+
+    std::string filepath;
+    const char* path = strrchr(url, '/');
+    if (path == NULL || path[1] == '\0') {
+        filepath = "index.html";
+    } else {
+        filepath = path + 1;
+    }
+    printf("save file to %s ...\n", filepath.c_str());
+
+    HFile file;
+    if (file.open(filepath.c_str(), "wb") != 0) {
+        fprintf(stderr, "Failed to open file %s\n", filepath.c_str());
+        return -10;
+    }
+
+    // HEAD
+    requests::Request req(new HttpRequest);
+    req->url = url;
+    req->method = HTTP_HEAD;
+    printf("%s\n", req->Dump(true, true).c_str());
+    auto resp = requests::request(req);
+    if (resp == NULL) {
+        fprintf(stderr, "request failed!\n");
+        return -1;
+    }
+    printf("%s\n", resp->Dump(true, false).c_str());
+
+    bool use_range = false;
+    int range_bytes = 1 << 20; // 1M
+    std::string accept_ranges = resp->GetHeader("Accept-Ranges");
+    size_t content_length = hv::from_string<size_t>(resp->GetHeader("Content-Length"));
+    // use Range if server accept_ranges and content_length > 1M
+    if (resp->status_code == 200 &&
+        accept_ranges == "bytes" &&
+        content_length > range_bytes) {
+        use_range = true;
+    }
+
+    // GET
+    req->method = HTTP_GET;
+    if (!use_range) {
+        printf("%s\n", req->Dump(true, true).c_str());
+        resp = requests::get(url);
+        if (resp == NULL) {
+            fprintf(stderr, "request failed!\n");
+            return -1;
+        }
+        printf("%s\n", resp->Dump(true, false).c_str());
+        file.write(resp->body.data(), resp->body.size());
+        return 0;
+    }
+
+    // [from, to]
+    long from, to;
+    from = 0;
+    while (from < content_length) {
+        to = from + range_bytes - 1;
+        if (to >= content_length) to = content_length - 1;
+        // Range: bytes=from-to
+        req->SetRange(from, to);
+        printf("%s\n", req->Dump(true, true).c_str());
+        resp = requests::request(req);
+        if (resp == NULL) {
+            fprintf(stderr, "request failed!\n");
+            return -1;
+        }
+        printf("%s\n", resp->Dump(true, false).c_str());
+        file.write(resp->body.data(), resp->body.size());
+        from = to + 1;
+    }
+
+    return 0;
+}

+ 2 - 2
http/client/requests.h

@@ -42,13 +42,13 @@ static http_headers DefaultHeaders;
 static http_body    NoBody;
 
 Response request(Request req) {
-    Response resp = Response(new HttpResponse);
+    Response resp(new HttpResponse);
     int ret = http_client_send(req.get(), resp.get());
     return ret ? NULL : resp;
 }
 
 Response request(http_method method, const char* url, const http_body& body = NoBody, const http_headers& headers = DefaultHeaders) {
-    Request req = Request(new HttpRequest);
+    Request req(new HttpRequest);
     req->method = method;
     req->url = url;
     if (&body != &NoBody) {

+ 3 - 0
http/server/HttpHandler.cpp

@@ -137,6 +137,9 @@ int HttpHandler::GetSendData(char** data, size_t* len) {
                 if (fc) {
                     res.headers["Accept-Ranges"] = "bytes";
                     res.headers["Content-Length"] = hv::to_string(fc->st.st_size);
+                } else {
+                    res.headers["Content-Type"] = "text/html";
+                    res.headers["Content-Length"] = "0";
                 }
                 state = SEND_DONE;
                 goto return_nobody;