Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(293)

Unified Diff: content/browser/download/download_browsertest.cc

Issue 246893006: [Downloads] Clear suggested filename on cross origin redirect. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | content/browser/download/download_resource_handler.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/download/download_browsertest.cc
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index 276f3c4587cd4588cec75db5199cfa1b60dba8e0..4045f33041bc788d892be7db85743d5bab6991b7 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -5,6 +5,8 @@
// This file contains download browser tests that are known to be runnable
// in a pure content context. Over time tests should be migrated here.
+#include <queue>
+
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
@@ -36,6 +38,9 @@
#include "content/shell/browser/shell_network_delegate.h"
#include "content/test/net/url_request_mock_http_job.h"
#include "content/test/net/url_request_slow_download_job.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -503,6 +508,176 @@ bool InitialSizeFilter(int* download_size, DownloadItem* download) {
return true;
}
+// RAII test server. Once instantiated, it initializes an embedded test server.
+// Expectations can be set via the OnURL method.
+//
+// Call HasFatalFailure() after constructing a TestServer instance to check if
+// the initialization was successful.
+//
+// Example:
+//
+// TEST_F(Foo, Bar) {
+// TestServer test_server;
+// if (HasFatalFailure())
+// return;
+//
+// test_server.OnURL("/foo").RedirectTo("/bar");
+// test_server.OnURL("/bar").SendBody("baz");
+// ...
+// test_server.ServeFilesFromDirectory(test_file_path);
+// ...
+// // Do something that fetches "/foo".
+// }
+//
+// Note that the order of the OnURL() calls are significant. The server expects
+// the requests to arrive in the same order as the OnURL() calls.
+// TODO(asanka): Consider migrating more tests to use the embedded test server.
Randy Smith (Not in Mondays) 2014/04/23 19:12:27 I'd encourage this; this type of infrastructure lo
asanka 2014/04/23 22:59:27 I actually removed this TestServer since in the en
+class TestServer {
+ public:
+ // An expectation for a request. Created by TestServer::OnURL().
+ class ExpectedRequest {
+ public:
+ // Respond with a status code of 302 and a Location header set to
+ // |redirect_url|.
+ ExpectedRequest& RedirectTo(const GURL& redirect_url);
+
+ // Set the body of the response to |string|. Overwrites the current body if
+ // one had been previously set.
+ ExpectedRequest& SendBody(const std::string& string);
Randy Smith (Not in Mondays) 2014/04/23 19:12:27 Should you say what happens if you use both Redire
asanka 2014/04/23 22:59:27 Removed.
+
+ // Set the Content-Type to |mime_type|.
+ ExpectedRequest& WithContentType(const char* mime_type);
+
+ private:
+ friend class TestServer;
+
+ // Construct a ExpectedRequest that will expect an HTTP GET for
+ // |relative_url|.
+ ExpectedRequest(const std::string& relative_url);
+
+ // Returns true if this expectation matches |url|.
+ bool MatchesUrl(const std::string& url) const;
+
+ // Returns the HttpResponse for this expectation. Once this is called, the
+ // ExpectedRequest object is no longer valid.
+ scoped_ptr<net::test_server::HttpResponse> GetResponse();
+
+ std::string relative_url_;
+ scoped_ptr<net::test_server::BasicHttpResponse> response_;
+ DISALLOW_COPY_AND_ASSIGN(ExpectedRequest);
+ };
+
+ TestServer();
+ ~TestServer();
+
+ // Create a new expectation for receiving an HTTP GET for |relative_url|. By
+ // default the response is an HTTP 200 with an empty body. The response can be
+ // modified via the returned |ExpectedRequest| reference. Note that the
+ // reference becomes invalidated once a request has been received by the
+ // server.
+ ExpectedRequest& OnURL(const std::string& relative_url);
Randy Smith (Not in Mondays) 2014/04/23 19:12:27 Huh. I twitch somewhat hard at returning a non-co
asanka 2014/04/23 22:59:27 Hehe. It's a builder pattern. See for example htt
+
+ // Get the absolue URL for |relative_url|.
+ GURL GetURL(const std::string& relative_url);
+
+ // Serve files contained within |directory|. See
+ // EmbeddedTestServer::ServeFilesFromDirectory().
+ void ServeFilesFromDirectory(const base::FilePath& directory);
Randy Smith (Not in Mondays) 2014/04/23 19:12:27 Probably worthwhile calling out that any ExpectedR
asanka 2014/04/23 22:59:27 Removed.
+
+ private:
+ scoped_ptr<net::test_server::HttpResponse> HandleRequest(
+ const net::test_server::HttpRequest& request);
+ void InitializeAndWaitUntilReady();
+ void ShutdownAndWaitUntilComplete();
+
+ net::test_server::EmbeddedTestServer embedded_test_server_;
+ std::queue<ExpectedRequest*> expected_requests_;
+ DISALLOW_COPY_AND_ASSIGN(TestServer);
+};
+
+TestServer::ExpectedRequest::ExpectedRequest(const std::string& relative_url)
+ : relative_url_(relative_url),
+ response_(new net::test_server::BasicHttpResponse) {
+}
+
+TestServer::ExpectedRequest& TestServer::ExpectedRequest::RedirectTo(
+ const GURL& redirect_url) {
+ response_->set_code(net::HTTP_FOUND);
+ response_->AddCustomHeader("Location", redirect_url.spec());
+ return *this;
+}
+
+TestServer::ExpectedRequest& TestServer::ExpectedRequest::SendBody(
+ const std::string& string) {
+ response_->set_content(string);
+ return *this;
+}
+
+TestServer::ExpectedRequest& TestServer::ExpectedRequest::WithContentType(
+ const char* mime_type) {
+ response_->set_content_type(mime_type);
+ return *this;
+}
+
+bool TestServer::ExpectedRequest::MatchesUrl(const std::string& url) const {
+ return relative_url_ == url;
+}
+
+scoped_ptr<net::test_server::HttpResponse>
+TestServer::ExpectedRequest::GetResponse() {
+ return response_.PassAs<net::test_server::HttpResponse>();
+}
+
+TestServer::TestServer() {
+ InitializeAndWaitUntilReady();
+}
+
+TestServer::~TestServer() {
+ EXPECT_TRUE(expected_requests_.empty());
+ while (!expected_requests_.empty()) {
+ delete expected_requests_.front();
+ expected_requests_.pop();
+ }
+ ShutdownAndWaitUntilComplete();
+}
+
+void TestServer::InitializeAndWaitUntilReady() {
+ ASSERT_TRUE(embedded_test_server_.InitializeAndWaitUntilReady());
+ embedded_test_server_.RegisterRequestHandler(
+ base::Bind(&TestServer::HandleRequest, base::Unretained(this)));
+}
+
+void TestServer::ShutdownAndWaitUntilComplete() {
+ ASSERT_TRUE(embedded_test_server_.ShutdownAndWaitUntilComplete());
+}
+
+TestServer::ExpectedRequest& TestServer::OnURL(
+ const std::string& relative_url) {
+ ExpectedRequest* request = new ExpectedRequest(relative_url);
+ expected_requests_.push(request);
+ return *request;
+}
+
+void TestServer::ServeFilesFromDirectory(const base::FilePath& directory) {
+ embedded_test_server_.ServeFilesFromDirectory(directory);
+}
+
+GURL TestServer::GetURL(const std::string& relative_url) {
+ return embedded_test_server_.GetURL(relative_url);
+}
+
+scoped_ptr<net::test_server::HttpResponse> TestServer::HandleRequest(
+ const net::test_server::HttpRequest& request) {
+ scoped_ptr<net::test_server::HttpResponse> response;
+ if (!expected_requests_.empty() &&
+ expected_requests_.front()->MatchesUrl(request.relative_url)) {
+ scoped_ptr<ExpectedRequest> expected_request(expected_requests_.front());
+ response = expected_request->GetResponse();
+ expected_requests_.pop();
+ }
+ return response.Pass();
+}
+
} // namespace
class DownloadContentTest : public ContentBrowserTest {
@@ -1651,4 +1826,87 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, CookiePolicy) {
GURL(download)));
}
+// A filename suggestion specified via a @download attribute should be effective
+// if the final download URL is in the same origin as the initial download URL.
+// Test that this holds even if there are cross origin redirects in the middle
+// of the redirect chain.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest,
+ DownloadAttributeSameOriginRedirect) {
+ TestServer origin_one;
+ TestServer origin_two;
+ if (HasFatalFailure())
+ return;
+
+ // The download-attribute.html page contains an anchor element whose href is
+ // set to the value of the query parameter (specified as |target| in the URL
+ // below). The suggested filename for the anchor is 'suggested-filename'. When
+ // the page is loaded, a script simulates a click on the anchor, triggering a
+ // download of the target URL.
+ //
+ // We construct two test servers; origin_one and origin_two. Once started, the
+ // server URLs will differ by the port number. Therefore they will be in
+ // different origins.
+ GURL download_url = origin_one.GetURL("/ping");
+ GURL referrer_url = origin_one.GetURL(
+ std::string("/download-attribute.html?target=") + download_url.spec());
+ origin_one.ServeFilesFromDirectory(GetTestFilePath("download", ""));
+
+ // <origin_one>/download-attribute.html initiates a download of
+ // <origin_one>/ping, which redirects to <origin_two>/pong, and then finally
+ // to <origin_one>/download.
+ origin_one.OnURL("/ping").RedirectTo(origin_two.GetURL("/pong"));
+ origin_two.OnURL("/pong").RedirectTo(origin_one.GetURL("/download"));
+ origin_one.OnURL("/download").SendBody("Hello").WithContentType(
+ "application/octet-stream");
+
+ DownloadAndWait(shell(), referrer_url, DownloadItem::COMPLETE);
Randy Smith (Not in Mondays) 2014/04/23 19:12:27 nit: Chuckle. This relies on "DownloadAndWait()"
asanka 2014/04/23 22:59:27 I renamed the function to NavigateToURLAndWaitForD
+
+ std::vector<DownloadItem*> downloads;
+ DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
+ ASSERT_EQ(1u, downloads.size());
+
+ EXPECT_EQ(FILE_PATH_LITERAL("suggested-filename"),
+ downloads[0]->GetTargetFilePath().BaseName().value());
+}
+
+// A filename suggestion specified via a @download attribute should not be
+// effective if the final download URL is in another origin from the original
+// download URL.
Randy Smith (Not in Mondays) 2014/04/23 19:12:27 nit: I'd interchange the order of these two tests;
asanka 2014/04/23 22:59:27 Done.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest,
+ DownloadAttributeCrossOriginRedirect) {
+ TestServer origin_one;
+ TestServer origin_two;
+ if (HasFatalFailure())
+ return;
+
+ // The download-attribute.html page contains an anchor element whose href is
+ // set to the value of the query parameter (specified as |target| in the URL
+ // below). The suggested filename for the anchor is 'suggested-filename'. When
+ // the page is loaded, a script simulates a click on the anchor, triggering a
+ // download of the target URL.
+ //
+ // We construct two test servers; origin_one and origin_two. Once started, the
+ // server URLs will differ by the port number. Therefore they will be in
+ // different origins.
+ GURL download_url = origin_one.GetURL("/ping");
+ GURL referrer_url = origin_one.GetURL(
+ std::string("/download-attribute.html?target=") + download_url.spec());
+
+ // <origin_one>/download-attribute.html initiates a download of
+ // <origin_one>/ping, which redirects to <origin_two>/download.
+ origin_one.ServeFilesFromDirectory(GetTestFilePath("download", ""));
+ origin_one.OnURL("/ping").RedirectTo(origin_two.GetURL("/download"));
+ origin_two.OnURL("/download").SendBody("Hello").WithContentType(
+ "application/octet-stream");
+
+ DownloadAndWait(shell(), referrer_url, DownloadItem::COMPLETE);
+
+ std::vector<DownloadItem*> downloads;
+ DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
+ ASSERT_EQ(1u, downloads.size());
+
+ EXPECT_EQ(FILE_PATH_LITERAL("download"),
+ downloads[0]->GetTargetFilePath().BaseName().value());
+}
+
} // namespace content
« no previous file with comments | « no previous file | content/browser/download/download_resource_handler.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698