瀏覽代碼

Add test for client functions and fix their timeout handling

bel 10 年之前
父節點
當前提交
457e6cc8c9
共有 2 個文件被更改,包括 104 次插入39 次删除
  1. 78 39
      src/civetweb.c
  2. 26 0
      test/unit_test.c

+ 78 - 39
src/civetweb.c

@@ -3724,6 +3724,10 @@ static SOCKET conn2(struct mg_context *ctx  /* may be null */, const char *host,
     struct hostent *he;
     SOCKET sock = INVALID_SOCKET;
 
+    if (ebuf_len>0) {
+        *ebuf = 0;
+    }
+
     if (host == NULL) {
         snprintf(ebuf, ebuf_len, "%s", "NULL host");
     } else if (use_ssl && SSLv23_client_method == NULL) {
@@ -4296,18 +4300,22 @@ static int read_request(FILE *fp, struct mg_connection *conn,
 {
     int request_len, n = 0;
     time_t last_action_time = 0;
-    double request_timout = 0.0;
+    double request_timout;
 
     if (conn->ctx->config[REQUEST_TIMEOUT]) {
         /* value of request_timout is in seconds, config in milliseconds */
         request_timout = atof(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
+    } else {
+        request_timout = -1.0;
     }
 
     request_len = get_request_len(buf, *nread);
-    while (conn->ctx->stop_flag == 0 &&
-           *nread < bufsiz && request_len == 0 &&
-           difftime(last_action_time, conn->birth_time) <= request_timout &&
-           (n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) {
+    while ((conn->ctx->stop_flag == 0) &&
+           (*nread < bufsiz) &&
+           (request_len == 0) &&
+           ((difftime(last_action_time, conn->birth_time) <= request_timout) || (request_timout < 0)) &&
+           ((n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0)
+          ) {
         *nread += n;
         assert(*nread <= bufsiz);
         request_len = get_request_len(buf, *nread);
@@ -6924,6 +6932,40 @@ static void reset_per_request_attributes(struct mg_connection *conn)
     conn->data_len = 0;
 }
 
+
+static int set_sock_timeout(SOCKET sock, int milliseconds)
+{
+    int r1, r2;
+#ifdef _WIN32
+    DWORD t = milliseconds;
+#else
+#if defined(TCP_USER_TIMEOUT)
+    unsigned int uto = (unsigned int)milliseconds;
+#endif
+    struct timeval t;
+    t.tv_sec = milliseconds / 1000;
+    t.tv_usec = (milliseconds * 1000) % 1000000;
+
+    /* TCP_USER_TIMEOUT/RFC5482 (http://tools.ietf.org/html/rfc5482):
+       max. time waiting for the acknowledged of TCP data before the connection
+       will be forcefully closed and ETIMEDOUT is returned to the application.
+       If this option is not set, the default timeout of 20-30 minutes is used.
+    */
+    /* #define TCP_USER_TIMEOUT (18) */
+
+#if defined(TCP_USER_TIMEOUT)
+    setsockopt(sock, 6, TCP_USER_TIMEOUT, (const void *)&uto, sizeof(uto));
+#endif
+
+#endif
+
+    r1 = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (SOCK_OPT_TYPE) &t, sizeof(t));
+    r2 = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_OPT_TYPE) &t, sizeof(t));
+
+    return r1 || r2;
+}
+
+
 static void close_socket_gracefully(struct mg_connection *conn)
 {
 #if defined(_WIN32)
@@ -7140,10 +7182,26 @@ static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *
 int mg_get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int timeout)
 {
     /* Implementation of API function for HTTP clients */
+    int err, ret;
+    struct mg_context *octx = conn->ctx;
+    struct mg_context rctx = *(conn->ctx);
+    char txt[32];
+
+    if (timeout >= 0) {
+        snprintf(txt, sizeof(txt), "%i", timeout);
+        rctx.config[REQUEST_TIMEOUT] = txt;
+        set_sock_timeout(conn->client.sock, timeout);
+    } else {
+        rctx.config[REQUEST_TIMEOUT] = NULL;
+    }
+
+    conn->ctx = &rctx;
+    ret = getreq(conn, ebuf, ebuf_len, &err);
+    conn->ctx = octx;
+
     /* TODO: Define proper return values - maybe return length?
              For the first test use <0 for error and >0 for OK */
-    int err;
-    return (getreq(conn, ebuf, ebuf_len, &err) == 0) ? -1 : +1;
+    return (ret == 0) ? -1 : +1;
 }
 
 struct mg_connection *mg_download(const char *host, int port, int use_ssl,
@@ -7490,37 +7548,6 @@ static void produce_socket(struct mg_context *ctx, const struct socket *sp)
     (void) pthread_mutex_unlock(&ctx->thread_mutex);
 }
 
-static int set_sock_timeout(SOCKET sock, int milliseconds)
-{
-    int r1, r2;
-#ifdef _WIN32
-    DWORD t = milliseconds;
-#else
-#if defined(TCP_USER_TIMEOUT)
-    unsigned int uto = (unsigned int)milliseconds;
-#endif
-    struct timeval t;
-    t.tv_sec = milliseconds / 1000;
-    t.tv_usec = (milliseconds * 1000) % 1000000;
-
-    /* TCP_USER_TIMEOUT/RFC5482 (http://tools.ietf.org/html/rfc5482):
-       max. time waiting for the acknowledged of TCP data before the connection
-       will be forcefully closed and ETIMEDOUT is returned to the application.
-       If this option is not set, the default timeout of 20-30 minutes is used.
-    */
-    /* #define TCP_USER_TIMEOUT (18) */
-
-#if defined(TCP_USER_TIMEOUT)
-    setsockopt(sock, 6, TCP_USER_TIMEOUT, (const void *)&uto, sizeof(uto));
-#endif
-
-#endif
-
-    r1 = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (SOCK_OPT_TYPE) &t, sizeof(t));
-    r2 = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_OPT_TYPE) &t, sizeof(t));
-
-    return r1 || r2;
-}
 
 static void accept_new_connection(const struct socket *listener,
                                   struct mg_context *ctx)
@@ -7529,6 +7556,7 @@ static void accept_new_connection(const struct socket *listener,
     char src_addr[IP_ADDR_STR_LEN];
     socklen_t len = sizeof(so.rsa);
     int on = 1;
+    int timeout;
 
     if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) == INVALID_SOCKET) {
     } else if (!check_acl(ctx, ntohl(* (uint32_t *) &so.rsa.sin.sin_addr))) {
@@ -7546,6 +7574,7 @@ static void accept_new_connection(const struct socket *listener,
             mg_cry(fc(ctx), "%s: getsockname() failed: %s",
                    __func__, strerror(ERRNO));
         }
+
         /* Set TCP keep-alive. This is needed because if HTTP-level keep-alive
            is enabled, and client resets the connection, server won't get
            TCP FIN or RST and will keep the connection open forever. With TCP
@@ -7557,7 +7586,17 @@ static void accept_new_connection(const struct socket *listener,
                    "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s",
                    __func__, strerror(ERRNO));
         }
-        set_sock_timeout(so.sock, atoi(ctx->config[REQUEST_TIMEOUT]));
+
+        if (ctx->config[REQUEST_TIMEOUT]) {
+            timeout = atoi(ctx->config[REQUEST_TIMEOUT]);
+        } else {
+            timeout = -1;
+        }
+
+        if (timeout>0) {
+            set_sock_timeout(so.sock, atoi(ctx->config[REQUEST_TIMEOUT]));
+        }
+
         produce_socket(ctx, &so);
     }
 }

+ 26 - 0
test/unit_test.c

@@ -397,6 +397,8 @@ static void test_mg_download(int use_ssl) {
     int i, len1, len2, port;
     struct mg_connection *conn;
     struct mg_context *ctx;
+    struct mg_request_info *ri;
+
     if (use_ssl) port = atoi(HTTPS_PORT); else port = atoi(HTTP_PORT);
 
     ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
@@ -527,6 +529,30 @@ static void test_mg_download(int use_ssl) {
         mg_close_connection(conn);
     }
 
+    /* Test new API */
+    ebuf[0] = 1;
+    conn = mg_connect_client("localhost", port, use_ssl, ebuf, sizeof(ebuf));
+    ASSERT(conn != NULL);
+    ASSERT(ebuf[0] == 0);
+    ri = mg_get_request_info(conn);
+    ASSERT(ri->content_length == 0);
+    i = mg_get_response(conn, ebuf, sizeof(ebuf), 1000);
+    ASSERT(ebuf[0] != 0);
+    ri = mg_get_request_info(conn);
+    ASSERT(ri->content_length == -1);
+    mg_printf(conn, "GET /index.html HTTP/1.1\r\n");
+    mg_printf(conn, "Host: www.example.com\r\n");
+    mg_printf(conn, "\r\n");
+    i = mg_get_response(conn, ebuf, sizeof(ebuf), 1000);
+    ASSERT(ebuf[0] == 0);
+    ri = mg_get_request_info(conn);
+    ASSERT(ri->content_length > 0);
+    mg_read(conn, ebuf, sizeof(ebuf));
+    ASSERT(!strncmp(ebuf, "Error 404", 9));
+
+    mg_close_connection(conn);
+
+    /* Stop the test server */
     mg_stop(ctx);
 }