Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "Response.h"
8 :
9 : #include "nsISupportsImpl.h"
10 : #include "nsIURI.h"
11 : #include "nsPIDOMWindow.h"
12 :
13 : #include "mozilla/ErrorResult.h"
14 : #include "mozilla/dom/FetchBinding.h"
15 : #include "mozilla/dom/Headers.h"
16 : #include "mozilla/dom/Promise.h"
17 : #include "mozilla/dom/URL.h"
18 :
19 : #include "nsDOMString.h"
20 :
21 : #include "InternalResponse.h"
22 : #include "WorkerPrivate.h"
23 :
24 : namespace mozilla {
25 : namespace dom {
26 :
27 3 : NS_IMPL_CYCLE_COLLECTING_ADDREF(Response)
28 2 : NS_IMPL_CYCLE_COLLECTING_RELEASE(Response)
29 3 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Response, mOwner, mHeaders)
30 :
31 10 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Response)
32 1 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
33 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
34 0 : NS_INTERFACE_MAP_END
35 :
36 1 : Response::Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse)
37 : : FetchBody<Response>(aGlobal)
38 1 : , mInternalResponse(aInternalResponse)
39 : {
40 1 : MOZ_ASSERT(aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Immutable ||
41 : aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Response);
42 1 : SetMimeType();
43 1 : }
44 :
45 0 : Response::~Response()
46 : {
47 0 : }
48 :
49 : /* static */ already_AddRefed<Response>
50 0 : Response::Error(const GlobalObject& aGlobal)
51 : {
52 0 : nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
53 0 : RefPtr<InternalResponse> error = InternalResponse::NetworkError();
54 0 : RefPtr<Response> r = new Response(global, error);
55 0 : return r.forget();
56 : }
57 :
58 : /* static */ already_AddRefed<Response>
59 0 : Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
60 : uint16_t aStatus, ErrorResult& aRv)
61 : {
62 0 : nsAutoString parsedURL;
63 :
64 0 : if (NS_IsMainThread()) {
65 0 : nsCOMPtr<nsIURI> baseURI;
66 0 : nsIDocument* doc = GetEntryDocument();
67 0 : if (doc) {
68 0 : baseURI = doc->GetBaseURI();
69 : }
70 0 : nsCOMPtr<nsIURI> resolvedURI;
71 0 : aRv = NS_NewURI(getter_AddRefs(resolvedURI), aUrl, nullptr, baseURI);
72 0 : if (NS_WARN_IF(aRv.Failed())) {
73 0 : return nullptr;
74 : }
75 :
76 0 : nsAutoCString spec;
77 0 : aRv = resolvedURI->GetSpec(spec);
78 0 : if (NS_WARN_IF(aRv.Failed())) {
79 0 : return nullptr;
80 : }
81 :
82 0 : CopyUTF8toUTF16(spec, parsedURL);
83 : } else {
84 0 : workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
85 0 : MOZ_ASSERT(worker);
86 0 : worker->AssertIsOnWorkerThread();
87 :
88 0 : NS_ConvertUTF8toUTF16 baseURL(worker->GetLocationInfo().mHref);
89 0 : RefPtr<URL> url = URL::WorkerConstructor(aGlobal, aUrl, baseURL, aRv);
90 0 : if (aRv.Failed()) {
91 0 : return nullptr;
92 : }
93 :
94 0 : url->Stringify(parsedURL, aRv);
95 : }
96 :
97 0 : if (aRv.Failed()) {
98 0 : return nullptr;
99 : }
100 :
101 0 : if (aStatus != 301 && aStatus != 302 && aStatus != 303 && aStatus != 307 && aStatus != 308) {
102 0 : aRv.ThrowRangeError<MSG_INVALID_REDIRECT_STATUSCODE_ERROR>();
103 0 : return nullptr;
104 : }
105 :
106 0 : Optional<fetch::BodyInit> body;
107 0 : ResponseInit init;
108 0 : init.mStatus = aStatus;
109 0 : RefPtr<Response> r = Response::Constructor(aGlobal, body, init, aRv);
110 0 : if (NS_WARN_IF(aRv.Failed())) {
111 0 : return nullptr;
112 : }
113 :
114 0 : r->GetInternalHeaders()->Set(NS_LITERAL_CSTRING("Location"),
115 0 : NS_ConvertUTF16toUTF8(parsedURL), aRv);
116 0 : if (NS_WARN_IF(aRv.Failed())) {
117 0 : return nullptr;
118 : }
119 0 : r->GetInternalHeaders()->SetGuard(HeadersGuardEnum::Immutable, aRv);
120 0 : MOZ_ASSERT(!aRv.Failed());
121 :
122 0 : return r.forget();
123 : }
124 :
125 : /*static*/ already_AddRefed<Response>
126 0 : Response::Constructor(const GlobalObject& aGlobal,
127 : const Optional<fetch::BodyInit>& aBody,
128 : const ResponseInit& aInit, ErrorResult& aRv)
129 : {
130 0 : nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
131 :
132 0 : if (aInit.mStatus < 200 || aInit.mStatus > 599) {
133 0 : aRv.ThrowRangeError<MSG_INVALID_RESPONSE_STATUSCODE_ERROR>();
134 0 : return nullptr;
135 : }
136 :
137 : // Check if the status text contains illegal characters
138 0 : nsACString::const_iterator start, end;
139 0 : aInit.mStatusText.BeginReading(start);
140 0 : aInit.mStatusText.EndReading(end);
141 0 : if (FindCharInReadable('\r', start, end)) {
142 0 : aRv.ThrowTypeError<MSG_RESPONSE_INVALID_STATUSTEXT_ERROR>();
143 0 : return nullptr;
144 : }
145 : // Reset iterator since FindCharInReadable advances it.
146 0 : aInit.mStatusText.BeginReading(start);
147 0 : if (FindCharInReadable('\n', start, end)) {
148 0 : aRv.ThrowTypeError<MSG_RESPONSE_INVALID_STATUSTEXT_ERROR>();
149 0 : return nullptr;
150 : }
151 :
152 : RefPtr<InternalResponse> internalResponse =
153 0 : new InternalResponse(aInit.mStatus, aInit.mStatusText);
154 :
155 : // Grab a valid channel info from the global so this response is 'valid' for
156 : // interception.
157 0 : if (NS_IsMainThread()) {
158 0 : ChannelInfo info;
159 0 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
160 0 : if (window) {
161 0 : nsIDocument* doc = window->GetExtantDoc();
162 0 : MOZ_ASSERT(doc);
163 0 : info.InitFromDocument(doc);
164 : } else {
165 0 : info.InitFromChromeGlobal(global);
166 : }
167 0 : internalResponse->InitChannelInfo(info);
168 : } else {
169 0 : workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
170 0 : MOZ_ASSERT(worker);
171 0 : internalResponse->InitChannelInfo(worker->GetChannelInfo());
172 : }
173 :
174 0 : RefPtr<Response> r = new Response(global, internalResponse);
175 :
176 0 : if (aInit.mHeaders.WasPassed()) {
177 0 : internalResponse->Headers()->Clear();
178 :
179 : // Instead of using Fill, create an object to allow the constructor to
180 : // unwrap the HeadersInit.
181 : RefPtr<Headers> headers =
182 0 : Headers::Create(global, aInit.mHeaders.Value(), aRv);
183 0 : if (aRv.Failed()) {
184 0 : return nullptr;
185 : }
186 :
187 0 : internalResponse->Headers()->Fill(*headers->GetInternalHeaders(), aRv);
188 0 : if (NS_WARN_IF(aRv.Failed())) {
189 0 : return nullptr;
190 : }
191 : }
192 :
193 0 : if (aBody.WasPassed()) {
194 0 : if (aInit.mStatus == 204 || aInit.mStatus == 205 || aInit.mStatus == 304) {
195 0 : aRv.ThrowTypeError<MSG_RESPONSE_NULL_STATUS_WITH_BODY>();
196 0 : return nullptr;
197 : }
198 :
199 0 : nsCOMPtr<nsIInputStream> bodyStream;
200 0 : nsCString contentTypeWithCharset;
201 0 : uint64_t bodySize = 0;
202 0 : aRv = ExtractByteStreamFromBody(aBody.Value(),
203 0 : getter_AddRefs(bodyStream),
204 : contentTypeWithCharset,
205 0 : bodySize);
206 0 : if (NS_WARN_IF(aRv.Failed())) {
207 0 : return nullptr;
208 : }
209 0 : internalResponse->SetBody(bodyStream, bodySize);
210 :
211 0 : if (!contentTypeWithCharset.IsVoid() &&
212 0 : !internalResponse->Headers()->Has(NS_LITERAL_CSTRING("Content-Type"),
213 0 : aRv)) {
214 : // Ignore Append() failing here.
215 0 : ErrorResult error;
216 0 : internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
217 0 : contentTypeWithCharset, error);
218 0 : error.SuppressException();
219 : }
220 :
221 0 : if (aRv.Failed()) {
222 0 : return nullptr;
223 : }
224 : }
225 :
226 0 : r->SetMimeType();
227 0 : return r.forget();
228 : }
229 :
230 : already_AddRefed<Response>
231 0 : Response::Clone(ErrorResult& aRv) const
232 : {
233 0 : if (BodyUsed()) {
234 0 : aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
235 0 : return nullptr;
236 : }
237 :
238 0 : RefPtr<InternalResponse> ir = mInternalResponse->Clone();
239 0 : RefPtr<Response> response = new Response(mOwner, ir);
240 0 : return response.forget();
241 : }
242 :
243 : already_AddRefed<Response>
244 0 : Response::CloneUnfiltered(ErrorResult& aRv) const
245 : {
246 0 : if (BodyUsed()) {
247 0 : aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
248 0 : return nullptr;
249 : }
250 :
251 0 : RefPtr<InternalResponse> clone = mInternalResponse->Clone();
252 0 : RefPtr<InternalResponse> ir = clone->Unfiltered();
253 0 : RefPtr<Response> ref = new Response(mOwner, ir);
254 0 : return ref.forget();
255 : }
256 :
257 : void
258 0 : Response::SetBody(nsIInputStream* aBody, int64_t aBodySize)
259 : {
260 0 : MOZ_ASSERT(!BodyUsed());
261 0 : mInternalResponse->SetBody(aBody, aBodySize);
262 0 : }
263 :
264 : already_AddRefed<InternalResponse>
265 0 : Response::GetInternalResponse() const
266 : {
267 0 : RefPtr<InternalResponse> ref = mInternalResponse;
268 0 : return ref.forget();
269 : }
270 :
271 : Headers*
272 0 : Response::Headers_()
273 : {
274 0 : if (!mHeaders) {
275 0 : mHeaders = new Headers(mOwner, mInternalResponse->Headers());
276 : }
277 :
278 0 : return mHeaders;
279 : }
280 :
281 : } // namespace dom
282 : } // namespace mozilla
|