| 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 "config.h" | 5 #include "config.h" |
| 6 #include "modules/fetch/Response.h" | 6 #include "modules/fetch/Response.h" |
| 7 | 7 |
| 8 #include "bindings/core/v8/Dictionary.h" | 8 #include "bindings/core/v8/Dictionary.h" |
| 9 #include "bindings/core/v8/ExceptionState.h" | 9 #include "bindings/core/v8/ExceptionState.h" |
| 10 #include "core/dom/DOMArrayBuffer.h" | 10 #include "core/dom/DOMArrayBuffer.h" |
| 11 #include "core/dom/DOMArrayBufferView.h" | 11 #include "core/dom/DOMArrayBufferView.h" |
| 12 #include "core/fileapi/Blob.h" | 12 #include "core/fileapi/Blob.h" |
| 13 #include "core/html/FormData.h" | 13 #include "core/html/FormData.h" |
| 14 #include "modules/fetch/BodyStreamBuffer.h" | 14 #include "modules/fetch/BodyStreamBuffer.h" |
| 15 #include "modules/fetch/FetchBlobDataConsumerHandle.h" | 15 #include "modules/fetch/FetchBlobDataConsumerHandle.h" |
| 16 #include "modules/fetch/FetchFormDataConsumerHandle.h" |
| 16 #include "modules/fetch/ResponseInit.h" | 17 #include "modules/fetch/ResponseInit.h" |
| 17 #include "platform/network/EncodedFormData.h" | 18 #include "platform/network/EncodedFormData.h" |
| 18 #include "platform/network/HTTPHeaderMap.h" | 19 #include "platform/network/HTTPHeaderMap.h" |
| 19 #include "public/platform/modules/serviceworker/WebServiceWorkerResponse.h" | 20 #include "public/platform/modules/serviceworker/WebServiceWorkerResponse.h" |
| 20 #include "wtf/RefPtr.h" | 21 #include "wtf/RefPtr.h" |
| 21 | 22 |
| 22 namespace blink { | 23 namespace blink { |
| 23 | 24 |
| 24 namespace { | 25 namespace { |
| 25 | 26 |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 89 || (0x80 <= c && c <= 0xFF))) // obs-text | 90 || (0x80 <= c && c <= 0xFF))) // obs-text |
| 90 return false; | 91 return false; |
| 91 } | 92 } |
| 92 return true; | 93 return true; |
| 93 } | 94 } |
| 94 | 95 |
| 95 } | 96 } |
| 96 | 97 |
| 97 Response* Response::create(ExecutionContext* context, ExceptionState& exceptionS
tate) | 98 Response* Response::create(ExecutionContext* context, ExceptionState& exceptionS
tate) |
| 98 { | 99 { |
| 99 return create(context, nullptr, ResponseInit(), exceptionState); | 100 return create(context, nullptr, String(), ResponseInit(), exceptionState); |
| 100 } | 101 } |
| 101 | 102 |
| 102 Response* Response::create(ExecutionContext* context, const BodyInit& body, cons
t Dictionary& responseInit, ExceptionState& exceptionState) | 103 Response* Response::create(ExecutionContext* context, const BodyInit& body, cons
t Dictionary& init, ExceptionState& exceptionState) |
| 103 { | 104 { |
| 104 ASSERT(!body.isNull()); | 105 ASSERT(!body.isNull()); |
| 105 if (body.isBlob()) | 106 OwnPtr<FetchDataConsumerHandle> bodyHandle; |
| 106 return create(context, body.getAsBlob(), ResponseInit(responseInit, exce
ptionState), exceptionState); | 107 String contentType; |
| 107 if (body.isUSVString()) { | 108 if (body.isBlob()) { |
| 108 OwnPtr<BlobData> blobData = BlobData::create(); | 109 bodyHandle = FetchBlobDataConsumerHandle::create(context, body.getAsBlob
()->blobDataHandle()); |
| 109 blobData->appendText(body.getAsUSVString(), false); | 110 contentType = body.getAsBlob()->type(); |
| 110 // "Set |Content-Type| to `text/plain;charset=UTF-8`." | 111 } else if (body.isUSVString()) { |
| 111 blobData->setContentType("text/plain;charset=UTF-8"); | 112 bodyHandle = FetchFormDataConsumerHandle::create(body.getAsUSVString()); |
| 112 const long long length = blobData->length(); | 113 contentType = "text/plain;charset=UTF-8"; |
| 113 Blob* blob = Blob::create(BlobDataHandle::create(blobData.release(), len
gth)); | 114 } else if (body.isArrayBuffer()) { |
| 114 return create(context, blob, ResponseInit(responseInit, exceptionState),
exceptionState); | 115 bodyHandle = FetchFormDataConsumerHandle::create(body.getAsArrayBuffer()
); |
| 116 } else if (body.isArrayBufferView()) { |
| 117 bodyHandle = FetchFormDataConsumerHandle::create(body.getAsArrayBufferVi
ew()); |
| 118 } else if (body.isFormData()) { |
| 119 RefPtr<EncodedFormData> formData = body.getAsFormData()->encodeMultiPart
FormData(); |
| 120 // Here we handle formData->boundary() as a C-style string. See |
| 121 // FormDataEncoder::generateUniqueBoundaryString. |
| 122 contentType = AtomicString("multipart/form-data; boundary=", AtomicStrin
g::ConstructFromLiteral) + formData->boundary().data(); |
| 123 bodyHandle = FetchFormDataConsumerHandle::create(context, formData.relea
se()); |
| 124 } else { |
| 125 ASSERT_NOT_REACHED(); |
| 126 return nullptr; |
| 115 } | 127 } |
| 116 if (body.isArrayBuffer()) { | 128 return create(context, bodyHandle.release(), contentType, ResponseInit(init,
exceptionState), exceptionState); |
| 117 RefPtr<DOMArrayBuffer> arrayBuffer = body.getAsArrayBuffer(); | |
| 118 OwnPtr<BlobData> blobData = BlobData::create(); | |
| 119 blobData->appendBytes(arrayBuffer->data(), arrayBuffer->byteLength()); | |
| 120 const long long length = blobData->length(); | |
| 121 Blob* blob = Blob::create(BlobDataHandle::create(blobData.release(), len
gth)); | |
| 122 return create(context, blob, ResponseInit(responseInit, exceptionState),
exceptionState); | |
| 123 } | |
| 124 if (body.isArrayBufferView()) { | |
| 125 RefPtr<DOMArrayBufferView> arrayBufferView = body.getAsArrayBufferView()
; | |
| 126 OwnPtr<BlobData> blobData = BlobData::create(); | |
| 127 blobData->appendBytes(arrayBufferView->baseAddress(), arrayBufferView->b
yteLength()); | |
| 128 const long long length = blobData->length(); | |
| 129 Blob* blob = Blob::create(BlobDataHandle::create(blobData.release(), len
gth)); | |
| 130 return create(context, blob, ResponseInit(responseInit, exceptionState),
exceptionState); | |
| 131 } | |
| 132 if (body.isFormData()) { | |
| 133 FormData* domFormData = body.getAsFormData(); | |
| 134 OwnPtr<BlobData> blobData = BlobData::create(); | |
| 135 // FIXME: the same code exist in RequestInit::RequestInit(). | |
| 136 RefPtr<EncodedFormData> httpBody = domFormData->encodeMultiPartFormData(
); | |
| 137 for (size_t i = 0; i < httpBody->elements().size(); ++i) { | |
| 138 const FormDataElement& element = httpBody->elements()[i]; | |
| 139 switch (element.m_type) { | |
| 140 case FormDataElement::data: { | |
| 141 blobData->appendBytes(element.m_data.data(), element.m_data.size
()); | |
| 142 break; | |
| 143 } | |
| 144 case FormDataElement::encodedFile: | |
| 145 blobData->appendFile(element.m_filename, element.m_fileStart, el
ement.m_fileLength, element.m_expectedFileModificationTime); | |
| 146 break; | |
| 147 case FormDataElement::encodedBlob: | |
| 148 if (element.m_optionalBlobDataHandle) | |
| 149 blobData->appendBlob(element.m_optionalBlobDataHandle, 0, el
ement.m_optionalBlobDataHandle->size()); | |
| 150 break; | |
| 151 case FormDataElement::encodedFileSystemURL: | |
| 152 blobData->appendFileSystemURL(element.m_fileSystemURL, element.m
_fileStart, element.m_fileLength, element.m_expectedFileModificationTime); | |
| 153 break; | |
| 154 default: | |
| 155 ASSERT_NOT_REACHED(); | |
| 156 } | |
| 157 } | |
| 158 blobData->setContentType(AtomicString("multipart/form-data; boundary=",
AtomicString::ConstructFromLiteral) + httpBody->boundary().data()); | |
| 159 const long long length = blobData->length(); | |
| 160 Blob* blob = Blob::create(BlobDataHandle::create(blobData.release(), len
gth)); | |
| 161 return create(context, blob, ResponseInit(responseInit, exceptionState),
exceptionState); | |
| 162 } | |
| 163 ASSERT_NOT_REACHED(); | |
| 164 return nullptr; | |
| 165 } | 129 } |
| 166 | 130 |
| 167 Response* Response::create(ExecutionContext* context, Blob* body, const Response
Init& responseInit, ExceptionState& exceptionState) | 131 Response* Response::create(ExecutionContext* context, PassOwnPtr<FetchDataConsum
erHandle> bodyHandle, const String& contentType, const ResponseInit& init, Excep
tionState& exceptionState) |
| 168 { | 132 { |
| 169 unsigned short status = responseInit.status; | 133 unsigned short status = init.status; |
| 170 | 134 |
| 171 // "1. If |init|'s status member is not in the range 200 to 599, inclusive,
throw a | 135 // "1. If |init|'s status member is not in the range 200 to 599, inclusive,
throw a |
| 172 // RangeError." | 136 // RangeError." |
| 173 if (status < 200 || 599 < status) { | 137 if (status < 200 || 599 < status) { |
| 174 exceptionState.throwRangeError(ExceptionMessages::indexOutsideRange<unsi
gned>("status", status, 200, ExceptionMessages::InclusiveBound, 599, ExceptionMe
ssages::InclusiveBound)); | 138 exceptionState.throwRangeError(ExceptionMessages::indexOutsideRange<unsi
gned>("status", status, 200, ExceptionMessages::InclusiveBound, 599, ExceptionMe
ssages::InclusiveBound)); |
| 175 return 0; | 139 return nullptr; |
| 176 } | 140 } |
| 177 | 141 |
| 178 // "2. If |init|'s statusText member does not match the Reason-Phrase | 142 // "2. If |init|'s statusText member does not match the Reason-Phrase |
| 179 // token production, throw a TypeError." | 143 // token production, throw a TypeError." |
| 180 if (!isValidReasonPhrase(responseInit.statusText)) { | 144 if (!isValidReasonPhrase(init.statusText)) { |
| 181 exceptionState.throwTypeError("Invalid statusText"); | 145 exceptionState.throwTypeError("Invalid statusText"); |
| 182 return 0; | 146 return nullptr; |
| 183 } | 147 } |
| 184 | 148 |
| 185 // "3. Let |r| be a new Response object, associated with a new response, | 149 // "3. Let |r| be a new Response object, associated with a new response, |
| 186 // Headers object, and Body object." | 150 // Headers object, and Body object." |
| 187 Response* r = new Response(context); | 151 Response* r = new Response(context); |
| 188 | 152 |
| 189 // "4. Set |r|'s response's status to |init|'s status member." | 153 // "4. Set |r|'s response's status to |init|'s status member." |
| 190 r->m_response->setStatus(responseInit.status); | 154 r->m_response->setStatus(init.status); |
| 191 | 155 |
| 192 // "5. Set |r|'s response's status message to |init|'s statusText member." | 156 // "5. Set |r|'s response's status message to |init|'s statusText member." |
| 193 r->m_response->setStatusMessage(AtomicString(responseInit.statusText)); | 157 r->m_response->setStatusMessage(AtomicString(init.statusText)); |
| 194 | 158 |
| 195 // "6. If |init|'s headers member is present, run these substeps:" | 159 // "6. If |init|'s headers member is present, run these substeps:" |
| 196 if (responseInit.headers) { | 160 if (init.headers) { |
| 197 // "1. Empty |r|'s response's header list." | 161 // "1. Empty |r|'s response's header list." |
| 198 r->m_response->headerList()->clearList(); | 162 r->m_response->headerList()->clearList(); |
| 199 // "2. Fill |r|'s Headers object with |init|'s headers member. Rethrow | 163 // "2. Fill |r|'s Headers object with |init|'s headers member. Rethrow |
| 200 // any exceptions." | 164 // any exceptions." |
| 201 r->m_headers->fillWith(responseInit.headers.get(), exceptionState); | 165 r->m_headers->fillWith(init.headers.get(), exceptionState); |
| 202 if (exceptionState.hadException()) | 166 if (exceptionState.hadException()) |
| 203 return 0; | 167 return nullptr; |
| 204 } else if (!responseInit.headersDictionary.isUndefinedOrNull()) { | 168 } else if (!init.headersDictionary.isUndefinedOrNull()) { |
| 205 // "1. Empty |r|'s response's header list." | 169 // "1. Empty |r|'s response's header list." |
| 206 r->m_response->headerList()->clearList(); | 170 r->m_response->headerList()->clearList(); |
| 207 // "2. Fill |r|'s Headers object with |init|'s headers member. Rethrow | 171 // "2. Fill |r|'s Headers object with |init|'s headers member. Rethrow |
| 208 // any exceptions." | 172 // any exceptions." |
| 209 r->m_headers->fillWith(responseInit.headersDictionary, exceptionState); | 173 r->m_headers->fillWith(init.headersDictionary, exceptionState); |
| 210 if (exceptionState.hadException()) | 174 if (exceptionState.hadException()) |
| 211 return 0; | 175 return nullptr; |
| 212 } | 176 } |
| 213 // "7. If body is given, run these substeps:" | 177 // "7. If body is given, run these substeps:" |
| 214 if (body) { | 178 if (bodyHandle) { |
| 215 // "1. If |init|'s status member is a null body status, throw a TypeErro
r." | 179 // "1. If |init|'s status member is a null body status, throw a |
| 216 // "2. Let |stream| and |Content-Type| be the result of extracting body.
" | 180 // TypeError." |
| 181 // "2. Let |stream| and |Content-Type| be the result of extracting |
| 182 // body." |
| 217 // "3. Set |r|'s response's body to |stream|." | 183 // "3. Set |r|'s response's body to |stream|." |
| 218 // "4. If |Content-Type| is non-null and |r|'s response's header list | 184 // "4. If |Content-Type| is non-null and |r|'s response's header list |
| 219 // contains no header named `Content-Type`, append `Content-Type`/ | 185 // contains no header named `Content-Type`, append `Content-Type`/ |
| 220 // |Content-Type| to |r|'s response's header list." | 186 // |Content-Type| to |r|'s response's header list." |
| 221 // https://fetch.spec.whatwg.org/#concept-bodyinit-extract | 187 // https://fetch.spec.whatwg.org/#concept-bodyinit-extract |
| 222 // Step 3, Blob: | 188 // Step 3, Blob: |
| 223 // "If object's type attribute is not the empty byte sequence, set | 189 // "If object's type attribute is not the empty byte sequence, set |
| 224 // Content-Type to its value." | 190 // Content-Type to its value." |
| 225 if (isNullBodyStatus(status)) { | 191 if (isNullBodyStatus(status)) { |
| 226 exceptionState.throwTypeError("Response with null body status cannot
have body"); | 192 exceptionState.throwTypeError("Response with null body status cannot
have body"); |
| 227 return 0; | 193 return nullptr; |
| 228 } | 194 } |
| 229 r->m_response->replaceBodyStreamBuffer(new BodyStreamBuffer(FetchBlobDat
aConsumerHandle::create(context, body->blobDataHandle()))); | 195 r->m_response->replaceBodyStreamBuffer(new BodyStreamBuffer(bodyHandle))
; |
| 230 if (!body->type().isEmpty() && !r->m_response->headerList()->has("Conten
t-Type")) | 196 if (!contentType.isEmpty() && !r->m_response->headerList()->has("Content
-Type")) |
| 231 r->m_response->headerList()->append("Content-Type", body->type()); | 197 r->m_response->headerList()->append("Content-Type", contentType); |
| 232 } | 198 } |
| 233 | 199 |
| 234 // "8. Set |r|'s MIME type to the result of extracting a MIME type | 200 // "8. Set |r|'s MIME type to the result of extracting a MIME type |
| 235 // from |r|'s response's header list." | 201 // from |r|'s response's header list." |
| 236 r->m_response->setMIMEType(r->m_response->headerList()->extractMIMEType()); | 202 r->m_response->setMIMEType(r->m_response->headerList()->extractMIMEType()); |
| 237 | 203 |
| 238 // "9. Return |r|." | 204 // "9. Return |r|." |
| 239 return r; | 205 return r; |
| 240 } | 206 } |
| 241 | 207 |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 406 } | 372 } |
| 407 | 373 |
| 408 DEFINE_TRACE(Response) | 374 DEFINE_TRACE(Response) |
| 409 { | 375 { |
| 410 Body::trace(visitor); | 376 Body::trace(visitor); |
| 411 visitor->trace(m_response); | 377 visitor->trace(m_response); |
| 412 visitor->trace(m_headers); | 378 visitor->trace(m_headers); |
| 413 } | 379 } |
| 414 | 380 |
| 415 } // namespace blink | 381 } // namespace blink |
| OLD | NEW |