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 |