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&) = 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 |
OLD | NEW |