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 #ifndef mozilla_dom_InternalResponse_h
8 #define mozilla_dom_InternalResponse_h
9 
10 #include "nsIInputStream.h"
11 #include "nsISupportsImpl.h"
12 
13 #include "mozilla/dom/ResponseBinding.h"
14 #include "mozilla/dom/ChannelInfo.h"
15 #include "mozilla/UniquePtr.h"
16 
17 namespace mozilla {
18 namespace ipc {
19 class PrincipalInfo;
20 class AutoIPCStream;
21 } // namespace ipc
22 
23 namespace dom {
24 
25 class InternalHeaders;
26 class IPCInternalResponse;
27 
28 class InternalResponse final
29 {
30   friend class FetchDriver;
31 
32 public:
33   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalResponse)
34 
35   InternalResponse(uint16_t aStatus, const nsACString& aStatusText);
36 
37   static already_AddRefed<InternalResponse>
38   FromIPC(const IPCInternalResponse& aIPCResponse);
39 
40   template<typename M>
41   void
42   ToIPC(IPCInternalResponse* aIPCResponse,
43         M* aManager,
44         UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream);
45 
46   already_AddRefed<InternalResponse> Clone();
47 
48   static already_AddRefed<InternalResponse>
NetworkError()49   NetworkError()
50   {
51     RefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
52     ErrorResult result;
53     response->Headers()->SetGuard(HeadersGuardEnum::Immutable, result);
54     MOZ_ASSERT(!result.Failed());
55     response->mType = ResponseType::Error;
56     return response.forget();
57   }
58 
59   already_AddRefed<InternalResponse>
60   OpaqueResponse();
61 
62   already_AddRefed<InternalResponse>
63   OpaqueRedirectResponse();
64 
65   already_AddRefed<InternalResponse>
66   BasicResponse();
67 
68   already_AddRefed<InternalResponse>
69   CORSResponse();
70 
71   ResponseType
Type()72   Type() const
73   {
74     MOZ_ASSERT_IF(mType == ResponseType::Error, !mWrappedResponse);
75     MOZ_ASSERT_IF(mType == ResponseType::Default, !mWrappedResponse);
76     MOZ_ASSERT_IF(mType == ResponseType::Basic, mWrappedResponse);
77     MOZ_ASSERT_IF(mType == ResponseType::Cors, mWrappedResponse);
78     MOZ_ASSERT_IF(mType == ResponseType::Opaque, mWrappedResponse);
79     MOZ_ASSERT_IF(mType == ResponseType::Opaqueredirect, mWrappedResponse);
80     return mType;
81   }
82 
83   bool
IsError()84   IsError() const
85   {
86     return Type() == ResponseType::Error;
87   }
88   // GetUrl should return last fetch URL in response's url list and null if
89   // response's url list is the empty list.
90   const nsCString&
GetURL()91   GetURL() const
92   {
93     // Empty urlList when response is a synthetic response.
94     if (mURLList.IsEmpty()) {
95       return EmptyCString();
96     }
97     return mURLList.LastElement();
98   }
99   void
GetURLList(nsTArray<nsCString> & aURLList)100   GetURLList(nsTArray<nsCString>& aURLList) const
101   {
102     aURLList.Assign(mURLList);
103   }
104   const nsCString&
GetUnfilteredURL()105   GetUnfilteredURL() const
106   {
107     if (mWrappedResponse) {
108       return mWrappedResponse->GetURL();
109     }
110     return GetURL();
111   }
112   void
GetUnfilteredURLList(nsTArray<nsCString> & aURLList)113   GetUnfilteredURLList(nsTArray<nsCString>& aURLList) const
114   {
115     if (mWrappedResponse) {
116       return mWrappedResponse->GetURLList(aURLList);
117     }
118 
119     return GetURLList(aURLList);
120   }
121 
122   void
SetURLList(const nsTArray<nsCString> & aURLList)123   SetURLList(const nsTArray<nsCString>& aURLList)
124   {
125     mURLList.Assign(aURLList);
126 
127 #ifdef DEBUG
128     for(uint32_t i = 0; i < mURLList.Length(); ++i) {
129       MOZ_ASSERT(mURLList[i].Find(NS_LITERAL_CSTRING("#")) == kNotFound);
130     }
131 #endif
132   }
133 
134   uint16_t
GetStatus()135   GetStatus() const
136   {
137     return mStatus;
138   }
139 
140   uint16_t
GetUnfilteredStatus()141   GetUnfilteredStatus() const
142   {
143     if (mWrappedResponse) {
144       return mWrappedResponse->GetStatus();
145     }
146 
147     return GetStatus();
148   }
149 
150   const nsCString&
GetStatusText()151   GetStatusText() const
152   {
153     return mStatusText;
154   }
155 
156   const nsCString&
GetUnfilteredStatusText()157   GetUnfilteredStatusText() const
158   {
159     if (mWrappedResponse) {
160       return mWrappedResponse->GetStatusText();
161     }
162 
163     return GetStatusText();
164   }
165 
166   InternalHeaders*
Headers()167   Headers()
168   {
169     return mHeaders;
170   }
171 
172   InternalHeaders*
UnfilteredHeaders()173   UnfilteredHeaders()
174   {
175     if (mWrappedResponse) {
176       return mWrappedResponse->Headers();
177     };
178 
179     return Headers();
180   }
181 
182   void
183   GetUnfilteredBody(nsIInputStream** aStream, int64_t* aBodySize = nullptr)
184   {
185     if (mWrappedResponse) {
186       MOZ_ASSERT(!mBody);
187       return mWrappedResponse->GetBody(aStream, aBodySize);
188     }
189     nsCOMPtr<nsIInputStream> stream = mBody;
190     stream.forget(aStream);
191     if (aBodySize) {
192       *aBodySize = mBodySize;
193     }
194   }
195 
196   void
197   GetBody(nsIInputStream** aStream, int64_t* aBodySize = nullptr)
198   {
199     if (Type() == ResponseType::Opaque ||
200         Type() == ResponseType::Opaqueredirect) {
201       *aStream = nullptr;
202       if (aBodySize) {
203         *aBodySize = UNKNOWN_BODY_SIZE;
204       }
205       return;
206     }
207 
208     return GetUnfilteredBody(aStream, aBodySize);
209   }
210 
211   void
SetBody(nsIInputStream * aBody,int64_t aBodySize)212   SetBody(nsIInputStream* aBody, int64_t aBodySize)
213   {
214     if (mWrappedResponse) {
215       return mWrappedResponse->SetBody(aBody, aBodySize);
216     }
217     // A request's body may not be reset once set.
218     MOZ_ASSERT(!mBody);
219     MOZ_ASSERT(mBodySize == UNKNOWN_BODY_SIZE);
220     // Check arguments.
221     MOZ_ASSERT(aBodySize == UNKNOWN_BODY_SIZE || aBodySize >= 0);
222     // If body is not given, then size must be unknown.
223     MOZ_ASSERT_IF(!aBody, aBodySize == UNKNOWN_BODY_SIZE);
224 
225     mBody = aBody;
226     mBodySize = aBodySize;
227   }
228 
229   void
InitChannelInfo(nsIChannel * aChannel)230   InitChannelInfo(nsIChannel* aChannel)
231   {
232     mChannelInfo.InitFromChannel(aChannel);
233   }
234 
235   void
InitChannelInfo(const mozilla::ipc::IPCChannelInfo & aChannelInfo)236   InitChannelInfo(const mozilla::ipc::IPCChannelInfo& aChannelInfo)
237   {
238     mChannelInfo.InitFromIPCChannelInfo(aChannelInfo);
239   }
240 
241   void
InitChannelInfo(const ChannelInfo & aChannelInfo)242   InitChannelInfo(const ChannelInfo& aChannelInfo)
243   {
244     mChannelInfo = aChannelInfo;
245   }
246 
247   const ChannelInfo&
GetChannelInfo()248   GetChannelInfo() const
249   {
250     return mChannelInfo;
251   }
252 
253   const UniquePtr<mozilla::ipc::PrincipalInfo>&
GetPrincipalInfo()254   GetPrincipalInfo() const
255   {
256     return mPrincipalInfo;
257   }
258 
259   bool
IsRedirected()260   IsRedirected() const
261   {
262     return mURLList.Length() > 1;
263   }
264 
265   // Takes ownership of the principal info.
266   void
267   SetPrincipalInfo(UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo);
268 
269   LoadTainting
270   GetTainting() const;
271 
272   already_AddRefed<InternalResponse>
273   Unfiltered();
274 
275 private:
276   ~InternalResponse();
277 
278   explicit InternalResponse(const InternalResponse& aOther) = delete;
279   InternalResponse& operator=(const InternalResponse&) = delete;
280 
281   // Returns an instance of InternalResponse which is a copy of this
282   // InternalResponse, except headers, body and wrapped response (if any) which
283   // are left uninitialized. Used for cloning and filtering.
284   already_AddRefed<InternalResponse> CreateIncompleteCopy();
285 
286   ResponseType mType;
287   nsCString mTerminationReason;
288   // A response has an associated url list (a list of zero or more fetch URLs).
289   // Unless stated otherwise, it is the empty list. The current url is the last
290   // element in mURLlist
291   nsTArray<nsCString> mURLList;
292   const uint16_t mStatus;
293   const nsCString mStatusText;
294   RefPtr<InternalHeaders> mHeaders;
295   nsCOMPtr<nsIInputStream> mBody;
296   int64_t mBodySize;
297 public:
298   static const int64_t UNKNOWN_BODY_SIZE = -1;
299 private:
300   ChannelInfo mChannelInfo;
301   UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
302 
303   // For filtered responses.
304   // Cache, and SW interception should always serialize/access the underlying
305   // unfiltered headers and when deserializing, create an InternalResponse
306   // with the unfiltered headers followed by wrapping it.
307   RefPtr<InternalResponse> mWrappedResponse;
308 };
309 
310 } // namespace dom
311 } // namespace mozilla
312 
313 #endif // mozilla_dom_InternalResponse_h
314