OLD | NEW |
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&) const = 0; |
33 virtual unsigned long long size() const = 0; | 35 virtual unsigned long long size() const = 0; |
| 36 virtual const AtomicString getContentType() const = 0; |
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&) const 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 { |
| 70 const String& blobType = m_data->type(); |
| 71 if (!blobType.isEmpty() && isValidContentType(blobType)) |
| 72 m_contentType = AtomicString(blobType); |
65 } | 73 } |
66 | 74 |
67 unsigned long long size() const override | 75 unsigned long long size() const override |
68 { | 76 { |
69 return m_data->size(); | 77 return m_data->size(); |
70 } | 78 } |
71 | 79 |
72 bool serialize(ResourceRequest& request, int, int&) const override | 80 bool serialize(ResourceRequest& request, int, int&) const override |
73 { | 81 { |
74 ASSERT(m_data); | 82 ASSERT(m_data); |
75 RefPtr<EncodedFormData> entityBody = EncodedFormData::create(); | 83 RefPtr<EncodedFormData> entityBody = EncodedFormData::create(); |
76 if (m_data->hasBackingFile()) | 84 if (m_data->hasBackingFile()) |
77 entityBody->appendFile(toFile(m_data)->path()); | 85 entityBody->appendFile(toFile(m_data)->path()); |
78 else | 86 else |
79 entityBody->appendBlob(m_data->uuid(), m_data->blobDataHandle()); | 87 entityBody->appendBlob(m_data->uuid(), m_data->blobDataHandle()); |
80 | 88 |
81 request.setHTTPBody(entityBody.release()); | 89 request.setHTTPBody(entityBody.release()); |
82 | 90 |
83 const String& blobType = m_data->type(); | 91 if (!m_contentType.isEmpty()) |
84 if (!blobType.isEmpty() && isValidContentType(blobType)) | 92 request.setHTTPContentType(m_contentType); |
85 request.setHTTPContentType(AtomicString(blobType)); | |
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&) const 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; } |
| 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) |
| 139 , m_entityBody(m_data->encodeMultiPartFormData()) |
127 { | 140 { |
| 141 m_contentType = AtomicString("multipart/form-data; boundary=") + m_entit
yBody->boundary().data(); |
128 } | 142 } |
129 | 143 |
130 unsigned long long size() const override | 144 unsigned long long size() const override |
131 { | 145 { |
132 // FormData's size cannot be determined until serialized. | 146 // FormData's size cannot be determined until serialized. |
133 return 0; | 147 return 0; |
134 } | 148 } |
135 | 149 |
136 bool serialize(ResourceRequest& request, int allowance, int& payloadLength)
const override | 150 bool serialize(ResourceRequest& request, int allowance, int& payloadLength)
const override |
137 { | 151 { |
138 ASSERT(m_data); | 152 unsigned long long entitySize = m_entityBody->sizeInBytes(); |
139 RefPtr<EncodedFormData> entityBody = m_data->encodeMultiPartFormData(); | |
140 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 request.setHTTPBody(m_entityBody.get()); |
145 request.setHTTPBody(entityBody.release()); | 157 request.setHTTPContentType(m_contentType); |
146 request.setHTTPContentType(contentType); | |
147 | 158 |
148 payloadLength = entitySize; | 159 payloadLength = entitySize; |
149 return true; | 160 return true; |
150 } | 161 } |
151 | 162 |
| 163 const AtomicString getContentType() const { return m_contentType; } |
| 164 |
152 private: | 165 private: |
153 const Persistent<FormData> m_data; | 166 const Member<FormData> m_data; |
| 167 RefPtr<EncodedFormData> m_entityBody; |
| 168 AtomicString m_contentType; |
154 }; | 169 }; |
155 | 170 |
156 } // namespace | 171 } // namespace |
157 | 172 |
158 class BeaconLoader::Sender { | 173 class BeaconLoader::Sender { |
159 public: | 174 public: |
160 static bool send(LocalFrame* frame, int allowance, const KURL& beaconURL, co
nst Beacon& beacon, int& payloadLength) | 175 static bool send(LocalFrame* frame, int allowance, const KURL& beaconURL, co
nst Beacon& beacon, int& payloadLength) |
161 { | 176 { |
162 if (!frame->document()) | 177 if (!frame->document()) |
163 return false; | 178 return false; |
(...skipping 10 matching lines...) Expand all Loading... |
174 frame->document()->fetcher()->context().addAdditionalRequestHeaders(requ
est, FetchSubresource); | 189 frame->document()->fetcher()->context().addAdditionalRequestHeaders(requ
est, FetchSubresource); |
175 frame->document()->fetcher()->context().populateRequestData(request); | 190 frame->document()->fetcher()->context().populateRequestData(request); |
176 | 191 |
177 if (MixedContentChecker::shouldBlockFetch(frame, request, request.url())
) | 192 if (MixedContentChecker::shouldBlockFetch(frame, request, request.url())
) |
178 return false; | 193 return false; |
179 | 194 |
180 payloadLength = entitySize; | 195 payloadLength = entitySize; |
181 if (!beacon.serialize(request, allowance, payloadLength)) | 196 if (!beacon.serialize(request, allowance, payloadLength)) |
182 return false; | 197 return false; |
183 | 198 |
| 199 const AtomicString contentType = beacon.getContentType(); |
| 200 CORSEnabled corsEnabled = IsCORSEnabled; |
| 201 if (!contentType.isNull() && FetchUtils::isSimpleHeader(AtomicString("co
ntent-type"), contentType)) |
| 202 corsEnabled = NotCORSEnabled; |
| 203 |
184 FetchInitiatorInfo initiatorInfo; | 204 FetchInitiatorInfo initiatorInfo; |
185 initiatorInfo.name = FetchInitiatorTypeNames::beacon; | 205 initiatorInfo.name = FetchInitiatorTypeNames::beacon; |
186 | 206 |
187 // The loader keeps itself alive until it receives a response and dispos
es itself. | 207 // The loader keeps itself alive until it receives a response and dispos
es itself. |
188 BeaconLoader* loader = new BeaconLoader(frame, request, initiatorInfo, A
llowStoredCredentials); | 208 BeaconLoader* loader = new BeaconLoader(frame, request, initiatorInfo, A
llowStoredCredentials, corsEnabled); |
189 ASSERT_UNUSED(loader, loader); | 209 ASSERT_UNUSED(loader, loader); |
190 return true; | 210 return true; |
191 } | 211 } |
192 }; | 212 }; |
193 | 213 |
194 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac
onURL, const String& data, int& payloadLength) | 214 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac
onURL, const String& data, int& payloadLength) |
195 { | 215 { |
196 BeaconString beacon(data); | 216 BeaconString beacon(data); |
197 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength); | 217 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength); |
198 } | 218 } |
199 | 219 |
200 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac
onURL, DOMArrayBufferView* data, int& payloadLength) | 220 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac
onURL, DOMArrayBufferView* data, int& payloadLength) |
201 { | 221 { |
202 BeaconDOMArrayBufferView beacon(data); | 222 BeaconDOMArrayBufferView beacon(data); |
203 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength); | 223 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength); |
204 } | 224 } |
205 | 225 |
206 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac
onURL, FormData* data, int& payloadLength) | 226 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac
onURL, FormData* data, int& payloadLength) |
207 { | 227 { |
208 BeaconFormData beacon(data); | 228 BeaconFormData beacon(data); |
209 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength); | 229 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength); |
210 } | 230 } |
211 | 231 |
212 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac
onURL, Blob* data, int& payloadLength) | 232 bool BeaconLoader::sendBeacon(LocalFrame* frame, int allowance, const KURL& beac
onURL, Blob* data, int& payloadLength) |
213 { | 233 { |
214 BeaconBlob beacon(data); | 234 BeaconBlob beacon(data); |
215 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength); | 235 return Sender::send(frame, allowance, beaconURL, beacon, payloadLength); |
216 } | 236 } |
217 | 237 |
218 BeaconLoader::BeaconLoader(LocalFrame* frame, ResourceRequest& request, const Fe
tchInitiatorInfo& initiatorInfo, StoredCredentials credentialsAllowed) | 238 BeaconLoader::BeaconLoader(LocalFrame* frame, ResourceRequest& request, const Fe
tchInitiatorInfo& initiatorInfo, StoredCredentials credentialsAllowed, CORSEnabl
ed corsMode) |
219 : PingLoader(frame, request, initiatorInfo, credentialsAllowed) | 239 : PingLoader(frame, request, initiatorInfo, credentialsAllowed) |
220 , m_beaconOrigin(frame->document()->getSecurityOrigin()) | 240 , m_beaconOrigin(frame->document()->getSecurityOrigin()) |
| 241 , m_redirectsFollowCORS(corsMode == IsCORSEnabled) |
221 { | 242 { |
222 } | 243 } |
223 | 244 |
224 void BeaconLoader::willFollowRedirect(WebURLLoader*, WebURLRequest& passedNewReq
uest, const WebURLResponse& passedRedirectResponse, int64_t encodedDataLength) | 245 void BeaconLoader::willFollowRedirect(WebURLLoader*, WebURLRequest& passedNewReq
uest, const WebURLResponse& passedRedirectResponse, int64_t encodedDataLength) |
225 { | 246 { |
226 passedNewRequest.setAllowStoredCredentials(true); | 247 passedNewRequest.setAllowStoredCredentials(true); |
| 248 if (!m_redirectsFollowCORS) |
| 249 return; |
| 250 |
227 ResourceRequest& newRequest(passedNewRequest.toMutableResourceRequest()); | 251 ResourceRequest& newRequest(passedNewRequest.toMutableResourceRequest()); |
228 const ResourceResponse& redirectResponse(passedRedirectResponse.toResourceRe
sponse()); | 252 const ResourceResponse& redirectResponse(passedRedirectResponse.toResourceRe
sponse()); |
229 | 253 |
230 ASSERT(!newRequest.isNull()); | 254 ASSERT(!newRequest.isNull()); |
231 ASSERT(!redirectResponse.isNull()); | 255 ASSERT(!redirectResponse.isNull()); |
232 | 256 |
233 String errorDescription; | 257 String errorDescription; |
234 StoredCredentials withCredentials = AllowStoredCredentials; | 258 StoredCredentials withCredentials = AllowStoredCredentials; |
235 ResourceLoaderOptions options; | 259 ResourceLoaderOptions options; |
236 if (!CrossOriginAccessControl::handleRedirect(m_beaconOrigin.get(), newReque
st, redirectResponse, withCredentials, options, errorDescription)) { | 260 if (!CrossOriginAccessControl::handleRedirect(m_beaconOrigin.get(), newReque
st, redirectResponse, withCredentials, options, errorDescription)) { |
237 if (LocalFrame* localFrame = frame()) { | 261 if (LocalFrame* localFrame = frame()) { |
238 if (localFrame->document()) | 262 if (localFrame->document()) |
239 localFrame->document()->addConsoleMessage(ConsoleMessage::create
(JSMessageSource, ErrorMessageLevel, errorDescription)); | 263 localFrame->document()->addConsoleMessage(ConsoleMessage::create
(JSMessageSource, ErrorMessageLevel, errorDescription)); |
240 } | 264 } |
241 // Cancel the load and self destruct. | 265 // Cancel the load and self destruct. |
242 dispose(); | 266 dispose(); |
| 267 // Signal WebURLLoader that the redirect musn't be followed. |
| 268 passedNewRequest = WebURLRequest(); |
243 return; | 269 return; |
244 } | 270 } |
245 // FIXME: http://crbug.com/427429 is needed to correctly propagate | 271 // FIXME: http://crbug.com/427429 is needed to correctly propagate |
246 // updates of Origin: following this successful redirect. | 272 // updates of Origin: following this successful redirect. |
247 } | 273 } |
248 | 274 |
249 } // namespace blink | 275 } // namespace blink |
OLD | NEW |