https://bugs.webkit.org/show_bug.cgi?id=175628
<rdar://problem/
33919278>
Reviewed by Geoffrey Garen.
LayoutTests/imported/w3c:
Rebaseline a few web-platform-tests due to revert of r220779.
* web-platform-tests/beacon/headers/header-content-type-expected.txt:
* web-platform-tests/fetch/api/basic/request-headers.any-expected.txt:
* web-platform-tests/fetch/api/basic/request-headers.any.worker-expected.txt:
Source/WebCore:
Cross origin Beacon requests with a ArrayBuffer / ArrayBufferView payload should not do a CORS preflight.
To achieve this, the following changes were made:
1. Revert r220779 which caused us to use a non CORS-safelisted Content-Type header for such payload
2. Teach PingLoad how to deal with "simple" cross origin requests (i.e. Don't assume we need a CORS
preflight merely because the fetch mode is set to "cors").
Test: http/wpt/beacon/cors/crossorigin-arraybufferview-no-preflight.html
* Modules/fetch/FetchBody.cpp:
(WebCore::FetchBody::extract):
* loader/CrossOriginAccessControl.h:
* loader/LoaderStrategy.h:
* loader/PingLoader.cpp:
(WebCore::PingLoader::loadImage):
(WebCore::PingLoader::sendPing):
(WebCore::PingLoader::sendViolationReport):
(WebCore::PingLoader::startPingLoad):
* loader/PingLoader.h:
* loader/cache/CachedResource.cpp:
(WebCore::CachedResource::CachedResource):
(WebCore::CachedResource::load):
* loader/cache/CachedResource.h:
* loader/cache/CachedResourceRequest.cpp:
(WebCore::CachedResourceRequest::CachedResourceRequest):
* loader/cache/CachedResourceRequest.h:
(WebCore::CachedResourceRequest::releaseOriginalRequestHeaders):
* platform/network/HTTPHeaderValues.cpp:
* platform/network/HTTPHeaderValues.h:
Source/WebKit:
Cross origin Beacon requests with a ArrayBuffer / ArrayBufferView payload should not do a CORS preflight.
To achieve this, the following changes were made:
1. Revert r220779 which caused us to use a non CORS-safelisted Content-Type header for such payload
2. Teach PingLoad how to deal with "simple" cross origin requests (i.e. Don't assume we need a CORS
preflight merely because the fetch mode is set to "cors").
* NetworkProcess/NetworkCORSPreflightChecker.cpp:
(WebKit::NetworkCORSPreflightChecker::startPreflight):
* NetworkProcess/NetworkResourceLoadParameters.cpp:
(WebKit::NetworkResourceLoadParameters::encode const):
(WebKit::NetworkResourceLoadParameters::decode):
* NetworkProcess/NetworkResourceLoadParameters.h:
* NetworkProcess/PingLoad.cpp:
(WebKit::PingLoad::PingLoad):
(WebKit::PingLoad::loadRequest):
(WebKit::PingLoad::originalRequestHeaders const):
(WebKit::PingLoad::willPerformHTTPRedirection):
(WebKit::PingLoad::isAllowedRedirect const):
(WebKit::PingLoad::makeCrossOriginAccessRequest):
(WebKit::PingLoad::makeSimpleCrossOriginAccessRequest):
(WebKit::PingLoad::makeCrossOriginAccessRequestWithPreflight):
(WebKit::PingLoad::preflightSuccess):
* NetworkProcess/PingLoad.h:
* WebProcess/Network/WebLoaderStrategy.cpp:
(WebKit::WebLoaderStrategy::createPingHandle):
* WebProcess/Network/WebLoaderStrategy.h:
Source/WebKitLegacy:
* WebCoreSupport/WebResourceLoadScheduler.cpp:
(WebResourceLoadScheduler::createPingHandle):
* WebCoreSupport/WebResourceLoadScheduler.h:
LayoutTests:
* http/tests/blink/sendbeacon/beacon-same-origin-expected.txt:
* http/wpt/fetch/fetch-request-arraybuffer-content-type-expected.txt:
* http/wpt/fetch/fetch-request-arraybuffer-content-type.html:
Rebaseline test due to revert of r220779.
* http/wpt/beacon/cors/cors-preflight-arraybufferview-failure-expected.txt: Removed.
* http/wpt/beacon/cors/cors-preflight-arraybufferview-failure.html: Removed.
Drop outdated test. CORS preflight failure is still covered by the corresponding Blob payload test.
* http/wpt/beacon/cors/cors-preflight-arraybufferview-success-expected.txt: Removed.
* http/wpt/beacon/cors/cors-preflight-arraybufferview-success.html: Removed.
* http/wpt/beacon/cors/crossorigin-arraybufferview-no-preflight-expected.txt: Added.
* http/wpt/beacon/cors/crossorigin-arraybufferview-no-preflight.html: Added.
Rename and update test so that it checks that we no longer do a CORS preflight for cross origin
beacons that have an ArrayBuffer payload.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@220817
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2017-08-16 Chris Dumez <cdumez@apple.com>
+
+ Cross origin Beacon requests with a ArrayBuffer / ArrayBufferView payload should not do a CORS preflight
+ https://bugs.webkit.org/show_bug.cgi?id=175628
+ <rdar://problem/33919278>
+
+ Reviewed by Geoffrey Garen.
+
+ * http/tests/blink/sendbeacon/beacon-same-origin-expected.txt:
+ * http/wpt/fetch/fetch-request-arraybuffer-content-type-expected.txt:
+ * http/wpt/fetch/fetch-request-arraybuffer-content-type.html:
+ Rebaseline test due to revert of r220779.
+
+ * http/wpt/beacon/cors/cors-preflight-arraybufferview-failure-expected.txt: Removed.
+ * http/wpt/beacon/cors/cors-preflight-arraybufferview-failure.html: Removed.
+ Drop outdated test. CORS preflight failure is still covered by the corresponding Blob payload test.
+
+ * http/wpt/beacon/cors/cors-preflight-arraybufferview-success-expected.txt: Removed.
+ * http/wpt/beacon/cors/cors-preflight-arraybufferview-success.html: Removed.
+ * http/wpt/beacon/cors/crossorigin-arraybufferview-no-preflight-expected.txt: Added.
+ * http/wpt/beacon/cors/crossorigin-arraybufferview-no-preflight.html: Added.
+ Rename and update test so that it checks that we no longer do a CORS preflight for cross origin
+ beacons that have an ArrayBuffer payload.
+
2017-08-16 Matt Baker <mattbaker@apple.com>
Web Inspector: capture async stack trace when workers/main context posts a message
Sending beacon with type: [object Uint32Array]
PASS navigator.sendBeacon("resources/save-beacon.php?name=same-origin", payload); is true
PASS Beacon sent successfully
-PASS Content-Type: application/octet-stream
+PASS Content-Type: application/x-www-form-urlencoded
PASS Origin: http://127.0.0.1:8000
PASS Referer: http://127.0.0.1:8000/blink/sendbeacon/beacon-same-origin.html
PASS Request-Method: POST
+++ /dev/null
-
-PASS CORS preflight failure test
-
+++ /dev/null
-<!doctype html>
-<html>
- <head>
- <meta charset="utf-8">
- <title>SendBeacon CORS preflight test</title>
- <script src=/resources/testharness.js></script>
- <script src=/resources/testharnessreport.js></script>
- </head>
- <body>
- <script src="/common/utils.js"></script>
- <script src="/common/get-host-info.sub.js"></script>
- <script>
-var RESOURCES_DIR = "/WebKit/beacon/resources/";
-
-function pollResult(test, id) {
- var checkUrl = RESOURCES_DIR + "beacon-preflight.py?cmd=get&id=" + id;
-
- return new Promise(resolve => {
- step_timeout(test.step_func(() => {
- fetch(checkUrl).then(response => {
- response.json().then(body => {
- resolve(body);
- });
- });
- }), 1000);
- });
-}
-
-function testCORSPreflightFailure(what) {
- var testBase = get_host_info().HTTP_REMOTE_ORIGIN + RESOURCES_DIR;
- var id = self.token();
- var testUrl = testBase + "beacon-preflight.py?allowCors=0&cmd=put&id=" + id;
-
- promise_test(function(test) {
- assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
- return pollResult(test, id) .then(result => {
- assert_equals(result['preflight'], 1, "Received preflight")
- assert_equals(result['preflight_referer'], document.URL, "Preflight referer header")
- assert_equals(result['preflight_requested_method'], "POST", "Preflight requested method")
- let requested_headers = result['preflight_requested_headers'].toLowerCase()
- assert_true(requested_headers.includes("content-type"), "Content-Type header is requested")
- assert_true(requested_headers.includes("referer"), "Referer header is requested")
- assert_true(requested_headers.includes("origin"), "Origin header is requested")
- assert_equals(result['beacon'], 0, "Did not receive beacon")
- });
- }, "CORS preflight failure test");
-}
-
-function stringToArrayBufferView(input) {
- var buffer = new ArrayBuffer(input.length * 2);
- var view = new Uint16Array(buffer);
-
- // dumbly copy over the bytes
- for (var i = 0, len = input.length; i < len; i++) {
- view[i] = input.charCodeAt(i);
- }
- return view;
-}
-
-testCORSPreflightFailure(stringToArrayBufferView("123"));
- </script>
- </body>
-</html>
+++ /dev/null
-
-PASS CORS preflight success test
-
--- /dev/null
+
+PASS Should send beacon with no CORS preflight
+
<html>
<head>
<meta charset="utf-8">
- <title>SendBeacon CORS preflight test</title>
+ <title>SendBeacon cross origin with an ArrayBuffer / ArrayBufferView payload should not do a CORS preflight</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
</head>
promise_test(function(test) {
assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
return pollResult(test, id) .then(result => {
- assert_equals(result['preflight'], 1, "Received preflight")
- assert_equals(result['preflight_referer'], document.URL, "Preflight referer header")
- assert_equals(result['preflight_requested_method'], "POST", "Preflight requested method")
- let requested_headers = result['preflight_requested_headers'].toLowerCase()
- assert_true(requested_headers.includes("content-type"), "Content-Type header is requested")
- assert_true(requested_headers.includes("referer"), "Referer header is requested")
- assert_true(requested_headers.includes("origin"), "Origin header is requested")
+ assert_equals(result['preflight'], 0, "Did not receive CORS preflight")
assert_equals(result['beacon'], 1, "Received beacon")
});
- }, "CORS preflight success test");
+ }, "Should send beacon with no CORS preflight");
}
function stringToArrayBufferView(input) {
-PASS Content-Type header for payload of ArrayBuffer type should be 'application/octet-stream'
+PASS Content-Type header for payload of ArrayBuffer type should be null
<script>
test(function(t) {
let request = new Request("http://localhost", { method: "POST", body: new ArrayBuffer() });
- assert_equals(request.headers.get('Content-Type'), "application/octet-stream");
-}, "Content-Type header for payload of ArrayBuffer type should be 'application/octet-stream'");
+ assert_equals(request.headers.get('Content-Type'), null);
+}, "Content-Type header for payload of ArrayBuffer type should be null");
</script>
</body>
</html>
+2017-08-16 Chris Dumez <cdumez@apple.com>
+
+ Cross origin Beacon requests with a ArrayBuffer / ArrayBufferView payload should not do a CORS preflight
+ https://bugs.webkit.org/show_bug.cgi?id=175628
+ <rdar://problem/33919278>
+
+ Reviewed by Geoffrey Garen.
+
+ Rebaseline a few web-platform-tests due to revert of r220779.
+
+ * web-platform-tests/beacon/headers/header-content-type-expected.txt:
+ * web-platform-tests/fetch/api/basic/request-headers.any-expected.txt:
+ * web-platform-tests/fetch/api/basic/request-headers.any.worker-expected.txt:
+
2017-08-16 Chris Dumez <cdumez@apple.com>
EventSource: ignore IDs with U+0000
PASS Test content-type header for a body string
-FAIL Test content-type header for a body ArrayBufferView assert_equals: Correct Content-Type header result expected "" but got "application/octet-stream"
-FAIL Test content-type header for a body ArrayBuffer assert_equals: Correct Content-Type header result expected "" but got "application/octet-stream"
+FAIL Test content-type header for a body ArrayBufferView assert_equals: Correct Content-Type header result expected "" but got "application/x-www-form-urlencoded"
+FAIL Test content-type header for a body ArrayBuffer assert_equals: Correct Content-Type header result expected "" but got "application/x-www-form-urlencoded"
PASS Test content-type header for a body Blob
PASS Test content-type header for a body FormData
PASS Test content-type header for a body URLSearchParams
PASS Fetch with POST with FormData body
PASS Fetch with POST with URLSearchParams body
FAIL Fetch with POST with Blob body assert_equals: Request should have header content-type: null expected (object) null but got (string) ""
-FAIL Fetch with POST with ArrayBuffer body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/octet-stream"
-FAIL Fetch with POST with Uint8Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/octet-stream"
-FAIL Fetch with POST with Int8Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/octet-stream"
-FAIL Fetch with POST with Float32Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/octet-stream"
-FAIL Fetch with POST with Float64Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/octet-stream"
-FAIL Fetch with POST with DataView body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/octet-stream"
+FAIL Fetch with POST with ArrayBuffer body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/x-www-form-urlencoded"
+FAIL Fetch with POST with Uint8Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/x-www-form-urlencoded"
+FAIL Fetch with POST with Int8Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/x-www-form-urlencoded"
+FAIL Fetch with POST with Float32Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/x-www-form-urlencoded"
+FAIL Fetch with POST with Float64Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/x-www-form-urlencoded"
+FAIL Fetch with POST with DataView body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/x-www-form-urlencoded"
PASS Fetch with POST with Blob body with mime type
FAIL Fetch with Chicken assert_equals: Request should have header content-length: null expected (object) null but got (string) "0"
PASS Fetch with Chicken with body
FAIL Fetch with POST with FormData body Can't find variable: FormData
PASS Fetch with POST with URLSearchParams body
FAIL Fetch with POST with Blob body assert_equals: Request should have header content-type: null expected (object) null but got (string) ""
-FAIL Fetch with POST with ArrayBuffer body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/octet-stream"
-FAIL Fetch with POST with Uint8Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/octet-stream"
-FAIL Fetch with POST with Int8Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/octet-stream"
-FAIL Fetch with POST with Float32Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/octet-stream"
-FAIL Fetch with POST with Float64Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/octet-stream"
-FAIL Fetch with POST with DataView body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/octet-stream"
+FAIL Fetch with POST with ArrayBuffer body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/x-www-form-urlencoded"
+FAIL Fetch with POST with Uint8Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/x-www-form-urlencoded"
+FAIL Fetch with POST with Int8Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/x-www-form-urlencoded"
+FAIL Fetch with POST with Float32Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/x-www-form-urlencoded"
+FAIL Fetch with POST with Float64Array body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/x-www-form-urlencoded"
+FAIL Fetch with POST with DataView body assert_equals: Request should have header content-type: null expected (object) null but got (string) "application/x-www-form-urlencoded"
PASS Fetch with POST with Blob body with mime type
FAIL Fetch with Chicken assert_equals: Request should have header content-length: null expected (object) null but got (string) "0"
PASS Fetch with Chicken with body
+2017-08-16 Chris Dumez <cdumez@apple.com>
+
+ Cross origin Beacon requests with a ArrayBuffer / ArrayBufferView payload should not do a CORS preflight
+ https://bugs.webkit.org/show_bug.cgi?id=175628
+ <rdar://problem/33919278>
+
+ Reviewed by Geoffrey Garen.
+
+ Cross origin Beacon requests with a ArrayBuffer / ArrayBufferView payload should not do a CORS preflight.
+ To achieve this, the following changes were made:
+ 1. Revert r220779 which caused us to use a non CORS-safelisted Content-Type header for such payload
+ 2. Teach PingLoad how to deal with "simple" cross origin requests (i.e. Don't assume we need a CORS
+ preflight merely because the fetch mode is set to "cors").
+
+ Test: http/wpt/beacon/cors/crossorigin-arraybufferview-no-preflight.html
+
+ * Modules/fetch/FetchBody.cpp:
+ (WebCore::FetchBody::extract):
+ * loader/CrossOriginAccessControl.h:
+ * loader/LoaderStrategy.h:
+ * loader/PingLoader.cpp:
+ (WebCore::PingLoader::loadImage):
+ (WebCore::PingLoader::sendPing):
+ (WebCore::PingLoader::sendViolationReport):
+ (WebCore::PingLoader::startPingLoad):
+ * loader/PingLoader.h:
+ * loader/cache/CachedResource.cpp:
+ (WebCore::CachedResource::CachedResource):
+ (WebCore::CachedResource::load):
+ * loader/cache/CachedResource.h:
+ * loader/cache/CachedResourceRequest.cpp:
+ (WebCore::CachedResourceRequest::CachedResourceRequest):
+ * loader/cache/CachedResourceRequest.h:
+ (WebCore::CachedResourceRequest::releaseOriginalRequestHeaders):
+ * platform/network/HTTPHeaderValues.cpp:
+ * platform/network/HTTPHeaderValues.h:
+
2017-08-16 Matt Baker <mattbaker@apple.com>
Web Inspector: capture async stack trace when workers/main context posts a message
if (WTF::holds_alternative<RefPtr<ArrayBuffer>>(value)) {
Ref<const ArrayBuffer> buffer = WTF::get<RefPtr<ArrayBuffer>>(value).releaseNonNull();
- // FIXME: We should not set a Content-Type here but we need to do this until all network stacks
- // support sending no Content-Type header (<dar://problem/33906567).
- contentType = HTTPHeaderValues::octetStreamContentType();
return FetchBody(WTFMove(buffer));
}
if (WTF::holds_alternative<RefPtr<ArrayBufferView>>(value)) {
Ref<const ArrayBufferView> buffer = WTF::get<RefPtr<ArrayBufferView>>(value).releaseNonNull();
- // FIXME: We should not set a Content-Type here but we need to do this until all network stacks
- // support sending no Content-Type header (<dar://problem/33906567).
- contentType = HTTPHeaderValues::octetStreamContentType();
return FetchBody(WTFMove(buffer));
}
class SecurityOrigin;
class URL;
-bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap&);
+WEBCORE_EXPORT bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap&);
bool isOnAccessControlSimpleRequestMethodWhitelist(const String&);
WEBCORE_EXPORT void updateRequestForAccessControl(ResourceRequest&, SecurityOrigin&, StoredCredentials);
class CachedResource;
class ContentSecurityPolicy;
class Frame;
+class HTTPHeaderMap;
class NetscapePlugInStreamLoader;
class NetscapePlugInStreamLoaderClient;
class NetworkingContext;
virtual void suspendPendingRequests() = 0;
virtual void resumePendingRequests() = 0;
- virtual void createPingHandle(NetworkingContext*, ResourceRequest&, Ref<SecurityOrigin>&& sourceOrigin, ContentSecurityPolicy*, const FetchOptions&) = 0;
+ virtual void createPingHandle(NetworkingContext*, ResourceRequest&, HTTPHeaderMap&& originalRequestHeaders, Ref<SecurityOrigin>&& sourceOrigin, ContentSecurityPolicy*, const FetchOptions&) = 0;
virtual void storeDerivedDataToCache(const SHA1::Digest& bodyKey, const String& type, const String& partition, WebCore::SharedBuffer&) = 0;
document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(request, ContentSecurityPolicy::InsecureRequestType::Load);
request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0");
+
+ HTTPHeaderMap originalRequestHeader = request.httpHeaderFields();
+
String referrer = SecurityPolicy::generateReferrerHeader(document.referrerPolicy(), request.url(), frame.loader().outgoingReferrer());
if (!referrer.isEmpty())
request.setHTTPReferrer(referrer);
frame.loader().addExtraFieldsToSubresourceRequest(request);
- startPingLoad(frame, request, document, ShouldFollowRedirects::Yes);
+ startPingLoad(frame, request, WTFMove(originalRequestHeader), document, ShouldFollowRedirects::Yes);
}
// http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing
request.setHTTPContentType("text/ping");
request.setHTTPBody(FormData::create("PING"));
request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0");
+
+ HTTPHeaderMap originalRequestHeader = request.httpHeaderFields();
+
frame.loader().addExtraFieldsToSubresourceRequest(request);
auto& sourceOrigin = document.securityOrigin();
}
}
- startPingLoad(frame, request, document, ShouldFollowRedirects::Yes);
+ startPingLoad(frame, request, WTFMove(originalRequestHeader), document, ShouldFollowRedirects::Yes);
}
void PingLoader::sendViolationReport(Frame& frame, const URL& reportURL, Ref<FormData>&& report, ViolationReportType reportType)
if (removeCookies)
request.setAllowCookies(false);
+ HTTPHeaderMap originalRequestHeader = request.httpHeaderFields();
+
frame.loader().addExtraFieldsToSubresourceRequest(request);
String referrer = SecurityPolicy::generateReferrerHeader(document.referrerPolicy(), reportURL, frame.loader().outgoingReferrer());
if (!referrer.isEmpty())
request.setHTTPReferrer(referrer);
- startPingLoad(frame, request, document, ShouldFollowRedirects::No);
+ startPingLoad(frame, request, WTFMove(originalRequestHeader), document, ShouldFollowRedirects::No);
}
-void PingLoader::startPingLoad(Frame& frame, ResourceRequest& request, Document& document, ShouldFollowRedirects shouldFollowRedirects)
+void PingLoader::startPingLoad(Frame& frame, ResourceRequest& request, HTTPHeaderMap&& originalRequestHeaders, Document& document, ShouldFollowRedirects shouldFollowRedirects)
{
unsigned long identifier = frame.page()->progress().createUniqueIdentifier();
// FIXME: Why activeDocumentLoader? I would have expected documentLoader().
InspectorInstrumentation::continueAfterPingLoader(frame, identifier, frame.loader().activeDocumentLoader(), request, ResourceResponse());
auto* contentSecurityPolicy = document.shouldBypassMainWorldContentSecurityPolicy() ? nullptr : document.contentSecurityPolicy();
- platformStrategies()->loaderStrategy()->createPingHandle(frame.loader().networkingContext(), request, document.securityOrigin(), contentSecurityPolicy, options);
+ platformStrategies()->loaderStrategy()->createPingHandle(frame.loader().networkingContext(), request, WTFMove(originalRequestHeaders), document.securityOrigin(), contentSecurityPolicy, options);
}
}
class Document;
class FormData;
class Frame;
+class HTTPHeaderMap;
class URL;
class ResourceRequest;
private:
enum class ShouldFollowRedirects { No, Yes };
- static void startPingLoad(Frame&, ResourceRequest&, Document&, ShouldFollowRedirects);
+ static void startPingLoad(Frame&, ResourceRequest&, HTTPHeaderMap&& originalRequestHeaders, Document&, ShouldFollowRedirects);
};
} // namespace WebCore
CachedResource::CachedResource(CachedResourceRequest&& request, Type type, SessionID sessionID)
: m_resourceRequest(request.releaseResourceRequest())
+ , m_originalRequestHeaders(request.releaseOriginalRequestHeaders())
, m_options(request.options())
, m_decodedDataDeletionTimer(*this, &CachedResource::destroyDecodedData, deadDecodedDataDeletionIntervalForResourceType(type))
, m_sessionID(sessionID)
// Beacon is not exposed to workers so it is safe to rely on the document here.
auto* document = cachedResourceLoader.document();
auto* contentSecurityPolicy = document && !document->shouldBypassMainWorldContentSecurityPolicy() ? document->contentSecurityPolicy() : nullptr;
- platformStrategies()->loaderStrategy()->createPingHandle(frame.loader().networkingContext(), request, *m_origin, contentSecurityPolicy, m_options);
+ platformStrategies()->loaderStrategy()->createPingHandle(frame.loader().networkingContext(), request, HTTPHeaderMap(m_originalRequestHeaders), *m_origin, contentSecurityPolicy, m_options);
// FIXME: We currently do not get notified when ping loads finish so we treat them as finishing right away.
finishLoading(nullptr);
return;
// FIXME: Make the rest of these data members private and use functions in derived classes instead.
HashCountedSet<CachedResourceClient*> m_clients;
ResourceRequest m_resourceRequest;
+ HTTPHeaderMap m_originalRequestHeaders;
RefPtr<SubresourceLoader> m_loader;
ResourceLoaderOptions m_options;
ResourceResponse m_response;
CachedResourceRequest::CachedResourceRequest(ResourceRequest&& resourceRequest, const ResourceLoaderOptions& options, std::optional<ResourceLoadPriority> priority, String&& charset)
: m_resourceRequest(WTFMove(resourceRequest))
+ , m_originalRequestHeaders(m_resourceRequest.httpHeaderFields())
, m_charset(WTFMove(charset))
, m_options(options)
, m_priority(priority)
CachedResourceRequest(ResourceRequest&&, const ResourceLoaderOptions&, std::optional<ResourceLoadPriority> = std::nullopt, String&& charset = String());
ResourceRequest&& releaseResourceRequest() { return WTFMove(m_resourceRequest); }
+ HTTPHeaderMap&& releaseOriginalRequestHeaders() { return WTFMove(m_originalRequestHeaders); }
const ResourceRequest& resourceRequest() const { return m_resourceRequest; }
const String& charset() const { return m_charset; }
void setCharset(const String& charset) { m_charset = charset; }
private:
ResourceRequest m_resourceRequest;
+ HTTPHeaderMap m_originalRequestHeaders;
String m_charset;
ResourceLoaderOptions m_options;
std::optional<ResourceLoadPriority> m_priority;
return contentType;
}
-const String& octetStreamContentType()
-{
- static NeverDestroyed<const String> contentType(MAKE_STATIC_STRING_IMPL("application/octet-stream"));
- return contentType;
-}
-
const String& noCache()
{
static NeverDestroyed<const String> value(MAKE_STATIC_STRING_IMPL("no-cache"));
const String& textPlainContentType();
const String& formURLEncodedContentType();
-const String& octetStreamContentType();
const String& noCache();
const String& maxAge0();
}
+2017-08-16 Chris Dumez <cdumez@apple.com>
+
+ Cross origin Beacon requests with a ArrayBuffer / ArrayBufferView payload should not do a CORS preflight
+ https://bugs.webkit.org/show_bug.cgi?id=175628
+ <rdar://problem/33919278>
+
+ Reviewed by Geoffrey Garen.
+
+ Cross origin Beacon requests with a ArrayBuffer / ArrayBufferView payload should not do a CORS preflight.
+ To achieve this, the following changes were made:
+ 1. Revert r220779 which caused us to use a non CORS-safelisted Content-Type header for such payload
+ 2. Teach PingLoad how to deal with "simple" cross origin requests (i.e. Don't assume we need a CORS
+ preflight merely because the fetch mode is set to "cors").
+
+ * NetworkProcess/NetworkCORSPreflightChecker.cpp:
+ (WebKit::NetworkCORSPreflightChecker::startPreflight):
+ * NetworkProcess/NetworkResourceLoadParameters.cpp:
+ (WebKit::NetworkResourceLoadParameters::encode const):
+ (WebKit::NetworkResourceLoadParameters::decode):
+ * NetworkProcess/NetworkResourceLoadParameters.h:
+ * NetworkProcess/PingLoad.cpp:
+ (WebKit::PingLoad::PingLoad):
+ (WebKit::PingLoad::loadRequest):
+ (WebKit::PingLoad::originalRequestHeaders const):
+ (WebKit::PingLoad::willPerformHTTPRedirection):
+ (WebKit::PingLoad::isAllowedRedirect const):
+ (WebKit::PingLoad::makeCrossOriginAccessRequest):
+ (WebKit::PingLoad::makeSimpleCrossOriginAccessRequest):
+ (WebKit::PingLoad::makeCrossOriginAccessRequestWithPreflight):
+ (WebKit::PingLoad::preflightSuccess):
+ * NetworkProcess/PingLoad.h:
+ * WebProcess/Network/WebLoaderStrategy.cpp:
+ (WebKit::WebLoaderStrategy::createPingHandle):
+ * WebProcess/Network/WebLoaderStrategy.h:
+
2017-08-16 Yoshiaki Jitsukawa <Yoshiaki.Jitsukawa@sony.com>
[PAL] Move spi/ios and spi/win directories into PAL
#include "NetworkLoadParameters.h"
#include "SessionTracker.h"
#include <WebCore/CrossOriginAccessControl.h>
-#include <WebCore/CrossOriginPreflightResultCache.h>
#include <WebCore/SecurityOrigin.h>
#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(m_parameters.sessionID.isAlwaysOnLoggingAllowed(), Network, "%p - NetworkCORSPreflightChecker::" fmt, this, ##__VA_ARGS__)
void NetworkCORSPreflightChecker::startPreflight()
{
RELEASE_LOG_IF_ALLOWED("startPreflight");
- if (CrossOriginPreflightResultCache::singleton().canSkipPreflight(m_parameters.sourceOrigin->toString(), m_parameters.originalRequest.url(), m_parameters.allowStoredCredentials, m_parameters.originalRequest.httpMethod(), m_parameters.originalRequest.httpHeaderFields())) {
- RELEASE_LOG_IF_ALLOWED("startPreflight - preflight can be skipped thanks to cached result");
- m_completionCallback(Result::Success);
- return;
- }
NetworkLoadParameters loadParameters;
loadParameters.sessionID = m_parameters.sessionID;
encoder << SecurityOriginData::fromSecurityOrigin(*sourceOrigin);
encoder.encodeEnum(mode);
encoder << cspResponseHeaders;
+ encoder << originalRequestHeaders;
}
bool NetworkResourceLoadParameters::decode(IPC::Decoder& decoder, NetworkResourceLoadParameters& result)
return false;
if (!decoder.decode(result.cspResponseHeaders))
return false;
+ if (!decoder.decode(result.originalRequestHeaders))
+ return false;
return true;
}
#include "SandboxExtension.h"
#include <WebCore/ContentSecurityPolicyResponseHeaders.h>
#include <WebCore/FetchOptions.h>
+#include <WebCore/HTTPHeaderMap.h>
#include <WebCore/ResourceHandle.h>
#include <WebCore/ResourceLoaderOptions.h>
#include <WebCore/ResourceRequest.h>
RefPtr<WebCore::SecurityOrigin> sourceOrigin;
WebCore::FetchOptions::Mode mode;
std::optional<WebCore::ContentSecurityPolicyResponseHeaders> cspResponseHeaders;
+ std::optional<WebCore::HTTPHeaderMap> originalRequestHeaders;
};
} // namespace WebKit
#include "SessionTracker.h"
#include <WebCore/ContentSecurityPolicy.h>
#include <WebCore/CrossOriginAccessControl.h>
+#include <WebCore/CrossOriginPreflightResultCache.h>
#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(m_parameters.sessionID.isAlwaysOnLoggingAllowed(), Network, "%p - PingLoad::" fmt, this, ##__VA_ARGS__)
, m_timeoutTimer(*this, &PingLoad::timeoutTimerFired)
, m_isSameOriginRequest(securityOrigin().canRequest(m_parameters.request.url()))
{
+ ASSERT(m_parameters.sourceOrigin);
+ ASSERT(m_parameters.originalRequestHeaders);
+
// If the server never responds, this object will hang around forever.
// Set a very generous timeout, just in case.
m_timeoutTimer.startOneShot(60000_s);
- if (needsCORSPreflight(m_parameters.request))
- doCORSPreflight(m_parameters.request);
- else
- loadRequest(m_parameters.request);
+ if (m_isSameOriginRequest || m_parameters.mode == FetchOptions::Mode::NoCors) {
+ loadRequest(ResourceRequest { m_parameters.request });
+ return;
+ }
+
+ makeCrossOriginAccessRequest(ResourceRequest { m_parameters.request });
}
PingLoad::~PingLoad()
}
}
-void PingLoad::loadRequest(const ResourceRequest& request)
+void PingLoad::loadRequest(ResourceRequest&& request)
{
RELEASE_LOG_IF_ALLOWED("startNetworkLoad");
if (auto* networkSession = SessionTracker::networkSession(m_parameters.sessionID)) {
auto loadParameters = m_parameters;
- loadParameters.request = request;
+ loadParameters.request = WTFMove(request);
m_task = NetworkDataTask::create(*networkSession, *this, WTFMove(loadParameters));
m_task->resume();
} else
return m_origin ? *m_origin : *m_parameters.sourceOrigin;
}
+const HTTPHeaderMap& PingLoad::originalRequestHeaders() const
+{
+ return *m_parameters.originalRequestHeaders;
+}
+
void PingLoad::willPerformHTTPRedirection(ResourceResponse&& redirectResponse, ResourceRequest&& request, RedirectCompletionHandler&& completionHandler)
{
RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection - shouldFollowRedirects? %d", m_parameters.shouldFollowRedirects);
// FIXME: We should ensure the number of redirects does not exceed 20.
- if (!needsCORSPreflight(request)) {
- completionHandler(request);
+ if (isAllowedRedirect(request.url())) {
+ completionHandler(WTFMove(request));
return;
}
RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection - Redirect requires a CORS preflight");
+ // Force any subsequent request to use these checks.
+ m_isSameOriginRequest = false;
+
// Use a unique origin for subsequent loads if needed.
// https://fetch.spec.whatwg.org/#concept-http-redirect-fetch (Step 10).
ASSERT(m_parameters.mode == FetchOptions::Mode::Cors);
m_origin = SecurityOrigin::createUnique();
}
- m_isSameOriginRequest = false;
+ // Except in case where preflight is needed, loading should be able to continue on its own.
+ if (m_isSimpleRequest && isSimpleCrossOriginAccessRequest(request.httpMethod(), originalRequestHeaders())) {
+ completionHandler(WTFMove(request));
+ return;
+ }
+
+ m_parameters.allowStoredCredentials = DoNotAllowStoredCredentials;
m_redirectHandler = WTFMove(completionHandler);
// Let's fetch the request with the original headers (equivalent to request cloning specified by fetch algorithm).
request.setHTTPHeaderFields(m_parameters.request.httpHeaderFields());
- doCORSPreflight(request);
+ makeCrossOriginAccessRequest(WTFMove(request));
}
void PingLoad::didReceiveChallenge(const AuthenticationChallenge&, ChallengeCompletionHandler&& completionHandler)
delete this;
}
-bool PingLoad::needsCORSPreflight(const ResourceRequest& request) const
+bool PingLoad::isAllowedRedirect(const URL& url) const
{
if (m_parameters.mode == FetchOptions::Mode::NoCors)
- return false;
+ return true;
- return !m_isSameOriginRequest || !securityOrigin().canRequest(request.url());
+ return m_isSameOriginRequest && securityOrigin().canRequest(url);
}
ContentSecurityPolicy* PingLoad::contentSecurityPolicy() const
return m_contentSecurityPolicy.get();
}
-void PingLoad::doCORSPreflight(const ResourceRequest& request)
+void PingLoad::makeCrossOriginAccessRequest(ResourceRequest&& request)
+{
+ ASSERT(m_parameters.mode == FetchOptions::Mode::Cors);
+ RELEASE_LOG_IF_ALLOWED("makeCrossOriginAccessRequest");
+
+ if (isSimpleCrossOriginAccessRequest(request.httpMethod(), originalRequestHeaders())) {
+ makeSimpleCrossOriginAccessRequest(WTFMove(request));
+ return;
+ }
+
+ m_isSimpleRequest = false;
+ if (CrossOriginPreflightResultCache::singleton().canSkipPreflight(securityOrigin().toString(), request.url(), m_parameters.allowStoredCredentials, request.httpMethod(), originalRequestHeaders())) {
+ RELEASE_LOG_IF_ALLOWED("makeCrossOriginAccessRequest - preflight can be skipped thanks to cached result");
+ preflightSuccess(WTFMove(request));
+ } else
+ makeCrossOriginAccessRequestWithPreflight(WTFMove(request));
+}
+
+void PingLoad::makeSimpleCrossOriginAccessRequest(ResourceRequest&& request)
+{
+ ASSERT(isSimpleCrossOriginAccessRequest(request.httpMethod(), originalRequestHeaders()));
+ RELEASE_LOG_IF_ALLOWED("makeSimpleCrossOriginAccessRequest");
+
+ if (!request.url().protocolIsInHTTPFamily()) {
+ RELEASE_LOG_IF_ALLOWED("makeSimpleCrossOriginAccessRequest: Cross origin requests are only supported for HTTP.");
+ return;
+ }
+
+ updateRequestForAccessControl(request, securityOrigin(), m_parameters.allowStoredCredentials);
+ loadRequest(WTFMove(request));
+}
+
+void PingLoad::makeCrossOriginAccessRequestWithPreflight(ResourceRequest&& request)
{
- RELEASE_LOG_IF_ALLOWED("doCORSPreflight");
+ RELEASE_LOG_IF_ALLOWED("makeCrossOriginAccessRequestWithPreflight");
ASSERT(!m_corsPreflightChecker);
NetworkCORSPreflightChecker::Parameters parameters = {
- request,
+ WTFMove(request),
securityOrigin(),
m_parameters.sessionID,
m_parameters.allowStoredCredentials
};
m_corsPreflightChecker = std::make_unique<NetworkCORSPreflightChecker>(WTFMove(parameters), [this](NetworkCORSPreflightChecker::Result result) {
- RELEASE_LOG_IF_ALLOWED("doCORSPreflight complete, success: %d forRedirect? %d", result == NetworkCORSPreflightChecker::Result::Success, !!m_redirectHandler);
+ RELEASE_LOG_IF_ALLOWED("makeCrossOriginAccessRequestWithPreflight preflight complete, success: %d forRedirect? %d", result == NetworkCORSPreflightChecker::Result::Success, !!m_redirectHandler);
auto corsPreflightChecker = WTFMove(m_corsPreflightChecker);
- if (result == NetworkCORSPreflightChecker::Result::Success) {
- ResourceRequest actualRequest = corsPreflightChecker->originalRequest();
- updateRequestForAccessControl(actualRequest, securityOrigin(), m_parameters.allowStoredCredentials);
- if (auto redirectHandler = std::exchange(m_redirectHandler, nullptr))
- redirectHandler(actualRequest);
- else
- loadRequest(actualRequest);
- } else
+ if (result == NetworkCORSPreflightChecker::Result::Success)
+ preflightSuccess(ResourceRequest { corsPreflightChecker->originalRequest() });
+ else
delete this;
});
m_corsPreflightChecker->startPreflight();
}
+void PingLoad::preflightSuccess(ResourceRequest&& request)
+{
+ RELEASE_LOG_IF_ALLOWED("preflightSuccess");
+
+ ResourceRequest actualRequest = WTFMove(request);
+ updateRequestForAccessControl(actualRequest, securityOrigin(), m_parameters.allowStoredCredentials);
+
+ if (auto redirectHandler = std::exchange(m_redirectHandler, nullptr))
+ redirectHandler(WTFMove(actualRequest));
+ else
+ loadRequest(WTFMove(actualRequest));
+}
+
} // namespace WebKit
#endif // USE(NETWORK_SESSION)
namespace WebCore {
class ContentSecurityPolicy;
+class URL;
}
namespace WebKit {
void cannotShowURL() final;
void timeoutTimerFired();
- void loadRequest(const WebCore::ResourceRequest&);
- bool needsCORSPreflight(const WebCore::ResourceRequest&) const;
- void doCORSPreflight(const WebCore::ResourceRequest&);
+ void loadRequest(WebCore::ResourceRequest&&);
+ bool isAllowedRedirect(const WebCore::URL&) const;
+ void makeCrossOriginAccessRequest(WebCore::ResourceRequest&&);
+ void makeSimpleCrossOriginAccessRequest(WebCore::ResourceRequest&&);
+ void makeCrossOriginAccessRequestWithPreflight(WebCore::ResourceRequest&&);
+ void preflightSuccess(WebCore::ResourceRequest&&);
WebCore::SecurityOrigin& securityOrigin() const;
+ const WebCore::HTTPHeaderMap& originalRequestHeaders() const; // Needed for CORS checks.
NetworkResourceLoadParameters m_parameters;
RefPtr<NetworkDataTask> m_task;
std::unique_ptr<NetworkCORSPreflightChecker> m_corsPreflightChecker;
RefPtr<WebCore::SecurityOrigin> m_origin;
bool m_isSameOriginRequest;
+ bool m_isSimpleRequest { true };
RedirectCompletionHandler m_redirectHandler;
mutable std::unique_ptr<WebCore::ContentSecurityPolicy> m_contentSecurityPolicy;
};
}
}
-void WebLoaderStrategy::createPingHandle(NetworkingContext* networkingContext, ResourceRequest& request, Ref<SecurityOrigin>&& sourceOrigin, ContentSecurityPolicy* contentSecurityPolicy, const FetchOptions& options)
+void WebLoaderStrategy::createPingHandle(NetworkingContext* networkingContext, ResourceRequest& request, HTTPHeaderMap&& originalRequestHeaders, Ref<SecurityOrigin>&& sourceOrigin, ContentSecurityPolicy* contentSecurityPolicy, const FetchOptions& options)
{
// It's possible that call to createPingHandle might be made during initial empty Document creation before a NetworkingContext exists.
// It is not clear that we should send ping loads during that process anyways.
NetworkResourceLoadParameters loadParameters;
loadParameters.request = request;
+ loadParameters.originalRequestHeaders = WTFMove(originalRequestHeaders);
loadParameters.sourceOrigin = WTFMove(sourceOrigin);
loadParameters.sessionID = webPage ? webPage->sessionID() : SessionID::defaultSessionID();
loadParameters.allowStoredCredentials = options.credentials == FetchOptions::Credentials::Omit ? DoNotAllowStoredCredentials : AllowStoredCredentials;
if (contentSecurityPolicy)
loadParameters.cspResponseHeaders = contentSecurityPolicy->responseHeaders();
- WebProcess::singleton().networkConnection().connection().send(Messages::NetworkConnectionToWebProcess::LoadPing(loadParameters), 0);
+ WebProcess::singleton().networkConnection().connection().send(Messages::NetworkConnectionToWebProcess::LoadPing(WTFMove(loadParameters)), 0);
}
void WebLoaderStrategy::storeDerivedDataToCache(const SHA1::Digest& bodyHash, const String& type, const String& partition, WebCore::SharedBuffer& data)
void suspendPendingRequests() final;
void resumePendingRequests() final;
- void createPingHandle(WebCore::NetworkingContext*, WebCore::ResourceRequest&, Ref<WebCore::SecurityOrigin>&& sourceOrigin, WebCore::ContentSecurityPolicy*, const WebCore::FetchOptions&) final;
+ void createPingHandle(WebCore::NetworkingContext*, WebCore::ResourceRequest&, WebCore::HTTPHeaderMap&& originalRequestHeaders, Ref<WebCore::SecurityOrigin>&& sourceOrigin, WebCore::ContentSecurityPolicy*, const WebCore::FetchOptions&) final;
void storeDerivedDataToCache(const SHA1::Digest& bodyHash, const String& type, const String& partition, WebCore::SharedBuffer&) final;
+2017-08-16 Chris Dumez <cdumez@apple.com>
+
+ Cross origin Beacon requests with a ArrayBuffer / ArrayBufferView payload should not do a CORS preflight
+ https://bugs.webkit.org/show_bug.cgi?id=175628
+ <rdar://problem/33919278>
+
+ Reviewed by Geoffrey Garen.
+
+ * WebCoreSupport/WebResourceLoadScheduler.cpp:
+ (WebResourceLoadScheduler::createPingHandle):
+ * WebCoreSupport/WebResourceLoadScheduler.h:
+
2017-08-16 Yoshiaki Jitsukawa <Yoshiaki.Jitsukawa@sony.com>
[PAL] Move spi/ios and spi/win directories into PAL
return m_requestsLoading.size() >= (webResourceLoadScheduler().isSerialLoadingEnabled() ? 1 : m_maxRequestsInFlight);
}
-void WebResourceLoadScheduler::createPingHandle(NetworkingContext* networkingContext, ResourceRequest& request, Ref<SecurityOrigin>&&, WebCore::ContentSecurityPolicy*, const FetchOptions& options)
+void WebResourceLoadScheduler::createPingHandle(NetworkingContext* networkingContext, ResourceRequest& request, HTTPHeaderMap&&, Ref<SecurityOrigin>&&, WebCore::ContentSecurityPolicy*, const FetchOptions& options)
{
// PingHandle manages its own lifetime, deleting itself when its purpose has been fulfilled.
new PingHandle(networkingContext, request, options.credentials != FetchOptions::Credentials::Omit, PingHandle::UsesAsyncCallbacks::No, options.redirect == FetchOptions::Redirect::Follow);
void suspendPendingRequests() override;
void resumePendingRequests() override;
- void createPingHandle(WebCore::NetworkingContext*, WebCore::ResourceRequest&, Ref<WebCore::SecurityOrigin>&& sourceOrigin, WebCore::ContentSecurityPolicy*, const WebCore::FetchOptions&) override;
+ void createPingHandle(WebCore::NetworkingContext*, WebCore::ResourceRequest&, WebCore::HTTPHeaderMap&&, Ref<WebCore::SecurityOrigin>&& sourceOrigin, WebCore::ContentSecurityPolicy*, const WebCore::FetchOptions&) override;
void storeDerivedDataToCache(const SHA1::Digest&, const String&, const String&, WebCore::SharedBuffer&) override { }