1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef nsBaseChannel_h__
7 #define nsBaseChannel_h__
8 
9 #include "mozilla/net/NeckoTargetHolder.h"
10 #include "mozilla/Maybe.h"
11 #include "mozilla/MozPromise.h"
12 #include "mozilla/UniquePtr.h"
13 #include "nsString.h"
14 #include "nsCOMPtr.h"
15 #include "nsHashPropertyBag.h"
16 #include "nsInputStreamPump.h"
17 
18 #include "nsIChannel.h"
19 #include "nsIURI.h"
20 #include "nsILoadGroup.h"
21 #include "nsILoadInfo.h"
22 #include "nsIStreamListener.h"
23 #include "nsIInterfaceRequestor.h"
24 #include "nsIProgressEventSink.h"
25 #include "nsITransport.h"
26 #include "nsIAsyncVerifyRedirectCallback.h"
27 #include "nsIThreadRetargetableRequest.h"
28 #include "nsIThreadRetargetableStreamListener.h"
29 #include "mozilla/net/PrivateBrowsingChannel.h"
30 #include "nsThreadUtils.h"
31 
32 class nsIInputStream;
33 class nsICancelable;
34 
35 //-----------------------------------------------------------------------------
36 // nsBaseChannel is designed to be subclassed.  The subclass is responsible for
37 // implementing the OpenContentStream method, which will be called by the
38 // nsIChannel::AsyncOpen and nsIChannel::Open implementations.
39 //
40 // nsBaseChannel implements nsIInterfaceRequestor to provide a convenient way
41 // for subclasses to query both the nsIChannel::notificationCallbacks and
42 // nsILoadGroup::notificationCallbacks for supported interfaces.
43 //
44 // nsBaseChannel implements nsITransportEventSink to support progress & status
45 // notifications generated by the transport layer.
46 
47 class nsBaseChannel
48     : public nsHashPropertyBag,
49       public nsIChannel,
50       public nsIThreadRetargetableRequest,
51       public nsIInterfaceRequestor,
52       public nsITransportEventSink,
53       public nsIAsyncVerifyRedirectCallback,
54       public mozilla::net::PrivateBrowsingChannel<nsBaseChannel>,
55       public mozilla::net::NeckoTargetHolder,
56       protected nsIStreamListener,
57       protected nsIThreadRetargetableStreamListener {
58  public:
59   NS_DECL_ISUPPORTS_INHERITED
60   NS_DECL_NSIREQUEST
61   NS_DECL_NSICHANNEL
62   NS_DECL_NSIINTERFACEREQUESTOR
63   NS_DECL_NSITRANSPORTEVENTSINK
64   NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
65   NS_DECL_NSITHREADRETARGETABLEREQUEST
66   NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
67 
68   nsBaseChannel();
69 
70  protected:
71   // -----------------------------------------------
72   // Methods to be implemented by the derived class:
73 
74   virtual ~nsBaseChannel();
75 
76   using BlockingPromise = mozilla::MozPromise<nsresult, nsresult, true>;
77 
78  private:
79   // Implemented by subclass to supply data stream.  The parameter, async, is
80   // true when called from nsIChannel::AsyncOpen and false otherwise.  When
81   // async is true, the resulting stream will be used with a nsIInputStreamPump
82   // instance.  This means that if it is a non-blocking stream that supports
83   // nsIAsyncInputStream that it will be read entirely on the main application
84   // thread, and its AsyncWait method will be called whenever ReadSegments
85   // returns NS_BASE_STREAM_WOULD_BLOCK.  Otherwise, if the stream is blocking,
86   // then it will be read on one of the background I/O threads, and it does not
87   // need to implement ReadSegments.  If async is false, this method may return
88   // NS_ERROR_NOT_IMPLEMENTED to cause the basechannel to implement Open in
89   // terms of AsyncOpen (see NS_ImplementChannelOpen).
90   // A callee is allowed to return an nsIChannel instead of an nsIInputStream.
91   // That case will be treated as a redirect to the new channel.  By default
92   // *channel will be set to null by the caller, so callees who don't want to
93   // return one an just not touch it.
94   virtual nsresult OpenContentStream(bool async, nsIInputStream** stream,
95                                      nsIChannel** channel) = 0;
96 
97   // Implemented by subclass to begin pumping data for an async channel, in
98   // lieu of returning a stream. If implemented, OpenContentStream will never
99   // be called for async channels. If not implemented, AsyncOpen will fall
100   // back to OpenContentStream.
101   //
102   // On success, the callee must begin pumping data to the stream listener,
103   // and at some point call OnStartRequest followed by OnStopRequest.
104   //
105   // Additionally, when a successful nsresult is returned, then the subclass
106   // should be setting  through its two out params either:
107   // - a request object, which may be used to suspend, resume, and cancel
108   //   the underlying request.
109   // - or a cancelable object (e.g. when a request can't be returned right away
110   //   due to some async work needed to retrieve it). which may be used to
111   //   cancel the underlying request (e.g. because the channel has been
112   //   canceled)
113   //
114   // Not returning a request or cancelable leads to potentially leaking the
115   // an underling stream pump (which would keep to be pumping data even after
116   // the channel has been canceled and nothing is going to handle the data
117   // available, e.g. see Bug 1706594).
BeginAsyncRead(nsIStreamListener * listener,nsIRequest ** request,nsICancelable ** cancelableRequest)118   virtual nsresult BeginAsyncRead(nsIStreamListener* listener,
119                                   nsIRequest** request,
120                                   nsICancelable** cancelableRequest) {
121     return NS_ERROR_NOT_IMPLEMENTED;
122   }
123 
124   // This method may return a promise that will keep the input stream pump
125   // suspended until the promise is resolved or rejected.  On resolution the
126   // pump is resumed.  On rejection the channel is canceled with the resulting
127   // error and then the pump is also resumed to propagate the error to the
128   // channel listener.  Use it to do any asynchronous/background tasks you need
129   // to finish prior calling OnStartRequest of the listener.  This method is
130   // called right after OpenContentStream() with async == true, after the input
131   // stream pump has already been called asyncRead().
ListenerBlockingPromise(BlockingPromise ** aPromise)132   virtual nsresult ListenerBlockingPromise(BlockingPromise** aPromise) {
133     NS_ENSURE_ARG(aPromise);
134     *aPromise = nullptr;
135     return NS_OK;
136   }
137 
138   // The basechannel calls this method from its OnTransportStatus method to
139   // determine whether to call nsIProgressEventSink::OnStatus in addition to
140   // nsIProgressEventSink::OnProgress.  This method may be overriden by the
141   // subclass to enable nsIProgressEventSink::OnStatus events.  If this method
142   // returns true, then the statusArg out param specifies the "statusArg" value
143   // to pass to the OnStatus method.  By default, OnStatus messages are
144   // suppressed.  The status parameter passed to this method is the status value
145   // from the OnTransportStatus method.
GetStatusArg(nsresult status,nsString & statusArg)146   virtual bool GetStatusArg(nsresult status, nsString& statusArg) {
147     return false;
148   }
149 
150   // Called when the callbacks available to this channel may have changed.
OnCallbacksChanged()151   virtual void OnCallbacksChanged() {}
152 
153   // Called when our channel is done, to allow subclasses to drop resources.
OnChannelDone()154   virtual void OnChannelDone() {}
155 
156  public:
157   // ----------------------------------------------
158   // Methods provided for use by the derived class:
159 
160   // Redirect to another channel.  This method takes care of notifying
161   // observers of this redirect as well as of opening the new channel, if asked
162   // to do so.  It also cancels |this| with the status code
163   // NS_BINDING_REDIRECTED.  A failure return from this method means that the
164   // redirect could not be performed (no channel was opened; this channel
165   // wasn't canceled.)  The redirectFlags parameter consists of the flag values
166   // defined on nsIChannelEventSink.
167   nsresult Redirect(nsIChannel* newChannel, uint32_t redirectFlags,
168                     bool openNewChannel);
169 
170   // Tests whether a type hint was set. Subclasses can use this to decide
171   // whether to call SetContentType.
172   // NOTE: This is only reliable if the subclass didn't itself call
173   // SetContentType, and should also not be called after OpenContentStream.
174   bool HasContentTypeHint() const;
175 
176   // The URI member should be initialized before the channel is used, and then
177   // it should never be changed again until the channel is destroyed.
URI()178   nsIURI* URI() { return mURI; }
SetURI(nsIURI * uri)179   void SetURI(nsIURI* uri) {
180     NS_ASSERTION(uri, "must specify a non-null URI");
181     NS_ASSERTION(!mURI, "must not modify URI");
182     NS_ASSERTION(!mOriginalURI, "how did that get set so early?");
183     mURI = uri;
184     mOriginalURI = uri;
185   }
OriginalURI()186   nsIURI* OriginalURI() { return mOriginalURI; }
187 
188   // The security info is a property of the transport-layer, which should be
189   // assigned by the subclass.
SecurityInfo()190   nsISupports* SecurityInfo() { return mSecurityInfo; }
SetSecurityInfo(nsISupports * info)191   void SetSecurityInfo(nsISupports* info) { mSecurityInfo = info; }
192 
193   // Test the load flags
HasLoadFlag(uint32_t flag)194   bool HasLoadFlag(uint32_t flag) { return (mLoadFlags & flag) != 0; }
195 
196   // This is a short-cut to calling nsIRequest::IsPending()
Pending()197   virtual bool Pending() const {
198     return mPumpingData || mWaitingOnAsyncRedirect;
199   }
200 
201   // Helper function for querying the channel's notification callbacks.
202   template <class T>
GetCallback(nsCOMPtr<T> & result)203   void GetCallback(nsCOMPtr<T>& result) {
204     GetInterface(NS_GET_TEMPLATE_IID(T), getter_AddRefs(result));
205   }
206 
207   // If a subclass does not want to feed transport-layer progress events to the
208   // base channel via nsITransportEventSink, then it may set this flag to cause
209   // the base channel to synthesize progress events when it receives data from
210   // the content stream.  By default, progress events are not synthesized.
EnableSynthesizedProgressEvents(bool enable)211   void EnableSynthesizedProgressEvents(bool enable) {
212     mSynthProgressEvents = enable;
213   }
214 
215   // Some subclasses may wish to manually insert a stream listener between this
216   // and the channel's listener.  The following methods make that possible.
SetStreamListener(nsIStreamListener * listener)217   void SetStreamListener(nsIStreamListener* listener) { mListener = listener; }
StreamListener()218   nsIStreamListener* StreamListener() { return mListener; }
219 
220   // Pushes a new stream converter in front of the channel's stream listener.
221   // The fromType and toType values are passed to nsIStreamConverterService's
222   // AsyncConvertData method.  If invalidatesContentLength is true, then the
223   // channel's content-length property will be assigned a value of -1.  This is
224   // necessary when the converter changes the length of the resulting data
225   // stream, which is almost always the case for a "stream converter" ;-)
226   // This function optionally returns a reference to the new converter.
227   nsresult PushStreamConverter(const char* fromType, const char* toType,
228                                bool invalidatesContentLength = true,
229                                nsIStreamListener** result = nullptr);
230 
231  protected:
DisallowThreadRetargeting()232   void DisallowThreadRetargeting() { mAllowThreadRetargeting = false; }
233 
234   virtual void SetupNeckoTarget();
235 
236  private:
237   NS_DECL_NSISTREAMLISTENER
238   NS_DECL_NSIREQUESTOBSERVER
239 
240   // Called to setup mPump and call AsyncRead on it.
241   nsresult BeginPumpingData();
242 
243   // Called when the callbacks available to this channel may have changed.
CallbacksChanged()244   void CallbacksChanged() {
245     mProgressSink = nullptr;
246     mQueriedProgressSink = false;
247     OnCallbacksChanged();
248   }
249 
250   // Called when our channel is done.  This should drop no-longer-needed
251   // pointers.
ChannelDone()252   void ChannelDone() {
253     mListener = nullptr;
254     OnChannelDone();
255   }
256 
257   // Handle an async redirect callback.  This will only be called if we
258   // returned success from AsyncOpen while posting a redirect runnable.
259   void HandleAsyncRedirect(nsIChannel* newChannel);
260   void ContinueHandleAsyncRedirect(nsresult result);
261   nsresult ContinueRedirect();
262 
263   // start URI classifier if requested
264   void ClassifyURI();
265 
266   class RedirectRunnable : public mozilla::Runnable {
267    public:
RedirectRunnable(nsBaseChannel * chan,nsIChannel * newChannel)268     RedirectRunnable(nsBaseChannel* chan, nsIChannel* newChannel)
269         : mozilla::Runnable("nsBaseChannel::RedirectRunnable"),
270           mChannel(chan),
271           mNewChannel(newChannel) {
272       MOZ_ASSERT(newChannel, "Must have channel to redirect to");
273     }
274 
Run()275     NS_IMETHOD Run() override {
276       mChannel->HandleAsyncRedirect(mNewChannel);
277       return NS_OK;
278     }
279 
280    private:
281     RefPtr<nsBaseChannel> mChannel;
282     nsCOMPtr<nsIChannel> mNewChannel;
283   };
284   friend class RedirectRunnable;
285 
286   RefPtr<nsInputStreamPump> mPump;
287   RefPtr<nsIRequest> mRequest;
288   nsCOMPtr<nsICancelable> mCancelableAsyncRequest;
289   bool mPumpingData{false};
290   nsCOMPtr<nsIProgressEventSink> mProgressSink;
291   nsCOMPtr<nsIURI> mOriginalURI;
292   nsCOMPtr<nsISupports> mOwner;
293   nsCOMPtr<nsISupports> mSecurityInfo;
294   nsCOMPtr<nsIChannel> mRedirectChannel;
295   nsCString mContentType;
296   nsCString mContentCharset;
297   uint32_t mLoadFlags{LOAD_NORMAL};
298   bool mQueriedProgressSink{true};
299   bool mSynthProgressEvents{false};
300   bool mAllowThreadRetargeting{true};
301   bool mWaitingOnAsyncRedirect{false};
302   bool mOpenRedirectChannel{false};
303   uint32_t mRedirectFlags{0};
304 
305  protected:
306   nsCOMPtr<nsIURI> mURI;
307   nsCOMPtr<nsILoadGroup> mLoadGroup;
308   nsCOMPtr<nsILoadInfo> mLoadInfo;
309   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
310   nsCOMPtr<nsIStreamListener> mListener;
311   nsresult mStatus{NS_OK};
312   uint32_t mContentDispositionHint{UINT32_MAX};
313   mozilla::UniquePtr<nsString> mContentDispositionFilename;
314   int64_t mContentLength{-1};
315   bool mWasOpened{false};
316   bool mCanceled{false};
317 
318   friend class mozilla::net::PrivateBrowsingChannel<nsBaseChannel>;
319 };
320 
321 #endif  // !nsBaseChannel_h__
322