Fetch local files asynchronously.
This patch implements a new fetcher that only handles local files.
While libcurl supports file:// urls, the stream can't be suspended when
accessing local files.
This new FileFetcher is based on the brillo::FileStream class which
properly handles the asynchronous reads from regular files.
Bug: 28866512
TEST=Added unittest. Deployed an update from a file:// URL.
(cherry picked from commit 2c131bbf81d8c02ade163b939c96e44aa93765e9)
Change-Id: I9949a0f214de992c2fd86c1d73aca1c1792f0de0
diff --git a/common/http_fetcher_unittest.cc b/common/http_fetcher_unittest.cc
index 0d4b5da..9bc4373 100644
--- a/common/http_fetcher_unittest.cc
+++ b/common/http_fetcher_unittest.cc
@@ -42,6 +42,7 @@
#include <gtest/gtest.h>
#include "update_engine/common/fake_hardware.h"
+#include "update_engine/common/file_fetcher.h"
#include "update_engine/common/http_common.h"
#include "update_engine/common/libcurl_http_fetcher.h"
#include "update_engine/common/mock_http_fetcher.h"
@@ -219,6 +220,7 @@
virtual bool IsMock() const = 0;
virtual bool IsMulti() const = 0;
+ virtual bool IsHttpSupported() const = 0;
virtual void IgnoreServerAborting(HttpServer* server) const {}
@@ -251,6 +253,7 @@
bool IsMock() const override { return true; }
bool IsMulti() const override { return false; }
+ bool IsHttpSupported() const override { return true; }
HttpServer* CreateServer() override {
return new NullHttpServer;
@@ -291,6 +294,7 @@
bool IsMock() const override { return false; }
bool IsMulti() const override { return false; }
+ bool IsHttpSupported() const override { return true; }
void IgnoreServerAborting(HttpServer* server) const override {
// Nothing to do.
@@ -326,6 +330,42 @@
bool IsMulti() const override { return true; }
};
+class FileFetcherTest : public AnyHttpFetcherTest {
+ public:
+ // Necessary to unhide the definition in the base class.
+ using AnyHttpFetcherTest::NewLargeFetcher;
+ HttpFetcher* NewLargeFetcher(ProxyResolver* /* proxy_resolver */) override {
+ return new FileFetcher();
+ }
+
+ // Necessary to unhide the definition in the base class.
+ using AnyHttpFetcherTest::NewSmallFetcher;
+ HttpFetcher* NewSmallFetcher(ProxyResolver* proxy_resolver) override {
+ return NewLargeFetcher(proxy_resolver);
+ }
+
+ string BigUrl(in_port_t port) const override {
+ return "file://" + temp_file_.path();
+ }
+ string SmallUrl(in_port_t port) const override {
+ test_utils::WriteFileString(temp_file_.path(), "small contents");
+ return "file://" + temp_file_.path();
+ }
+ string ErrorUrl(in_port_t port) const override {
+ return "file:///path/to/non-existing-file";
+ }
+
+ bool IsMock() const override { return false; }
+ bool IsMulti() const override { return false; }
+ bool IsHttpSupported() const override { return false; }
+
+ void IgnoreServerAborting(HttpServer* server) const override {}
+
+ HttpServer* CreateServer() override { return new NullHttpServer; }
+
+ private:
+ test_utils::ScopedTempFile temp_file_{"ue_file_fetcher.XXXXXX"};
+};
//
// Infrastructure for type tests of HTTP fetcher.
@@ -363,7 +403,9 @@
// Test case types list.
typedef ::testing::Types<LibcurlHttpFetcherTest,
MockHttpFetcherTest,
- MultiRangeHttpFetcherTest> HttpFetcherTestTypes;
+ MultiRangeHttpFetcherTest,
+ FileFetcherTest>
+ HttpFetcherTestTypes;
TYPED_TEST_CASE(HttpFetcherTest, HttpFetcherTestTypes);
@@ -394,6 +436,7 @@
void TransferTerminated(HttpFetcher* fetcher) override {
ADD_FAILURE();
times_transfer_terminated_called_++;
+ MessageLoop::current()->BreakLoop();
}
// Are we expecting an error response? (default: no)
@@ -477,7 +520,7 @@
}
TYPED_TEST(HttpFetcherTest, ExtraHeadersInRequestTest) {
- if (this->test_.IsMock())
+ if (this->test_.IsMock() || !this->test_.IsHttpSupported())
return;
HttpFetcherTestDelegate delegate;
@@ -498,7 +541,11 @@
int port = server.GetPort();
ASSERT_TRUE(server.started_);
- StartTransfer(fetcher.get(), LocalServerUrlForPath(port, "/echo-headers"));
+ this->loop_.PostTask(
+ FROM_HERE,
+ base::Bind(StartTransfer,
+ fetcher.get(),
+ LocalServerUrlForPath(port, "/echo-headers")));
this->loop_.Run();
EXPECT_NE(string::npos,
@@ -571,7 +618,7 @@
// This test will pause the fetcher while the download is not yet started
// because it is waiting for the proxy to be resolved.
TYPED_TEST(HttpFetcherTest, PauseWhileResolvingProxyTest) {
- if (this->test_.IsMock())
+ if (this->test_.IsMock() || !this->test_.IsHttpSupported())
return;
MockProxyResolver mock_resolver;
unique_ptr<HttpFetcher> fetcher(this->test_.NewLargeFetcher(&mock_resolver));
@@ -684,7 +731,7 @@
} // namespace
TYPED_TEST(HttpFetcherTest, FlakyTest) {
- if (this->test_.IsMock())
+ if (this->test_.IsMock() || !this->test_.IsHttpSupported())
return;
{
FlakyHttpFetcherTestDelegate delegate;
@@ -897,7 +944,7 @@
} // namespace
TYPED_TEST(HttpFetcherTest, SimpleRedirectTest) {
- if (this->test_.IsMock())
+ if (this->test_.IsMock() || !this->test_.IsHttpSupported())
return;
unique_ptr<HttpServer> server(this->test_.CreateServer());
@@ -912,7 +959,7 @@
}
TYPED_TEST(HttpFetcherTest, MaxRedirectTest) {
- if (this->test_.IsMock())
+ if (this->test_.IsMock() || !this->test_.IsHttpSupported())
return;
unique_ptr<HttpServer> server(this->test_.CreateServer());
@@ -928,7 +975,7 @@
}
TYPED_TEST(HttpFetcherTest, BeyondMaxRedirectTest) {
- if (this->test_.IsMock())
+ if (this->test_.IsMock() || !this->test_.IsHttpSupported())
return;
unique_ptr<HttpServer> server(this->test_.CreateServer());