瀏覽代碼

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;
     struct hostent *he;
     SOCKET sock = INVALID_SOCKET;
     SOCKET sock = INVALID_SOCKET;
 
 
+    if (ebuf_len>0) {
+        *ebuf = 0;
+    }
+
     if (host == NULL) {
     if (host == NULL) {
         snprintf(ebuf, ebuf_len, "%s", "NULL host");
         snprintf(ebuf, ebuf_len, "%s", "NULL host");
     } else if (use_ssl && SSLv23_client_method == NULL) {
     } 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;
     int request_len, n = 0;
     time_t last_action_time = 0;
     time_t last_action_time = 0;
-    double request_timout = 0.0;
+    double request_timout;
 
 
     if (conn->ctx->config[REQUEST_TIMEOUT]) {
     if (conn->ctx->config[REQUEST_TIMEOUT]) {
         /* value of request_timout is in seconds, config in milliseconds */
         /* value of request_timout is in seconds, config in milliseconds */
         request_timout = atof(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
         request_timout = atof(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0;
+    } else {
+        request_timout = -1.0;
     }
     }
 
 
     request_len = get_request_len(buf, *nread);
     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;
         *nread += n;
         assert(*nread <= bufsiz);
         assert(*nread <= bufsiz);
         request_len = get_request_len(buf, *nread);
         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;
     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)
 static void close_socket_gracefully(struct mg_connection *conn)
 {
 {
 #if defined(_WIN32)
 #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)
 int mg_get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int timeout)
 {
 {
     /* Implementation of API function for HTTP clients */
     /* 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?
     /* TODO: Define proper return values - maybe return length?
              For the first test use <0 for error and >0 for OK */
              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,
 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);
     (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,
 static void accept_new_connection(const struct socket *listener,
                                   struct mg_context *ctx)
                                   struct mg_context *ctx)
@@ -7529,6 +7556,7 @@ static void accept_new_connection(const struct socket *listener,
     char src_addr[IP_ADDR_STR_LEN];
     char src_addr[IP_ADDR_STR_LEN];
     socklen_t len = sizeof(so.rsa);
     socklen_t len = sizeof(so.rsa);
     int on = 1;
     int on = 1;
+    int timeout;
 
 
     if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) == INVALID_SOCKET) {
     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))) {
     } 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",
             mg_cry(fc(ctx), "%s: getsockname() failed: %s",
                    __func__, strerror(ERRNO));
                    __func__, strerror(ERRNO));
         }
         }
+
         /* Set TCP keep-alive. This is needed because if HTTP-level keep-alive
         /* Set TCP keep-alive. This is needed because if HTTP-level keep-alive
            is enabled, and client resets the connection, server won't get
            is enabled, and client resets the connection, server won't get
            TCP FIN or RST and will keep the connection open forever. With TCP
            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",
                    "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s",
                    __func__, strerror(ERRNO));
                    __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);
         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;
     int i, len1, len2, port;
     struct mg_connection *conn;
     struct mg_connection *conn;
     struct mg_context *ctx;
     struct mg_context *ctx;
+    struct mg_request_info *ri;
+
     if (use_ssl) port = atoi(HTTPS_PORT); else port = atoi(HTTP_PORT);
     if (use_ssl) port = atoi(HTTPS_PORT); else port = atoi(HTTP_PORT);
 
 
     ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
     ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
@@ -527,6 +529,30 @@ static void test_mg_download(int use_ssl) {
         mg_close_connection(conn);
         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);
     mg_stop(ctx);
 }
 }