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

Side by Side Diff: third_party/WebKit/Source/core/loader/BeaconLoader.cpp

Issue 2177383006: Update and fix sendBeacon() redirect behavior. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: comment spelling Created 4 years, 4 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 unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/loader/BeaconLoader.h" 5 #include "core/loader/BeaconLoader.h"
6 6
7 #include "core/dom/DOMArrayBufferView.h" 7 #include "core/dom/DOMArrayBufferView.h"
8 #include "core/dom/Document.h" 8 #include "core/dom/Document.h"
9 #include "core/fetch/CrossOriginAccessControl.h" 9 #include "core/fetch/CrossOriginAccessControl.h"
10 #include "core/fetch/FetchContext.h" 10 #include "core/fetch/FetchContext.h"
11 #include "core/fetch/FetchInitiatorTypeNames.h" 11 #include "core/fetch/FetchInitiatorTypeNames.h"
12 #include "core/fetch/FetchUtils.h"
12 #include "core/fetch/ResourceFetcher.h" 13 #include "core/fetch/ResourceFetcher.h"
13 #include "core/fileapi/File.h" 14 #include "core/fileapi/File.h"
14 #include "core/frame/LocalFrame.h" 15 #include "core/frame/LocalFrame.h"
15 #include "core/html/FormData.h" 16 #include "core/html/FormData.h"
16 #include "core/inspector/ConsoleMessage.h" 17 #include "core/inspector/ConsoleMessage.h"
17 #include "core/loader/MixedContentChecker.h" 18 #include "core/loader/MixedContentChecker.h"
18 #include "platform/exported/WrappedResourceRequest.h" 19 #include "platform/exported/WrappedResourceRequest.h"
19 #include "platform/exported/WrappedResourceResponse.h" 20 #include "platform/exported/WrappedResourceResponse.h"
20 #include "platform/network/EncodedFormData.h" 21 #include "platform/network/EncodedFormData.h"
21 #include "platform/network/ParsedContentType.h" 22 #include "platform/network/ParsedContentType.h"
22 #include "platform/network/ResourceRequest.h" 23 #include "platform/network/ResourceRequest.h"
23 #include "public/platform/WebURLRequest.h" 24 #include "public/platform/WebURLRequest.h"
24 #include "wtf/Functional.h" 25 #include "wtf/Functional.h"
25 26
26 namespace blink { 27 namespace blink {
27 28
28 namespace { 29 namespace {
29 30
30 class Beacon { 31 class Beacon {
32 STACK_ALLOCATED();
31 public: 33 public:
32 virtual bool serialize(ResourceRequest&, int, int&) const = 0; 34 virtual bool serialize(ResourceRequest&, int, int&) = 0;
33 virtual unsigned long long size() const = 0; 35 virtual unsigned long long size() const = 0;
36 virtual const AtomicString getContentType() const = 0;
tyoshino (SeeGerritForStatus) 2016/07/28 07:21:57 BeaconString returns the type including the charse
sof 2016/07/28 08:28:19 getContentType() implements what https://fetch.spe
34 }; 37 };
35 38
36 class BeaconString final : public Beacon { 39 class BeaconString final : public Beacon {
37 public: 40 public:
38 BeaconString(const String& data) 41 explicit BeaconString(const String& data)
39 : m_data(data) 42 : m_data(data)
40 { 43 {
41 } 44 }
42 45
43 unsigned long long size() const override 46 unsigned long long size() const override
44 { 47 {
45 return m_data.sizeInBytes(); 48 return m_data.sizeInBytes();
46 } 49 }
47 50
48 bool serialize(ResourceRequest& request, int, int&) const override 51 bool serialize(ResourceRequest& request, int, int&) override
49 { 52 {
50 RefPtr<EncodedFormData> entityBody = EncodedFormData::create(m_data.utf8 ()); 53 RefPtr<EncodedFormData> entityBody = EncodedFormData::create(m_data.utf8 ());
51 request.setHTTPBody(entityBody); 54 request.setHTTPBody(entityBody);
52 request.setHTTPContentType("text/plain;charset=UTF-8"); 55 request.setHTTPContentType(getContentType());
53 return true; 56 return true;
54 } 57 }
55 58
59 const AtomicString getContentType() const { return AtomicString("text/plain; charset=UTF-8"); }
60
56 private: 61 private:
57 const String m_data; 62 const String m_data;
58 }; 63 };
59 64
60 class BeaconBlob final : public Beacon { 65 class BeaconBlob final : public Beacon {
61 public: 66 public:
62 BeaconBlob(Blob* data) 67 explicit BeaconBlob(Blob* data)
63 : m_data(data) 68 : m_data(data)
64 { 69 {
65 } 70 }
66 71
67 unsigned long long size() const override 72 unsigned long long size() const override
68 { 73 {
69 return m_data->size(); 74 return m_data->size();
70 } 75 }
71 76
72 bool serialize(ResourceRequest& request, int, int&) const override 77 bool serialize(ResourceRequest& request, int, int&) override
73 { 78 {
74 ASSERT(m_data); 79 ASSERT(m_data);
75 RefPtr<EncodedFormData> entityBody = EncodedFormData::create(); 80 RefPtr<EncodedFormData> entityBody = EncodedFormData::create();
76 if (m_data->hasBackingFile()) 81 if (m_data->hasBackingFile())
77 entityBody->appendFile(toFile(m_data)->path()); 82 entityBody->appendFile(toFile(m_data)->path());
78 else 83 else
79 entityBody->appendBlob(m_data->uuid(), m_data->blobDataHandle()); 84 entityBody->appendBlob(m_data->uuid(), m_data->blobDataHandle());
80 85
81 request.setHTTPBody(entityBody.release()); 86 request.setHTTPBody(entityBody.release());
82 87
83 const String& blobType = m_data->type(); 88 const String& blobType = m_data->type();
84 if (!blobType.isEmpty() && isValidContentType(blobType)) 89 if (!blobType.isEmpty() && isValidContentType(blobType)) {
85 request.setHTTPContentType(AtomicString(blobType)); 90 m_contentType = AtomicString(blobType);
91 request.setHTTPContentType(m_contentType);
92 }
tyoshino (SeeGerritForStatus) 2016/07/28 07:21:57 do the extraction in the constructor and use it sa
sof 2016/07/28 08:28:20 Good idea, much better; done.
86 93
87 return true; 94 return true;
88 } 95 }
89 96
97 const AtomicString getContentType() const { return m_contentType; }
98
90 private: 99 private:
91 const Persistent<Blob> m_data; 100 const Member<Blob> m_data;
101 AtomicString m_contentType;
92 }; 102 };
93 103
94 class BeaconDOMArrayBufferView final : public Beacon { 104 class BeaconDOMArrayBufferView final : public Beacon {
95 public: 105 public:
96 BeaconDOMArrayBufferView(DOMArrayBufferView* data) 106 explicit BeaconDOMArrayBufferView(DOMArrayBufferView* data)
97 : m_data(data) 107 : m_data(data)
98 { 108 {
99 } 109 }
100 110
101 unsigned long long size() const override 111 unsigned long long size() const override
102 { 112 {
103 return m_data->byteLength(); 113 return m_data->byteLength();
104 } 114 }
105 115
106 bool serialize(ResourceRequest& request, int, int&) const override 116 bool serialize(ResourceRequest& request, int, int&) override
107 { 117 {
108 ASSERT(m_data); 118 ASSERT(m_data);
109 RefPtr<EncodedFormData> entityBody = EncodedFormData::create(m_data->bas eAddress(), m_data->byteLength()); 119 RefPtr<EncodedFormData> entityBody = EncodedFormData::create(m_data->bas eAddress(), m_data->byteLength());
110 request.setHTTPBody(entityBody.release()); 120 request.setHTTPBody(entityBody.release());
111 121
112 // FIXME: a reasonable choice, but not in the spec; should it give a def ault? 122 // FIXME: a reasonable choice, but not in the spec; should it give a def ault?
113 AtomicString contentType = AtomicString("application/octet-stream"); 123 AtomicString contentType = AtomicString("application/octet-stream");
114 request.setHTTPContentType(contentType); 124 request.setHTTPContentType(contentType);
115 125
116 return true; 126 return true;
117 } 127 }
118 128
129 const AtomicString getContentType() const { return nullAtom; }
tyoshino (SeeGerritForStatus) 2016/07/28 07:21:57 be consistent with L123-124?
sof 2016/07/28 08:28:19 No, that would make ArrayBuffer-backed follow CORS
tyoshino (SeeGerritForStatus) 2016/07/28 12:03:20 OK
130
119 private: 131 private:
120 const Persistent<DOMArrayBufferView> m_data; 132 const Member<DOMArrayBufferView> m_data;
121 }; 133 };
122 134
123 class BeaconFormData final : public Beacon { 135 class BeaconFormData final : public Beacon {
124 public: 136 public:
125 BeaconFormData(FormData* data) 137 explicit BeaconFormData(FormData* data)
126 : m_data(data) 138 : m_data(data)
127 { 139 {
128 } 140 }
129 141
130 unsigned long long size() const override 142 unsigned long long size() const override
131 { 143 {
132 // FormData's size cannot be determined until serialized. 144 // FormData's size cannot be determined until serialized.
133 return 0; 145 return 0;
134 } 146 }
135 147
136 bool serialize(ResourceRequest& request, int allowance, int& payloadLength) const override 148 bool serialize(ResourceRequest& request, int allowance, int& payloadLength) override
137 { 149 {
138 ASSERT(m_data); 150 ASSERT(m_data);
139 RefPtr<EncodedFormData> entityBody = m_data->encodeMultiPartFormData(); 151 RefPtr<EncodedFormData> entityBody = m_data->encodeMultiPartFormData();
140 unsigned long long entitySize = entityBody->sizeInBytes(); 152 unsigned long long entitySize = entityBody->sizeInBytes();
141 if (allowance > 0 && static_cast<unsigned long long>(allowance) < entity Size) 153 if (allowance > 0 && static_cast<unsigned long long>(allowance) < entity Size)
142 return false; 154 return false;
143 155
144 AtomicString contentType = AtomicString("multipart/form-data; boundary=" ) + entityBody->boundary().data(); 156 AtomicString contentType = AtomicString("multipart/form-data; boundary=" ) + entityBody->boundary().data();
145 request.setHTTPBody(entityBody.release()); 157 request.setHTTPBody(entityBody.release());
146 request.setHTTPContentType(contentType); 158 request.setHTTPContentType(contentType);
147 159
148 payloadLength = entitySize; 160 payloadLength = entitySize;
149 return true; 161 return true;
150 } 162 }
151 163
164 const AtomicString getContentType() const { return AtomicString("multipart/f orm-data"); }
165
152 private: 166 private:
153 const Persistent<FormData> m_data; 167 const Member<FormData> m_data;
154 }; 168 };
155 169
156 } // namespace 170 } // namespace
157 171
158 class BeaconLoader::Sender { 172 class BeaconLoader::Sender {
159 public: 173 public:
160 static bool send(LocalFrame* frame, int allowance, const KURL& beaconURL, co nst Beacon& beacon, int& payloadLength) 174 static bool send(LocalFrame* frame, int allowance, const KURL& beaconURL, Be acon& beacon, int& payloadLength)
161 { 175 {
162 if (!frame->document()) 176 if (!frame->document())
163 return false; 177 return false;
164 178
165 unsigned long long entitySize = beacon.size(); 179 unsigned long long entitySize = beacon.size();
166 if (allowance > 0 && static_cast<unsigned long long>(allowance) < entity Size) 180 if (allowance > 0 && static_cast<unsigned long long>(allowance) < entity Size)
167 return false; 181 return false;
168 182
169 ResourceRequest request(beaconURL); 183 ResourceRequest request(beaconURL);
170 request.setRequestContext(WebURLRequest::RequestContextBeacon); 184 request.setRequestContext(WebURLRequest::RequestContextBeacon);
171 request.setHTTPMethod(HTTPNames::POST); 185 request.setHTTPMethod(HTTPNames::POST);
172 request.setHTTPHeaderField(HTTPNames::Cache_Control, "max-age=0"); 186 request.setHTTPHeaderField(HTTPNames::Cache_Control, "max-age=0");
173 request.setAllowStoredCredentials(true); 187 request.setAllowStoredCredentials(true);
174 frame->document()->fetcher()->context().addAdditionalRequestHeaders(requ est, FetchSubresource); 188 frame->document()->fetcher()->context().addAdditionalRequestHeaders(requ est, FetchSubresource);
175 frame->document()->fetcher()->context().populateRequestData(request); 189 frame->document()->fetcher()->context().populateRequestData(request);
176 190
177 if (MixedContentChecker::shouldBlockFetch(frame, request, request.url()) ) 191 if (MixedContentChecker::shouldBlockFetch(frame, request, request.url()) )
178 return false; 192 return false;
179 193
180 payloadLength = entitySize; 194 payloadLength = entitySize;
181 if (!beacon.serialize(request, allowance, payloadLength)) 195 if (!beacon.serialize(request, allowance, payloadLength))
182 return false; 196 return false;
183 197
198 const AtomicString contentType = beacon.getContentType();
199 CORSEnabled corsEnabled = IsCORSEnabled;
200 if (!contentType.isNull() && FetchUtils::isSimpleHeader(AtomicString("co ntent-type"), contentType))
201 corsEnabled = NotCORSEnabled;
202
184 FetchInitiatorInfo initiatorInfo; 203 FetchInitiatorInfo initiatorInfo;
185 initiatorInfo.name = FetchInitiatorTypeNames::beacon; 204 initiatorInfo.name = FetchInitiatorTypeNames::beacon;
186 205
187 // The loader keeps itself alive until it receives a response and dispos es itself. 206 // The loader keeps itself alive until it receives a response and dispos es itself.
188 BeaconLoader* loader = new BeaconLoader(frame, request, initiatorInfo, A llowStoredCredentials); 207 BeaconLoader* loader = new BeaconLoader(frame, request, initiatorInfo, A llowStoredCredentials, corsEnabled);
189 ASSERT_UNUSED(loader, loader); 208 ASSERT_UNUSED(loader, loader);
190 return true; 209 return true;
191 } 210 }
192 }; 211 };
193 212
194 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac onURL, const String& data, int& payloadLength) 213 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac onURL, const String& data, int& payloadLength)
195 { 214 {
196 BeaconString beacon(data); 215 BeaconString beacon(data);
197 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength); 216 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength);
198 } 217 }
199 218
200 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac onURL, DOMArrayBufferView* data, int& payloadLength) 219 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac onURL, DOMArrayBufferView* data, int& payloadLength)
201 { 220 {
202 BeaconDOMArrayBufferView beacon(data); 221 BeaconDOMArrayBufferView beacon(data);
203 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength); 222 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength);
204 } 223 }
205 224
206 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac onURL, FormData* data, int& payloadLength) 225 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac onURL, FormData* data, int& payloadLength)
207 { 226 {
208 BeaconFormData beacon(data); 227 BeaconFormData beacon(data);
209 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength); 228 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength);
210 } 229 }
211 230
212 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac onURL, Blob* data, int& payloadLength) 231 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac onURL, Blob* data, int& payloadLength)
213 { 232 {
214 BeaconBlob beacon(data); 233 BeaconBlob beacon(data);
215 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength); 234 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength);
216 } 235 }
217 236
218 BeaconLoader::BeaconLoader(LocalFrame* frame, ResourceRequest& request, const Fe tchInitiatorInfo& initiatorInfo, StoredCredentials credentialsAllowed) 237 BeaconLoader::BeaconLoader(LocalFrame* frame, ResourceRequest& request, const Fe tchInitiatorInfo& initiatorInfo, StoredCredentials credentialsAllowed, CORSEnabl ed corsMode)
219 : PingLoader(frame, request, initiatorInfo, credentialsAllowed) 238 : PingLoader(frame, request, initiatorInfo, credentialsAllowed)
220 , m_beaconOrigin(frame->document()->getSecurityOrigin()) 239 , m_beaconOrigin(frame->document()->getSecurityOrigin())
240 , m_redirectsFollowCORS(corsMode == IsCORSEnabled)
221 { 241 {
222 } 242 }
223 243
224 void BeaconLoader::willFollowRedirect(WebURLLoader*, WebURLRequest& passedNewReq uest, const WebURLResponse& passedRedirectResponse, int64_t encodedDataLength) 244 void BeaconLoader::willFollowRedirect(WebURLLoader*, WebURLRequest& passedNewReq uest, const WebURLResponse& passedRedirectResponse, int64_t encodedDataLength)
225 { 245 {
226 passedNewRequest.setAllowStoredCredentials(true); 246 passedNewRequest.setAllowStoredCredentials(true);
247 if (!m_redirectsFollowCORS)
248 return;
249
227 ResourceRequest& newRequest(passedNewRequest.toMutableResourceRequest()); 250 ResourceRequest& newRequest(passedNewRequest.toMutableResourceRequest());
228 const ResourceResponse& redirectResponse(passedRedirectResponse.toResourceRe sponse()); 251 const ResourceResponse& redirectResponse(passedRedirectResponse.toResourceRe sponse());
229 252
230 ASSERT(!newRequest.isNull()); 253 ASSERT(!newRequest.isNull());
231 ASSERT(!redirectResponse.isNull()); 254 ASSERT(!redirectResponse.isNull());
232 255
233 String errorDescription; 256 String errorDescription;
234 StoredCredentials withCredentials = AllowStoredCredentials; 257 StoredCredentials withCredentials = AllowStoredCredentials;
235 ResourceLoaderOptions options; 258 ResourceLoaderOptions options;
236 if (!CrossOriginAccessControl::handleRedirect(m_beaconOrigin.get(), newReque st, redirectResponse, withCredentials, options, errorDescription)) { 259 if (!CrossOriginAccessControl::handleRedirect(m_beaconOrigin.get(), newReque st, redirectResponse, withCredentials, options, errorDescription)) {
237 if (LocalFrame* localFrame = frame()) { 260 if (LocalFrame* localFrame = frame()) {
238 if (localFrame->document()) 261 if (localFrame->document())
239 localFrame->document()->addConsoleMessage(ConsoleMessage::create (JSMessageSource, ErrorMessageLevel, errorDescription)); 262 localFrame->document()->addConsoleMessage(ConsoleMessage::create (JSMessageSource, ErrorMessageLevel, errorDescription));
240 } 263 }
241 // Cancel the load and self destruct. 264 // Cancel the load and self destruct.
242 dispose(); 265 dispose();
266 // Signal WebURLLoader that the redirect musn't be followed.
267 passedNewRequest = WebURLRequest();
tyoshino (SeeGerritForStatus) 2016/07/28 07:21:57 This is ok but I wonder if you had to add this to
sof 2016/07/28 08:28:20 It won't work without it (a longer-standing bug.)
243 return; 268 return;
244 } 269 }
245 // FIXME: http://crbug.com/427429 is needed to correctly propagate 270 // FIXME: http://crbug.com/427429 is needed to correctly propagate
246 // updates of Origin: following this successful redirect. 271 // updates of Origin: following this successful redirect.
247 } 272 }
248 273
249 } // namespace blink 274 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698