summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--http_fetcher_unittest.cc36
-rw-r--r--libcurl_http_fetcher.cc8
-rw-r--r--test_http_server.cc9
3 files changed, 50 insertions, 3 deletions
diff --git a/http_fetcher_unittest.cc b/http_fetcher_unittest.cc
index 33cedf2b..f3a9c3ef 100644
--- a/http_fetcher_unittest.cc
+++ b/http_fetcher_unittest.cc
@@ -711,6 +711,42 @@ TYPED_TEST(HttpFetcherTest, FailureTest) {
}
}
+TYPED_TEST(HttpFetcherTest, NoResponseTest) {
+ // This test starts a new http server but the server doesn't respond and just
+ // closes the connection.
+ if (this->test_.IsMock())
+ return;
+
+ PythonHttpServer* server = new PythonHttpServer();
+ int port = server->GetPort();
+ ASSERT_TRUE(server->started_);
+
+ // Handles destruction and claims ownership.
+ FailureHttpFetcherTestDelegate delegate(server);
+ unique_ptr<HttpFetcher> fetcher(this->test_.NewSmallFetcher());
+ fetcher->set_delegate(&delegate);
+ // The server will not reply at all, so we can limit the execution time of the
+ // test by reducing the low-speed timeout to something small. The test will
+ // finish once the TimeoutCallback() triggers (every second) and the timeout
+ // expired.
+ fetcher->set_low_speed_limit(kDownloadLowSpeedLimitBps, 1);
+
+ this->loop_.PostTask(FROM_HERE, base::Bind(
+ StartTransfer,
+ fetcher.get(),
+ LocalServerUrlForPath(port, "/hang")));
+ this->loop_.Run();
+
+ // Check that no other callback runs in the next two seconds. That would
+ // indicate a leaked callback.
+ bool timeout = false;
+ auto callback = base::Bind([&timeout]{ timeout = true;});
+ this->loop_.PostDelayedTask(FROM_HERE, callback,
+ base::TimeDelta::FromSeconds(2));
+ EXPECT_TRUE(this->loop_.RunOnce(true));
+ EXPECT_TRUE(timeout);
+}
+
TYPED_TEST(HttpFetcherTest, ServerDiesTest) {
// This test starts a new http server and kills it after receiving its first
// set of bytes. It test whether or not our fetcher eventually gives up on
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index e8a8c4b5..5b6c6050 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -514,9 +514,6 @@ void LibcurlHttpFetcher::RetryTimeoutCallback() {
}
void LibcurlHttpFetcher::TimeoutCallback() {
- if (transfer_in_progress_)
- CurlPerformOnce();
-
// We always re-schedule the callback, even if we don't want to be called
// anymore. We will remove the event source separately if we don't want to
// be called back.
@@ -524,6 +521,11 @@ void LibcurlHttpFetcher::TimeoutCallback() {
FROM_HERE,
base::Bind(&LibcurlHttpFetcher::TimeoutCallback, base::Unretained(this)),
TimeDelta::FromSeconds(idle_seconds_));
+
+ // CurlPerformOnce() may call CleanUp(), so we need to schedule our callback
+ // first, since it could be canceled by this call.
+ if (transfer_in_progress_)
+ CurlPerformOnce();
}
void LibcurlHttpFetcher::CleanUp() {
diff --git a/test_http_server.cc b/test_http_server.cc
index f4190c30..7326eb53 100644
--- a/test_http_server.cc
+++ b/test_http_server.cc
@@ -41,6 +41,7 @@
#include <vector>
#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
@@ -450,6 +451,12 @@ ssize_t HandleErrorIfOffset(int fd, const HttpRequest& request,
}
}
+void HandleHang(int fd) {
+ LOG(INFO) << "Hanging until the other side of the connection is closed.";
+ char c;
+ while (HANDLE_EINTR(read(fd, &c, 1)) > 0) {}
+}
+
void HandleDefault(int fd, const HttpRequest& request) {
const off_t start_offset = request.start_offset;
const string data("unhandled path");
@@ -517,6 +524,8 @@ void HandleConnection(int fd) {
} else if (base::StartsWithASCII(url, "/error-if-offset/", true)) {
const UrlTerms terms(url, 3);
HandleErrorIfOffset(fd, request, terms.GetSizeT(1), terms.GetInt(2));
+ } else if (url == "/hang") {
+ HandleHang(fd);
} else {
HandleDefault(fd, request);
}