Chromium Code Reviews| 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 |