1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "core/loader/BeaconLoader.h"
8 #include "core/dom/DOMArrayBufferView.h"
9 #include "core/dom/Document.h"
10 #include "core/fetch/CrossOriginAccessControl.h"
11 #include "core/fetch/FetchContext.h"
12 #include "core/fetch/FetchInitiatorTypeNames.h"
13 #include "core/fetch/ResourceFetcher.h"
14 #include "core/fileapi/File.h"
15 #include "core/frame/LocalFrame.h"
16 #include "core/html/FormData.h"
17 #include "core/inspector/ConsoleMessage.h"
18 #include "core/loader/MixedContentChecker.h"
19 #include "platform/exported/WrappedResourceRequest.h"
20 #include "platform/exported/WrappedResourceResponse.h"
21 #include "platform/network/EncodedFormData.h"
22 #include "platform/network/ParsedContentType.h"
23 #include "platform/network/ResourceRequest.h"
24 #include "public/platform/WebURLRequest.h"
25 #include "wtf/Functional.h"
33 virtual bool serialize(ResourceRequest
&, int, int&) const = 0;
34 virtual unsigned long long size() const = 0;
37 static unsigned long long beaconSize(const String
&);
38 static unsigned long long beaconSize(Blob
*);
39 static unsigned long long beaconSize(PassRefPtr
<DOMArrayBufferView
>);
40 static unsigned long long beaconSize(FormData
*);
42 static bool serialize(const String
&, ResourceRequest
&, int, int&);
43 static bool serialize(Blob
*, ResourceRequest
&, int, int&);
44 static bool serialize(PassRefPtr
<DOMArrayBufferView
>, ResourceRequest
&, int, int&);
45 static bool serialize(FormData
*, ResourceRequest
&, int, int&);
48 template<typename Payload
>
49 class BeaconData final
: public Beacon
{
51 BeaconData(const Payload
& data
)
56 bool serialize(ResourceRequest
& request
, int allowance
, int& payloadLength
) const override
58 return Beacon::serialize(m_data
, request
, allowance
, payloadLength
);
61 unsigned long long size() const override
63 return beaconSize(m_data
);
67 const typename
WTF::ParamStorageTraits
<Payload
>::StorageType m_data
;
72 class BeaconLoader::Sender
{
74 static bool send(LocalFrame
* frame
, int allowance
, const KURL
& beaconURL
, const Beacon
& beacon
, int& payloadLength
)
76 if (!frame
->document())
79 unsigned long long entitySize
= beacon
.size();
80 if (allowance
> 0 && static_cast<unsigned long long>(allowance
) < entitySize
)
83 ResourceRequest
request(beaconURL
);
84 request
.setRequestContext(WebURLRequest::RequestContextBeacon
);
85 request
.setHTTPMethod("POST");
86 request
.setHTTPHeaderField("Cache-Control", "max-age=0");
87 request
.setAllowStoredCredentials(true);
88 frame
->document()->fetcher()->context().addAdditionalRequestHeaders(request
, FetchSubresource
);
89 frame
->document()->fetcher()->context().setFirstPartyForCookies(request
);
91 if (MixedContentChecker::shouldBlockFetch(frame
, request
, request
.url()))
94 payloadLength
= entitySize
;
95 if (!beacon
.serialize(request
, allowance
, payloadLength
))
98 FetchInitiatorInfo initiatorInfo
;
99 initiatorInfo
.name
= FetchInitiatorTypeNames::beacon
;
101 // Leak the loader, since it will kill itself as soon as it receives a response.
102 RefPtrWillBeRawPtr
<BeaconLoader
> loader
= adoptRefWillBeNoop(new BeaconLoader(frame
, request
, initiatorInfo
, AllowStoredCredentials
));
108 bool BeaconLoader::sendBeacon(LocalFrame
* frame
, int allowance
, const KURL
& beaconURL
, const String
& data
, int& payloadLength
)
110 BeaconData
<decltype(data
)> beacon(data
);
111 return Sender::send(frame
, allowance
, beaconURL
, beacon
, payloadLength
);
114 bool BeaconLoader::sendBeacon(LocalFrame
* frame
, int allowance
, const KURL
& beaconURL
, PassRefPtr
<DOMArrayBufferView
> data
, int& payloadLength
)
116 BeaconData
<decltype(data
)> beacon(data
);
117 return Sender::send(frame
, allowance
, beaconURL
, beacon
, payloadLength
);
120 bool BeaconLoader::sendBeacon(LocalFrame
* frame
, int allowance
, const KURL
& beaconURL
, FormData
* data
, int& payloadLength
)
122 BeaconData
<decltype(data
)> beacon(data
);
123 return Sender::send(frame
, allowance
, beaconURL
, beacon
, payloadLength
);
126 bool BeaconLoader::sendBeacon(LocalFrame
* frame
, int allowance
, const KURL
& beaconURL
, Blob
* data
, int& payloadLength
)
128 BeaconData
<decltype(data
)> beacon(data
);
129 return Sender::send(frame
, allowance
, beaconURL
, beacon
, payloadLength
);
132 BeaconLoader::BeaconLoader(LocalFrame
* frame
, ResourceRequest
& request
, const FetchInitiatorInfo
& initiatorInfo
, StoredCredentials credentialsAllowed
)
133 : PingLoader(frame
, request
, initiatorInfo
, credentialsAllowed
)
134 , m_beaconOrigin(frame
->document()->securityOrigin())
138 void BeaconLoader::willSendRequest(WebURLLoader
*, WebURLRequest
& passedNewRequest
, const WebURLResponse
& passedRedirectResponse
)
140 passedNewRequest
.setAllowStoredCredentials(true);
141 ResourceRequest
& newRequest(passedNewRequest
.toMutableResourceRequest());
142 const ResourceResponse
& redirectResponse(passedRedirectResponse
.toResourceResponse());
144 ASSERT(!newRequest
.isNull());
145 ASSERT(!redirectResponse
.isNull());
147 String errorDescription
;
148 StoredCredentials withCredentials
= AllowStoredCredentials
;
149 ResourceLoaderOptions options
;
150 if (!CrossOriginAccessControl::handleRedirect(m_beaconOrigin
.get(), newRequest
, redirectResponse
, withCredentials
, options
, errorDescription
)) {
151 if (LocalFrame
* localFrame
= frame()) {
152 if (localFrame
->document())
153 localFrame
->document()->addConsoleMessage(ConsoleMessage::create(JSMessageSource
, ErrorMessageLevel
, errorDescription
));
155 // Cancel the load and self destruct.
159 // FIXME: http://crbug.com/427429 is needed to correctly propagate
160 // updates of Origin: following this successful redirect.
165 unsigned long long Beacon::beaconSize(const String
& data
)
167 return data
.sizeInBytes();
170 bool Beacon::serialize(const String
& data
, ResourceRequest
& request
, int, int&)
172 RefPtr
<EncodedFormData
> entityBody
= EncodedFormData::create(data
.utf8());
173 request
.setHTTPBody(entityBody
);
174 request
.setHTTPContentType("text/plain;charset=UTF-8");
178 unsigned long long Beacon::beaconSize(Blob
* data
)
183 bool Beacon::serialize(Blob
* data
, ResourceRequest
& request
, int, int&)
186 RefPtr
<EncodedFormData
> entityBody
= EncodedFormData::create();
187 if (data
->hasBackingFile())
188 entityBody
->appendFile(toFile(data
)->path());
190 entityBody
->appendBlob(data
->uuid(), data
->blobDataHandle());
192 request
.setHTTPBody(entityBody
.release());
194 const String
& blobType
= data
->type();
195 if (!blobType
.isEmpty() && isValidContentType(blobType
))
196 request
.setHTTPContentType(AtomicString(blobType
));
201 unsigned long long Beacon::beaconSize(PassRefPtr
<DOMArrayBufferView
> data
)
203 return data
->byteLength();
206 bool Beacon::serialize(PassRefPtr
<DOMArrayBufferView
> data
, ResourceRequest
& request
, int, int&)
209 RefPtr
<EncodedFormData
> entityBody
= EncodedFormData::create(data
->baseAddress(), data
->byteLength());
210 request
.setHTTPBody(entityBody
.release());
212 // FIXME: a reasonable choice, but not in the spec; should it give a default?
213 AtomicString contentType
= AtomicString("application/octet-stream");
214 request
.setHTTPContentType(contentType
);
219 unsigned long long Beacon::beaconSize(FormData
*)
221 // FormData's size cannot be determined until serialized.
225 bool Beacon::serialize(FormData
* data
, ResourceRequest
& request
, int allowance
, int& payloadLength
)
228 RefPtr
<EncodedFormData
> entityBody
= data
->encodeMultiPartFormData();
229 unsigned long long entitySize
= entityBody
->sizeInBytes();
230 if (allowance
> 0 && static_cast<unsigned long long>(allowance
) < entitySize
)
233 AtomicString contentType
= AtomicString("multipart/form-data; boundary=", AtomicString::ConstructFromLiteral
) + entityBody
->boundary().data();
234 request
.setHTTPBody(entityBody
.release());
235 request
.setHTTPContentType(contentType
);
237 payloadLength
= entitySize
;