Commit 05a2bf0b authored by tyoshino's avatar tyoshino Committed by Commit bot

Set response headers for data URL.

Resources represented by a data URL will stay being considered to be
unique origin resource but are changed to allow cross origin access by
the Access-Control-Allow-Origin header.

We take this approach mainly because we want scripts in an iframe with
a data URL specified to its src attribute to be executed as a script on
a unique origin resource, not on the parent frame.

We can choose to treat "loading" of data URL specially as same origin
while execution as different origin. But such an approach complicates
security policy checking algorithm.

Grammar checking code is added to ensure we emit a valid content-type.

Blink side CL https://codereview.chromium.org/54173002/ will be landed
first to temporarily disable layout tests that will break, and then
this CL will be landed.

BUG=308768

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=291007

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

Cr-Commit-Position: refs/heads/master@{#294107}
parent ed19be6a
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "net/http/http_response_headers.h" #include "net/http/http_response_headers.h"
#include "net/http/http_util.h" #include "net/http/http_util.h"
#include "net/url_request/redirect_info.h" #include "net/url_request/redirect_info.h"
#include "net/url_request/url_request_data_job.h"
#include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h" #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
#include "third_party/WebKit/public/platform/WebHTTPLoadInfo.h" #include "third_party/WebKit/public/platform/WebHTTPLoadInfo.h"
#include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/platform/WebURL.h"
...@@ -107,35 +108,6 @@ class HeaderFlattener : public WebHTTPHeaderVisitor { ...@@ -107,35 +108,6 @@ class HeaderFlattener : public WebHTTPHeaderVisitor {
bool has_accept_header_; bool has_accept_header_;
}; };
// Extracts the information from a data: url.
bool GetInfoFromDataURL(const GURL& url,
ResourceResponseInfo* info,
std::string* data,
int* error_code) {
std::string mime_type;
std::string charset;
if (net::DataURL::Parse(url, &mime_type, &charset, data)) {
*error_code = net::OK;
// Assure same time for all time fields of data: URLs.
Time now = Time::Now();
info->load_timing.request_start = TimeTicks::Now();
info->load_timing.request_start_time = now;
info->request_time = now;
info->response_time = now;
info->headers = NULL;
info->mime_type.swap(mime_type);
info->charset.swap(charset);
info->security_info.clear();
info->content_length = data->length();
info->encoded_data_length = 0;
return true;
}
*error_code = net::ERR_INVALID_URL;
return false;
}
typedef ResourceDevToolsInfo::HeadersVector HeadersVector; typedef ResourceDevToolsInfo::HeadersVector HeadersVector;
// Converts timing data from |load_timing| to the format used by WebKit. // Converts timing data from |load_timing| to the format used by WebKit.
...@@ -196,6 +168,37 @@ net::RequestPriority ConvertWebKitPriorityToNetPriority( ...@@ -196,6 +168,37 @@ net::RequestPriority ConvertWebKitPriorityToNetPriority(
} }
} }
// Extracts info from a data scheme URL into |info| and |data|. Returns net::OK
// if successful. Returns a net error code otherwise. Exported only for testing.
int GetInfoFromDataURL(const GURL& url,
ResourceResponseInfo* info,
std::string* data) {
// Assure same time for all time fields of data: URLs.
Time now = Time::Now();
info->load_timing.request_start = TimeTicks::Now();
info->load_timing.request_start_time = now;
info->request_time = now;
info->response_time = now;
std::string mime_type;
std::string charset;
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(std::string()));
int result = net::URLRequestDataJob::BuildResponse(
url, &mime_type, &charset, data, headers.get());
if (result != net::OK)
return result;
info->headers = headers;
info->mime_type.swap(mime_type);
info->charset.swap(charset);
info->security_info.clear();
info->content_length = data->length();
info->encoded_data_length = 0;
return net::OK;
}
} // namespace } // namespace
// WebURLLoaderImpl::Context -------------------------------------------------- // WebURLLoaderImpl::Context --------------------------------------------------
...@@ -315,10 +318,9 @@ void WebURLLoaderImpl::Context::Start(const WebURLRequest& request, ...@@ -315,10 +318,9 @@ void WebURLLoaderImpl::Context::Start(const WebURLRequest& request,
if (sync_load_response) { if (sync_load_response) {
// This is a sync load. Do the work now. // This is a sync load. Do the work now.
sync_load_response->url = url; sync_load_response->url = url;
std::string data; sync_load_response->error_code =
GetInfoFromDataURL(sync_load_response->url, sync_load_response, GetInfoFromDataURL(sync_load_response->url, sync_load_response,
&sync_load_response->data, &sync_load_response->data);
&sync_load_response->error_code);
} else { } else {
base::MessageLoop::current()->PostTask( base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&Context::HandleDataURL, this)); FROM_HERE, base::Bind(&Context::HandleDataURL, this));
...@@ -694,10 +696,11 @@ bool WebURLLoaderImpl::Context::CanHandleDataURLRequestLocally() const { ...@@ -694,10 +696,11 @@ bool WebURLLoaderImpl::Context::CanHandleDataURLRequestLocally() const {
void WebURLLoaderImpl::Context::HandleDataURL() { void WebURLLoaderImpl::Context::HandleDataURL() {
ResourceResponseInfo info; ResourceResponseInfo info;
int error_code;
std::string data; std::string data;
if (GetInfoFromDataURL(request_.url(), &info, &data, &error_code)) { int error_code = GetInfoFromDataURL(request_.url(), &info, &data);
if (error_code == net::OK) {
OnReceivedResponse(info); OnReceivedResponse(info);
if (!data.empty()) if (!data.empty())
OnReceivedData(data.data(), data.size(), 0); OnReceivedData(data.data(), data.size(), 0);
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "net/base/escape.h" #include "net/base/escape.h"
#include "net/base/mime_util.h" #include "net/base/mime_util.h"
#include "net/http/http_util.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace net { namespace net {
...@@ -57,6 +58,10 @@ bool DataURL::Parse(const GURL& url, std::string* mime_type, ...@@ -57,6 +58,10 @@ bool DataURL::Parse(const GURL& url, std::string* mime_type,
} else if (charset->empty() && } else if (charset->empty() &&
iter->compare(0, kCharsetTagLength, kCharsetTag) == 0) { iter->compare(0, kCharsetTagLength, kCharsetTag) == 0) {
charset->assign(iter->substr(kCharsetTagLength)); charset->assign(iter->substr(kCharsetTagLength));
// The grammar for charset is not specially defined in RFC2045 and
// RFC2397. It just needs to be a token.
if (!net::HttpUtil::IsToken(*charset))
return false;
} }
} }
......
...@@ -40,10 +40,10 @@ TEST(DataURLTest, Parse) { ...@@ -40,10 +40,10 @@ TEST(DataURLTest, Parse) {
"" }, "" },
{ "data:;charset=,test", { "data:;charset=,test",
true, false,
"text/plain", "",
"US-ASCII", "",
"test" }, "" },
{ "data:TeXt/HtMl,<b>x</b>", { "data:TeXt/HtMl,<b>x</b>",
true, true,
......
...@@ -1673,6 +1673,7 @@ ...@@ -1673,6 +1673,7 @@
'url_request/url_fetcher_impl_unittest.cc', 'url_request/url_fetcher_impl_unittest.cc',
'url_request/url_fetcher_response_writer_unittest.cc', 'url_request/url_fetcher_response_writer_unittest.cc',
'url_request/url_request_context_builder_unittest.cc', 'url_request/url_request_context_builder_unittest.cc',
'url_request/url_request_data_job_unittest.cc',
'url_request/url_request_file_job_unittest.cc', 'url_request/url_request_file_job_unittest.cc',
'url_request/url_request_filter_unittest.cc', 'url_request/url_request_filter_unittest.cc',
'url_request/url_request_ftp_job_unittest.cc', 'url_request/url_request_ftp_job_unittest.cc',
......
...@@ -8,9 +8,41 @@ ...@@ -8,9 +8,41 @@
#include "net/base/data_url.h" #include "net/base/data_url.h"
#include "net/base/net_errors.h" #include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "url/gurl.h"
namespace net { namespace net {
int URLRequestDataJob::BuildResponse(const GURL& url,
std::string* mime_type,
std::string* charset,
std::string* data,
HttpResponseHeaders* headers) {
if (!net::DataURL::Parse(url, mime_type, charset, data))
return net::ERR_INVALID_URL;
// |mime_type| set by net::DataURL::Parse() is guaranteed to be in
// token "/" token
// form. |charset| is also guaranteed to be a token.
DCHECK(!mime_type->empty());
DCHECK(!charset->empty());
if (headers) {
headers->ReplaceStatusLine("HTTP/1.1 200 OK");
// "charset" in the Content-Type header is specified explicitly to follow
// the "token" ABNF in the HTTP spec. When DataURL::Parse() call is
// successful, it's guaranteed that the string in |charset| follows the
// "token" ABNF.
std::string content_type_header =
"Content-Type: " + *mime_type + ";charset=" + *charset;
headers->AddHeader(content_type_header);
headers->AddHeader("Access-Control-Allow-Origin: *");
}
return net::OK;
}
URLRequestDataJob::URLRequestDataJob( URLRequestDataJob::URLRequestDataJob(
URLRequest* request, NetworkDelegate* network_delegate) URLRequest* request, NetworkDelegate* network_delegate)
: URLRequestSimpleJob(request, network_delegate) { : URLRequestSimpleJob(request, network_delegate) {
...@@ -25,7 +57,10 @@ int URLRequestDataJob::GetData(std::string* mime_type, ...@@ -25,7 +57,10 @@ int URLRequestDataJob::GetData(std::string* mime_type,
const GURL& url = request_->url(); const GURL& url = request_->url();
if (!url.is_valid()) if (!url.is_valid())
return ERR_INVALID_URL; return ERR_INVALID_URL;
return DataURL::Parse(url, mime_type, charset, data)? OK: ERR_INVALID_URL;
// TODO(tyoshino): Get the headers and export via
// URLRequestJob::GetResponseInfo().
return BuildResponse(url, mime_type, charset, data, NULL);
} }
URLRequestDataJob::~URLRequestDataJob() { URLRequestDataJob::~URLRequestDataJob() {
......
...@@ -10,12 +10,23 @@ ...@@ -10,12 +10,23 @@
#include "net/url_request/url_request.h" #include "net/url_request/url_request.h"
#include "net/url_request/url_request_simple_job.h" #include "net/url_request/url_request_simple_job.h"
class GURL;
namespace net { namespace net {
class HttpResponseHeaders;
class URLRequest; class URLRequest;
class URLRequestDataJob : public URLRequestSimpleJob { class NET_EXPORT URLRequestDataJob : public URLRequestSimpleJob {
public: public:
// Extracts info from a data scheme URL. Returns OK if successful. Returns
// ERR_INVALID_URL otherwise.
static int BuildResponse(const GURL& url,
std::string* mime_type,
std::string* charset,
std::string* data,
HttpResponseHeaders* headers);
URLRequestDataJob(URLRequest* request, NetworkDelegate* network_delegate); URLRequestDataJob(URLRequest* request, NetworkDelegate* network_delegate);
// URLRequestSimpleJob // URLRequestSimpleJob
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "base/memory/ref_counted.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_version.h"
#include "net/url_request/url_request_data_job.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace net {
TEST(BuildResponseTest, Simple) {
std::string mime_type;
std::string charset;
std::string data;
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(std::string()));
ASSERT_EQ(
net::OK,
URLRequestDataJob::BuildResponse(
GURL("data:,Hello"), &mime_type, &charset, &data, headers.get()));
EXPECT_EQ("text/plain", mime_type);
EXPECT_EQ("US-ASCII", charset);
EXPECT_EQ("Hello", data);
const net::HttpVersion& version = headers->GetParsedHttpVersion();
EXPECT_EQ(1, version.major_value());
EXPECT_EQ(1, version.minor_value());
EXPECT_EQ("OK", headers->GetStatusText());
std::string value;
EXPECT_TRUE(headers->GetNormalizedHeader("Content-Type", &value));
EXPECT_EQ(value, "text/plain;charset=US-ASCII");
value.clear();
EXPECT_TRUE(
headers->GetNormalizedHeader("Access-Control-Allow-Origin", &value));
EXPECT_EQ(value, "*");
}
TEST(BuildResponseTest, InvalidInput) {
std::string mime_type;
std::string charset;
std::string data;
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(std::string()));
EXPECT_EQ(
net::ERR_INVALID_URL,
URLRequestDataJob::BuildResponse(
GURL("bogus"), &mime_type, &charset, &data, headers.get()));
}
TEST(BuildResponseTest, InvalidMimeType) {
std::string mime_type;
std::string charset;
std::string data;
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(std::string()));
// MIME type contains delimiters. Must be rejected.
EXPECT_EQ(
net::ERR_INVALID_URL,
URLRequestDataJob::BuildResponse(
GURL("data:f(o/b)r,test"),
&mime_type, &charset, &data, headers.get()));
}
TEST(BuildResponseTest, InvalidCharset) {
std::string mime_type;
std::string charset;
std::string data;
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(std::string()));
// MIME type contains delimiters. Must be rejected.
EXPECT_EQ(
net::ERR_INVALID_URL,
URLRequestDataJob::BuildResponse(
GURL("data:text/html;charset=(),test"),
&mime_type, &charset, &data, headers.get()));
}
} // namespace net
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment