Skip to content

Commit

Permalink
[ServiceWorker] Fills SSLInfo of the response from a SW with the SSLI…
Browse files Browse the repository at this point in the history
…nfo of the SW script.

After https://codereview.chromium.org/874833003/, every mixed-content requests from the ServiceWorker are blocked.
So we can show the HTTPS padlock because every responses from the ServiceWorker to the page are created using the resources which are served through the secure protocol.

To show the padlock we fill the SSLInfo of the response from the ServiceWorker using the SSLInfo of the ServiceWorker script.

When we support fetching mixed-content requests from the ServiceWorker in the future, we have to check the security level of the responses.

BUG=392409

Review URL: https://codereview.chromium.org/877623002

Cr-Commit-Position: refs/heads/master@{#314549}
  • Loading branch information
horo-t authored and Commit bot committed Feb 4, 2015
1 parent e63cb64 commit 4bd0e01
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 43 deletions.
146 changes: 107 additions & 39 deletions content/browser/service_worker/service_worker_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@
#include "content/common/service_worker/service_worker_types.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/referrer.h"
#include "content/public/common/security_style.h"
#include "content/public/common/ssl_status.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
Expand Down Expand Up @@ -766,45 +769,110 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, SyncEventHandled) {
}

IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, Reload) {
const std::string kPageUrl = "/service_worker/reload.html";
const std::string kWorkerUrl = "/service_worker/fetch_event_reload.js";
{
scoped_refptr<WorkerActivatedObserver> observer =
new WorkerActivatedObserver(wrapper());
observer->Init();
public_context()->RegisterServiceWorker(
embedded_test_server()->GetURL(kPageUrl),
embedded_test_server()->GetURL(kWorkerUrl),
base::Bind(&ExpectResultAndRun, true, base::Bind(&base::DoNothing)));
observer->Wait();
}
{
const base::string16 title = base::ASCIIToUTF16("reload=false");
TitleWatcher title_watcher(shell()->web_contents(), title);
NavigateToURL(shell(), embedded_test_server()->GetURL(kPageUrl));
EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
}
{
const base::string16 title = base::ASCIIToUTF16("reload=true");
TitleWatcher title_watcher(shell()->web_contents(), title);
ReloadBlockUntilNavigationsComplete(shell(), 1);
EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
}
const char kPageUrl[] = "/service_worker/reload.html";
const char kWorkerUrl[] = "/service_worker/fetch_event_reload.js";
scoped_refptr<WorkerActivatedObserver> observer =
new WorkerActivatedObserver(wrapper());
observer->Init();
public_context()->RegisterServiceWorker(
embedded_test_server()->GetURL(kPageUrl),
embedded_test_server()->GetURL(kWorkerUrl),
base::Bind(&ExpectResultAndRun, true, base::Bind(&base::DoNothing)));
observer->Wait();

const base::string16 title1 = base::ASCIIToUTF16("reload=false");
TitleWatcher title_watcher1(shell()->web_contents(), title1);
NavigateToURL(shell(), embedded_test_server()->GetURL(kPageUrl));
EXPECT_EQ(title1, title_watcher1.WaitAndGetTitle());

const base::string16 title2 = base::ASCIIToUTF16("reload=true");
TitleWatcher title_watcher2(shell()->web_contents(), title2);
ReloadBlockUntilNavigationsComplete(shell(), 1);
EXPECT_EQ(title2, title_watcher2.WaitAndGetTitle());

shell()->Close();
{
base::RunLoop run_loop;
public_context()->UnregisterServiceWorker(
embedded_test_server()->GetURL(kPageUrl),
base::Bind(&ExpectResultAndRun, true, run_loop.QuitClosure()));
run_loop.Run();
}

base::RunLoop run_loop;
public_context()->UnregisterServiceWorker(
embedded_test_server()->GetURL(kPageUrl),
base::Bind(&ExpectResultAndRun, true, run_loop.QuitClosure()));
run_loop.Run();
}

IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest,
ResponseFromHTTPSServiceWorkerIsMarkedAsSecure) {
const char kPageUrl[] = "files/service_worker/fetch_event_blob.html";
const char kWorkerUrl[] = "files/service_worker/fetch_event_blob.js";
net::SpawnedTestServer https_server(
net::SpawnedTestServer::TYPE_HTTPS,
net::BaseTestServer::SSLOptions(
net::BaseTestServer::SSLOptions::CERT_OK),
base::FilePath(FILE_PATH_LITERAL("content/test/data/")));
ASSERT_TRUE(https_server.Start());

scoped_refptr<WorkerActivatedObserver> observer =
new WorkerActivatedObserver(wrapper());
observer->Init();
public_context()->RegisterServiceWorker(
https_server.GetURL(kPageUrl),
https_server.GetURL(kWorkerUrl),
base::Bind(&ExpectResultAndRun, true, base::Bind(&base::DoNothing)));
observer->Wait();

const base::string16 title = base::ASCIIToUTF16("Title");
TitleWatcher title_watcher(shell()->web_contents(), title);
NavigateToURL(shell(), https_server.GetURL(kPageUrl));
EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
EXPECT_FALSE(shell()->web_contents()->DisplayedInsecureContent());
NavigationEntry* entry =
shell()->web_contents()->GetController().GetVisibleEntry();
EXPECT_EQ(SECURITY_STYLE_AUTHENTICATED, entry->GetSSL().security_style);

shell()->Close();

base::RunLoop run_loop;
public_context()->UnregisterServiceWorker(
https_server.GetURL(kPageUrl),
base::Bind(&ExpectResultAndRun, true, run_loop.QuitClosure()));
run_loop.Run();
}

IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest,
ResponseFromHTTPServiceWorkerIsNotMarkedAsSecure) {
const char kPageUrl[] = "/service_worker/fetch_event_blob.html";
const char kWorkerUrl[] = "/service_worker/fetch_event_blob.js";
scoped_refptr<WorkerActivatedObserver> observer =
new WorkerActivatedObserver(wrapper());
observer->Init();
public_context()->RegisterServiceWorker(
embedded_test_server()->GetURL(kPageUrl),
embedded_test_server()->GetURL(kWorkerUrl),
base::Bind(&ExpectResultAndRun, true, base::Bind(&base::DoNothing)));
observer->Wait();

const base::string16 title = base::ASCIIToUTF16("Title");
TitleWatcher title_watcher(shell()->web_contents(), title);
NavigateToURL(shell(), embedded_test_server()->GetURL(kPageUrl));
EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
EXPECT_FALSE(shell()->web_contents()->DisplayedInsecureContent());
NavigationEntry* entry =
shell()->web_contents()->GetController().GetVisibleEntry();
EXPECT_EQ(SECURITY_STYLE_UNAUTHENTICATED, entry->GetSSL().security_style);

shell()->Close();

base::RunLoop run_loop;
public_context()->UnregisterServiceWorker(
embedded_test_server()->GetURL(kPageUrl),
base::Bind(&ExpectResultAndRun, true, run_loop.QuitClosure()));
run_loop.Run();
}

IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, ImportsBustMemcache) {
const std::string kScopeUrl = "/service_worker/imports_bust_memcache_scope/";
const std::string kPageUrl = "/service_worker/imports_bust_memcache.html";
const std::string kScriptUrl = "/service_worker/worker_with_one_import.js";
const std::string kImportUrl = "/service_worker/long_lived_import.js";
const char kScopeUrl[] = "/service_worker/imports_bust_memcache_scope/";
const char kPageUrl[] = "/service_worker/imports_bust_memcache.html";
const char kScriptUrl[] = "/service_worker/worker_with_one_import.js";
const char kImportUrl[] = "/service_worker/long_lived_import.js";
const base::string16 kOKTitle(base::ASCIIToUTF16("OK"));
const base::string16 kFailTitle(base::ASCIIToUTF16("FAIL"));

Expand Down Expand Up @@ -878,8 +946,8 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerBlackBoxBrowserTest, MAYBE_Registration) {
shell()->Close();
EXPECT_EQ(0, CountRenderProcessHosts());

const std::string kWorkerUrl = "/service_worker/fetch_event.js";
const std::string kScope = "/service_worker/";
const char kWorkerUrl[] = "/service_worker/fetch_event.js";
const char kScope[] = "/service_worker/";

// Unregistering nothing should return false.
{
Expand Down Expand Up @@ -953,7 +1021,7 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerBlackBoxBrowserTest, MAYBE_Registration) {

IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, CrossSiteTransfer) {
// The first page registers a service worker.
const std::string kRegisterPageUrl = "/service_worker/cross_site_xfer.html";
const char kRegisterPageUrl[] = "/service_worker/cross_site_xfer.html";
const base::string16 kOKTitle1(base::ASCIIToUTF16("OK_1"));
const base::string16 kFailTitle1(base::ASCIIToUTF16("FAIL_1"));
content::TitleWatcher title_watcher1(shell()->web_contents(), kOKTitle1);
Expand All @@ -966,7 +1034,7 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, CrossSiteTransfer) {
ShellContentBrowserClient::SetSwapProcessesForRedirect(true);

// The second pages loads via the serviceworker including a subresource.
const std::string kConfirmPageUrl =
const char kConfirmPageUrl[] =
"/service_worker/cross_site_xfer_scope/"
"cross_site_xfer_confirm_via_serviceworker.html";
const base::string16 kOKTitle2(base::ASCIIToUTF16("OK_2"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJob(
int64 response_id = kInvalidServiceWorkerResponseId;
if (ShouldReadFromScriptCache(request->url(), &response_id)) {
return new ServiceWorkerReadFromCacheJob(
request, network_delegate, context_, response_id);
request, network_delegate, context_, version_, response_id);
}

// NULL means use the network.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ ServiceWorkerReadFromCacheJob::ServiceWorkerReadFromCacheJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate,
base::WeakPtr<ServiceWorkerContextCore> context,
const scoped_refptr<ServiceWorkerVersion>& version,
int64 response_id)
: net::URLRequestJob(request, network_delegate),
context_(context),
version_(version),
response_id_(response_id),
has_been_killed_(false),
weak_factory_(this) {
Expand Down Expand Up @@ -164,6 +166,8 @@ void ServiceWorkerReadFromCacheJob::OnReadInfoComplete(int result) {
if (is_range_request())
SetupRangeResponse(http_info_io_buffer_->response_data_size);
http_info_io_buffer_ = NULL;
if (request_->url() == version_->script_url())
version_->SetMainScriptHttpResponseInfo(*http_info_);
TRACE_EVENT_ASYNC_END1("ServiceWorker",
"ServiceWorkerReadFromCacheJob::ReadInfo",
this,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace content {

class ServiceWorkerContextCore;
class ServiceWorkerResponseReader;
class ServiceWorkerVersion;

// A URLRequestJob derivative used to retrieve script resources
// from the service workers script cache. It uses a response reader
Expand All @@ -29,6 +30,7 @@ class CONTENT_EXPORT ServiceWorkerReadFromCacheJob
net::URLRequest* request,
net::NetworkDelegate* network_delegate,
base::WeakPtr<ServiceWorkerContextCore> context,
const scoped_refptr<ServiceWorkerVersion>& version,
int64 response_id);

private:
Expand All @@ -55,6 +57,7 @@ class CONTENT_EXPORT ServiceWorkerReadFromCacheJob
void SetupRangeResponse(int response_data_size);

base::WeakPtr<ServiceWorkerContextCore> context_;
scoped_refptr<ServiceWorkerVersion> version_;
int64 response_id_;
scoped_ptr<ServiceWorkerResponseReader> reader_;
scoped_refptr<HttpResponseInfoIOBuffer> http_info_io_buffer_;
Expand Down
17 changes: 16 additions & 1 deletion content/browser/service_worker/service_worker_url_request_job.cc
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ bool ServiceWorkerURLRequestJob::GetMimeType(std::string* mime_type) const {
void ServiceWorkerURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
if (!http_info())
return;
const base::Time request_time = info->request_time;
*info = *http_info();
info->request_time = request_time;
info->response_time = response_time_;
}

Expand Down Expand Up @@ -538,6 +540,16 @@ void ServiceWorkerURLRequestJob::DidDispatchFetchEvent(
fetch_end_time_ = base::TimeTicks::Now();
load_timing_info_.send_end = fetch_end_time_;

// Creates a new HttpResponseInfo using the the ServiceWorker script's
// HttpResponseInfo to show HTTPS padlock.
// TODO(horo): When we support mixed-content (HTTP) no-cors requests from a
// ServiceWorker, we have to check the security level of the responses.
DCHECK(!http_response_info_);
const net::HttpResponseInfo* main_script_http_info =
provider_host_->active_version()->GetMainScriptHttpResponseInfo();
DCHECK(main_script_http_info);
http_response_info_.reset(new net::HttpResponseInfo(*main_script_http_info));

// Set up a request for reading the stream.
if (response.stream_url.is_valid()) {
DCHECK(response.blob_uuid.empty());
Expand Down Expand Up @@ -610,8 +622,11 @@ void ServiceWorkerURLRequestJob::CreateResponseHeader(
}

void ServiceWorkerURLRequestJob::CommitResponseHeader() {
http_response_info_.reset(new net::HttpResponseInfo());
if (!http_response_info_)
http_response_info_.reset(new net::HttpResponseInfo());
http_response_info_->headers.swap(http_response_headers_);
http_response_info_->vary_data = net::HttpVaryData();
http_response_info_->metadata = nullptr;
NotifyHeadersComplete();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ class CONTENT_EXPORT ServiceWorkerURLRequestJob
// StreamRegisterObserver override:
void OnStreamRegistered(Stream* stream) override;

const net::HttpResponseInfo* http_info() const;

void GetExtraResponseInfo(
bool* was_fetched_via_service_worker,
bool* was_fallback_required_by_service_worker,
Expand Down Expand Up @@ -163,6 +161,8 @@ class CONTENT_EXPORT ServiceWorkerURLRequestJob
// Releases the resources for streaming.
void ClearStream();

const net::HttpResponseInfo* http_info() const;

base::WeakPtr<ServiceWorkerProviderHost> provider_host_;

// Timing info to show on the popup in Devtools' Network tab.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "net/base/io_buffer.h"
#include "net/base/test_data_directory.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/ssl/ssl_info.h"
#include "net/test/cert_test_util.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_job_factory_impl.h"
Expand Down Expand Up @@ -125,6 +128,15 @@ class ServiceWorkerURLRequestJobTest : public testing::Test {
GURL("http://example.com/service_worker.js"),
1L,
helper_->context()->AsWeakPtr());
net::HttpResponseInfo http_info;
http_info.ssl_info.cert =
net::ImportCertFromFile(net::GetTestCertsDirectory(),
"ok_cert.pem");
EXPECT_TRUE(http_info.ssl_info.is_valid());
http_info.ssl_info.security_bits = 0x100;
// SSL3 TLS_DHE_RSA_WITH_AES_256_CBC_SHA
http_info.ssl_info.connection_status = 0x300039;
version_->SetMainScriptHttpResponseInfo(http_info);

scoped_ptr<ServiceWorkerProviderHost> provider_host(
new ServiceWorkerProviderHost(
Expand Down Expand Up @@ -180,6 +192,10 @@ class ServiceWorkerURLRequestJobTest : public testing::Test {
EXPECT_EQ(expected_status_text,
request_->response_headers()->GetStatusText());
EXPECT_EQ(expected_response, url_request_delegate_.response_data());
const net::SSLInfo& ssl_info = request_->response_info().ssl_info;
EXPECT_TRUE(ssl_info.is_valid());
EXPECT_EQ(ssl_info.security_bits, 0x100);
EXPECT_EQ(ssl_info.connection_status, 0x300039);
}

bool HasInflightRequests() {
Expand Down
11 changes: 11 additions & 0 deletions content/browser/service_worker/service_worker_version.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "content/common/service_worker/service_worker_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_switches.h"
#include "net/http/http_response_info.h"

namespace content {

Expand Down Expand Up @@ -676,6 +677,16 @@ void ServiceWorkerVersion::SetDevToolsAttached(bool attached) {
}
}

void ServiceWorkerVersion::SetMainScriptHttpResponseInfo(
const net::HttpResponseInfo& http_info) {
main_script_http_info_.reset(new net::HttpResponseInfo(http_info));
}

const net::HttpResponseInfo*
ServiceWorkerVersion::GetMainScriptHttpResponseInfo() {
return main_script_http_info_.get();
}

void ServiceWorkerVersion::OnStarted() {
DCHECK_EQ(RUNNING, running_status());
DCHECK(cache_listener_.get());
Expand Down
12 changes: 12 additions & 0 deletions content/browser/service_worker/service_worker_version.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ namespace blink {
struct WebCircularGeofencingRegion;
}

namespace net {
class HttpResponseInfo;
}

namespace content {

class EmbeddedWorkerRegistry;
Expand Down Expand Up @@ -292,6 +296,13 @@ class CONTENT_EXPORT ServiceWorkerVersion

void SetDevToolsAttached(bool attached);

// Sets the HttpResponseInfo used to load the main script.
// This HttpResponseInfo will be used for all responses sent back from the
// service worker, as the effective security of these responses is equivalent
// to that of the ServiceWorker.
void SetMainScriptHttpResponseInfo(const net::HttpResponseInfo& http_info);
const net::HttpResponseInfo* GetMainScriptHttpResponseInfo();

private:
class GetClientDocumentsCallback;

Expand Down Expand Up @@ -411,6 +422,7 @@ class CONTENT_EXPORT ServiceWorkerVersion
bool is_doomed_;
std::vector<int> pending_skip_waiting_requests_;
bool skip_waiting_;
scoped_ptr<net::HttpResponseInfo> main_script_http_info_;

base::WeakPtrFactory<ServiceWorkerVersion> weak_factory_;

Expand Down
Loading

0 comments on commit 4bd0e01

Please sign in to comment.