diff --git a/examples/http-streaming-client/main.c b/examples/http-streaming-client/main.c index 96e18f65..eb72363c 100644 --- a/examples/http-streaming-client/main.c +++ b/examples/http-streaming-client/main.c @@ -32,7 +32,9 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { // Send request mg_printf(c, - "GET %s HTTP/1.0\r\n" + "GET %s HTTP/1.1\r\n" + "Connection: keep-alive\r\n" + "Keep-Alive: timeout=60\r\n" "Host: %.*s\r\n" "\r\n", mg_url_uri(s_url), (int) host.len, host.ptr); diff --git a/mongoose.c b/mongoose.c index 0e228959..594c622b 100644 --- a/mongoose.c +++ b/mongoose.c @@ -2724,7 +2724,7 @@ struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url, c->is_client = true; c->fd = (void *) (size_t) -1; // Set to invalid socket c->fn_data = fn_data; - MG_DEBUG(("%lu %p %s", c->id, c->fd, url)); + MG_DEBUG(("%lu -1 %s", c->id, url)); mg_call(c, MG_EV_OPEN, NULL); mg_resolve(c, url); } @@ -3191,7 +3191,7 @@ static void iolog(struct mg_connection *c, char *buf, long n, bool r) { if (n == 0) { // Do nothing } else if (n < 0) { - c->is_closing = 1; // Error, or normal termination + mg_error(c, "IO error"); } else if (n > 0) { if (c->is_hexdumping) { union usa usa; @@ -3571,7 +3571,7 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE); } #else - struct timeval tv = {ms / 1000, (ms % 1000) * 1000}; + struct timeval tv = {ms / 1000, (ms % 1000) * 1000}, tv_zero = {0, 0}; struct mg_connection *c; fd_set rset, wset; SOCKET maxfd = 0; @@ -3586,6 +3586,7 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { if (FD(c) > maxfd) maxfd = FD(c); if (c->is_connecting || (c->send.len > 0 && c->is_tls_hs == 0)) FD_SET(FD(c), &wset); + if (mg_tls_pending(c) > 0) tv = tv_zero; } if ((rc = select((int) maxfd + 1, &rset, &wset, NULL, &tv)) < 0) { @@ -3597,6 +3598,7 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { for (c = mgr->conns; c != NULL; c = c->next) { c->is_readable = FD(c) != INVALID_SOCKET && FD_ISSET(FD(c), &rset); c->is_writable = FD(c) != INVALID_SOCKET && FD_ISSET(FD(c), &wset); + if (mg_tls_pending(c) > 0) c->is_readable = 1; } #endif } @@ -4208,6 +4210,10 @@ long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { return c == NULL || buf == NULL || len == 0 ? 0 : -1; } +size_t mg_tls_pending(struct mg_connection *c) { + (void) c; + return 0; +} #endif #ifdef MG_ENABLE_LINES @@ -4405,6 +4411,11 @@ fail: mg_tls_free(c); } +size_t mg_tls_pending(struct mg_connection *c) { + struct mg_tls *tls = (struct mg_tls *) c->tls; + return tls == NULL ? 0 : mbedtls_ssl_get_bytes_avail(&tls->ssl); +} + long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { struct mg_tls *tls = (struct mg_tls *) c->tls; long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len); @@ -4564,6 +4575,11 @@ void mg_tls_free(struct mg_connection *c) { c->tls = NULL; } +size_t mg_tls_pending(struct mg_connection *c) { + struct mg_tls *tls = (struct mg_tls *) c->tls; + return tls == NULL ? 0 : SSL_pending(tls->ssl); +} + long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { struct mg_tls *tls = (struct mg_tls *) c->tls; int n = SSL_read(tls->ssl, buf, (int) len); diff --git a/mongoose.h b/mongoose.h index e3c910b8..d04510e8 100644 --- a/mongoose.h +++ b/mongoose.h @@ -1053,6 +1053,7 @@ void mg_tls_init(struct mg_connection *, const struct mg_tls_opts *); void mg_tls_free(struct mg_connection *); long mg_tls_send(struct mg_connection *, const void *buf, size_t len); long mg_tls_recv(struct mg_connection *, void *buf, size_t len); +size_t mg_tls_pending(struct mg_connection *); void mg_tls_handshake(struct mg_connection *); diff --git a/src/net.c b/src/net.c index 3c76d4d7..41679bb2 100644 --- a/src/net.c +++ b/src/net.c @@ -178,7 +178,7 @@ struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url, c->is_client = true; c->fd = (void *) (size_t) -1; // Set to invalid socket c->fn_data = fn_data; - MG_DEBUG(("%lu %p %s", c->id, c->fd, url)); + MG_DEBUG(("%lu -1 %s", c->id, url)); mg_call(c, MG_EV_OPEN, NULL); mg_resolve(c, url); } diff --git a/src/sock.c b/src/sock.c index d9569a6d..45262463 100644 --- a/src/sock.c +++ b/src/sock.c @@ -102,7 +102,7 @@ static void iolog(struct mg_connection *c, char *buf, long n, bool r) { if (n == 0) { // Do nothing } else if (n < 0) { - c->is_closing = 1; // Error, or normal termination + mg_error(c, "IO error"); } else if (n > 0) { if (c->is_hexdumping) { union usa usa; @@ -482,7 +482,7 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE); } #else - struct timeval tv = {ms / 1000, (ms % 1000) * 1000}; + struct timeval tv = {ms / 1000, (ms % 1000) * 1000}, tv_zero = {0, 0}; struct mg_connection *c; fd_set rset, wset; SOCKET maxfd = 0; @@ -497,6 +497,7 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { if (FD(c) > maxfd) maxfd = FD(c); if (c->is_connecting || (c->send.len > 0 && c->is_tls_hs == 0)) FD_SET(FD(c), &wset); + if (mg_tls_pending(c) > 0) tv = tv_zero; } if ((rc = select((int) maxfd + 1, &rset, &wset, NULL, &tv)) < 0) { @@ -508,6 +509,7 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { for (c = mgr->conns; c != NULL; c = c->next) { c->is_readable = FD(c) != INVALID_SOCKET && FD_ISSET(FD(c), &rset); c->is_writable = FD(c) != INVALID_SOCKET && FD_ISSET(FD(c), &wset); + if (mg_tls_pending(c) > 0) c->is_readable = 1; } #endif } diff --git a/src/tls.h b/src/tls.h index 3cb7ece2..5f8d299c 100644 --- a/src/tls.h +++ b/src/tls.h @@ -18,4 +18,5 @@ void mg_tls_init(struct mg_connection *, const struct mg_tls_opts *); void mg_tls_free(struct mg_connection *); long mg_tls_send(struct mg_connection *, const void *buf, size_t len); long mg_tls_recv(struct mg_connection *, void *buf, size_t len); +size_t mg_tls_pending(struct mg_connection *); void mg_tls_handshake(struct mg_connection *); diff --git a/src/tls_dummy.c b/src/tls_dummy.c index edb87e1c..c0067283 100644 --- a/src/tls_dummy.c +++ b/src/tls_dummy.c @@ -17,4 +17,8 @@ long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { return c == NULL || buf == NULL || len == 0 ? 0 : -1; } +size_t mg_tls_pending(struct mg_connection *c) { + (void) c; + return 0; +} #endif diff --git a/src/tls_mbed.c b/src/tls_mbed.c index 32f955cc..f6923616 100644 --- a/src/tls_mbed.c +++ b/src/tls_mbed.c @@ -190,6 +190,11 @@ fail: mg_tls_free(c); } +size_t mg_tls_pending(struct mg_connection *c) { + struct mg_tls *tls = (struct mg_tls *) c->tls; + return tls == NULL ? 0 : mbedtls_ssl_get_bytes_avail(&tls->ssl); +} + long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { struct mg_tls *tls = (struct mg_tls *) c->tls; long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len); diff --git a/src/tls_openssl.c b/src/tls_openssl.c index e22b0e08..2c482a3e 100644 --- a/src/tls_openssl.c +++ b/src/tls_openssl.c @@ -141,6 +141,11 @@ void mg_tls_free(struct mg_connection *c) { c->tls = NULL; } +size_t mg_tls_pending(struct mg_connection *c) { + struct mg_tls *tls = (struct mg_tls *) c->tls; + return tls == NULL ? 0 : SSL_pending(tls->ssl); +} + long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { struct mg_tls *tls = (struct mg_tls *) c->tls; int n = SSL_read(tls->ssl, buf, (int) len); diff --git a/test/data/ca.pem b/test/data/ca.pem index 29765545..65009791 100644 --- a/test/data/ca.pem +++ b/test/data/ca.pem @@ -281,30 +281,22 @@ DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== -----END CERTIFICATE----- -Subject: OU=GlobalSign Root CA - R2, O=GlobalSign, CN=GlobalSign -Not Before: Dec 15 08:00:00 2006 GMT -Not After : Dec 15 08:00:00 2021 GMT +Subject: OU = GlobalSign ECC Root CA - R5, O = GlobalSign, CN = GlobalSign +Not Before: Nov 13 00:00:00 2012 GMT +Not After : Jan 19 03:14:07 2038 GMT -----BEGIN CERTIFICATE----- -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 +xwy8p2Fp8fc74SrL+SvzZpA3 -----END CERTIFICATE----- Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA @@ -541,3 +533,4 @@ AwEB/zAdBgNVHQ4EFgQUPv7/zFLrvzQ+PfNA0OQlsV+4u1IwCgYIKoZIzj0EAwID SAAwRQIhAPKuf/VtBHqGw3TUwUIq7TfaExp3bH7bjCBmVXJupT9FAiBr0SmCtsuk miGgpajjf/gFigGM34F9021bCWs1MbL0SA== -----END CERTIFICATE----- + diff --git a/test/unit_test.c b/test/unit_test.c index 8748cee7..7e309c07 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -833,7 +833,8 @@ static void f3(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { // MG_INFO(("%d", ev)); if (ev == MG_EV_CONNECT) { // c->is_hexdumping = 1; - mg_printf(c, "GET / HTTP/1.0\r\nHost: %s\r\n\r\n", + mg_printf(c, "GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", + c->rem.is_ip6 ? "" : "/robots.txt", c->rem.is_ip6 ? "ipv6.google.com" : "cesanta.com"); } else if (ev == MG_EV_HTTP_MSG) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; @@ -868,8 +869,10 @@ static void test_http_client(void) { c = mg_http_connect(&mgr, url, f3, &ok); ASSERT(c != NULL); mg_tls_init(c, &opts); - for (i = 0; i < 500 && ok <= 0; i++) mg_mgr_poll(&mgr, 10); + for (i = 0; i < 1500 && ok <= 0; i++) mg_mgr_poll(&mgr, 1000); ASSERT(ok == 200); + c->is_closing = 1; + mg_mgr_poll(&mgr, 1); // Test failed host validation ok = 0; @@ -879,6 +882,7 @@ static void test_http_client(void) { mg_tls_init(c, &opts); for (i = 0; i < 500 && ok <= 0; i++) mg_mgr_poll(&mgr, 10); ASSERT(ok == 777); + mg_mgr_poll(&mgr, 1); } #endif @@ -887,7 +891,6 @@ static void test_http_client(void) { // ipv6.google.com does not have IPv4 address, only IPv6, therefore // it is guaranteed to hit IPv6 resolution path. c = mg_http_connect(&mgr, "http://ipv6.google.com", f3, &ok); - ASSERT(c != NULL); for (i = 0; i < 500 && ok <= 0; i++) mg_mgr_poll(&mgr, 10); ASSERT(ok == 200); #endif