summaryrefslogtreecommitdiffstats
path: root/netwerk/base/nsBaseChannel.h
blob: cacb74231b7bb5695b9c7340c4db3b470db8a650 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef nsBaseChannel_h__
#define nsBaseChannel_h__

#include "mozilla/Maybe.h"
#include "mozilla/MozPromise.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/net/NeckoTargetHolder.h"
#include "mozilla/net/PrivateBrowsingChannel.h"
#include "nsHashPropertyBag.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsIChannel.h"
#include "nsIInterfaceRequestor.h"
#include "nsILoadGroup.h"
#include "nsILoadInfo.h"
#include "nsIProgressEventSink.h"
#include "nsIStreamListener.h"
#include "nsIThreadRetargetableRequest.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "nsITransport.h"
#include "nsITransportSecurityInfo.h"
#include "nsIURI.h"
#include "nsInputStreamPump.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "nsCOMPtr.h"

class nsIInputStream;
class nsICancelable;

//-----------------------------------------------------------------------------
// nsBaseChannel is designed to be subclassed.  The subclass is responsible for
// implementing the OpenContentStream method, which will be called by the
// nsIChannel::AsyncOpen and nsIChannel::Open implementations.
//
// nsBaseChannel implements nsIInterfaceRequestor to provide a convenient way
// for subclasses to query both the nsIChannel::notificationCallbacks and
// nsILoadGroup::notificationCallbacks for supported interfaces.
//
// nsBaseChannel implements nsITransportEventSink to support progress & status
// notifications generated by the transport layer.

class nsBaseChannel
    : public nsHashPropertyBag,
      public nsIChannel,
      public nsIThreadRetargetableRequest,
      public nsIInterfaceRequestor,
      public nsITransportEventSink,
      public nsIAsyncVerifyRedirectCallback,
      public mozilla::net::PrivateBrowsingChannel<nsBaseChannel>,
      public mozilla::net::NeckoTargetHolder,
      protected nsIStreamListener,
      protected nsIThreadRetargetableStreamListener {
 public:
  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_NSIREQUEST
  NS_DECL_NSICHANNEL
  NS_DECL_NSIINTERFACEREQUESTOR
  NS_DECL_NSITRANSPORTEVENTSINK
  NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
  NS_DECL_NSITHREADRETARGETABLEREQUEST
  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER

  nsBaseChannel();

 protected:
  // -----------------------------------------------
  // Methods to be implemented by the derived class:

  virtual ~nsBaseChannel();

  using BlockingPromise = mozilla::MozPromise<nsresult, nsresult, true>;

 private:
  // Implemented by subclass to supply data stream.  The parameter, async, is
  // true when called from nsIChannel::AsyncOpen and false otherwise.  When
  // async is true, the resulting stream will be used with a nsIInputStreamPump
  // instance.  This means that if it is a non-blocking stream that supports
  // nsIAsyncInputStream that it will be read entirely on the main application
  // thread, and its AsyncWait method will be called whenever ReadSegments
  // returns NS_BASE_STREAM_WOULD_BLOCK.  Otherwise, if the stream is blocking,
  // then it will be read on one of the background I/O threads, and it does not
  // need to implement ReadSegments.  If async is false, this method may return
  // NS_ERROR_NOT_IMPLEMENTED to cause the basechannel to implement Open in
  // terms of AsyncOpen (see NS_ImplementChannelOpen).
  // A callee is allowed to return an nsIChannel instead of an nsIInputStream.
  // That case will be treated as a redirect to the new channel.  By default
  // *channel will be set to null by the caller, so callees who don't want to
  // return one an just not touch it.
  virtual nsresult OpenContentStream(bool async, nsIInputStream** stream,
                                     nsIChannel** channel) = 0;

  // Implemented by subclass to begin pumping data for an async channel, in
  // lieu of returning a stream. If implemented, OpenContentStream will never
  // be called for async channels. If not implemented, AsyncOpen will fall
  // back to OpenContentStream.
  //
  // On success, the callee must begin pumping data to the stream listener,
  // and at some point call OnStartRequest followed by OnStopRequest.
  //
  // Additionally, when a successful nsresult is returned, then the subclass
  // should be setting  through its two out params either:
  // - a request object, which may be used to suspend, resume, and cancel
  //   the underlying request.
  // - or a cancelable object (e.g. when a request can't be returned right away
  //   due to some async work needed to retrieve it). which may be used to
  //   cancel the underlying request (e.g. because the channel has been
  //   canceled)
  //
  // Not returning a request or cancelable leads to potentially leaking the
  // an underling stream pump (which would keep to be pumping data even after
  // the channel has been canceled and nothing is going to handle the data
  // available, e.g. see Bug 1706594).
  virtual nsresult BeginAsyncRead(nsIStreamListener* listener,
                                  nsIRequest** request,
                                  nsICancelable** cancelableRequest) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  // This method may return a promise that will keep the input stream pump
  // suspended until the promise is resolved or rejected.  On resolution the
  // pump is resumed.  On rejection the channel is canceled with the resulting
  // error and then the pump is also resumed to propagate the error to the
  // channel listener.  Use it to do any asynchronous/background tasks you need
  // to finish prior calling OnStartRequest of the listener.  This method is
  // called right after OpenContentStream() with async == true, after the input
  // stream pump has already been called asyncRead().
  virtual nsresult ListenerBlockingPromise(BlockingPromise** aPromise) {
    NS_ENSURE_ARG(aPromise);
    *aPromise = nullptr;
    return NS_OK;
  }

  // The basechannel calls this method from its OnTransportStatus method to
  // determine whether to call nsIProgressEventSink::OnStatus in addition to
  // nsIProgressEventSink::OnProgress.  This method may be overriden by the
  // subclass to enable nsIProgressEventSink::OnStatus events.  If this method
  // returns true, then the statusArg out param specifies the "statusArg" value
  // to pass to the OnStatus method.  By default, OnStatus messages are
  // suppressed.  The status parameter passed to this method is the status value
  // from the OnTransportStatus method.
  virtual bool GetStatusArg(nsresult status, nsString& statusArg) {
    return false;
  }

  // Called when the callbacks available to this channel may have changed.
  virtual void OnCallbacksChanged() {}

  // Called when our channel is done, to allow subclasses to drop resources.
  virtual void OnChannelDone() {}

 public:
  // ----------------------------------------------
  // Methods provided for use by the derived class:

  // Redirect to another channel.  This method takes care of notifying
  // observers of this redirect as well as of opening the new channel, if asked
  // to do so.  It also cancels |this| with the status code
  // NS_BINDING_REDIRECTED.  A failure return from this method means that the
  // redirect could not be performed (no channel was opened; this channel
  // wasn't canceled.)  The redirectFlags parameter consists of the flag values
  // defined on nsIChannelEventSink.
  nsresult Redirect(nsIChannel* newChannel, uint32_t redirectFlags,
                    bool openNewChannel);

  // Tests whether a type hint was set. Subclasses can use this to decide
  // whether to call SetContentType.
  // NOTE: This is only reliable if the subclass didn't itself call
  // SetContentType, and should also not be called after OpenContentStream.
  bool HasContentTypeHint() const;

  // The URI member should be initialized before the channel is used, and then
  // it should never be changed again until the channel is destroyed.
  nsIURI* URI() { return mURI; }
  void SetURI(nsIURI* uri) {
    NS_ASSERTION(uri, "must specify a non-null URI");
    NS_ASSERTION(!mURI, "must not modify URI");
    NS_ASSERTION(!mOriginalURI, "how did that get set so early?");
    mURI = uri;
    mOriginalURI = uri;
  }
  nsIURI* OriginalURI() { return mOriginalURI; }

  // The security info is a property of the transport-layer, which should be
  // assigned by the subclass.
  nsITransportSecurityInfo* SecurityInfo() { return mSecurityInfo; }
  void SetSecurityInfo(nsITransportSecurityInfo* info) { mSecurityInfo = info; }

  // Test the load flags
  bool HasLoadFlag(uint32_t flag) { return (mLoadFlags & flag) != 0; }

  // This is a short-cut to calling nsIRequest::IsPending()
  virtual bool Pending() const {
    return mPumpingData || mWaitingOnAsyncRedirect;
  }

  // Helper function for querying the channel's notification callbacks.
  template <class T>
  void GetCallback(nsCOMPtr<T>& result) {
    GetInterface(NS_GET_TEMPLATE_IID(T), getter_AddRefs(result));
  }

  // If a subclass does not want to feed transport-layer progress events to the
  // base channel via nsITransportEventSink, then it may set this flag to cause
  // the base channel to synthesize progress events when it receives data from
  // the content stream.  By default, progress events are not synthesized.
  void EnableSynthesizedProgressEvents(bool enable) {
    mSynthProgressEvents = enable;
  }

  // Some subclasses may wish to manually insert a stream listener between this
  // and the channel's listener.  The following methods make that possible.
  void SetStreamListener(nsIStreamListener* listener) { mListener = listener; }
  nsIStreamListener* StreamListener() { return mListener; }

 protected:
  void DisallowThreadRetargeting() { mAllowThreadRetargeting = false; }

  virtual void SetupNeckoTarget();

 private:
  NS_DECL_NSISTREAMLISTENER
  NS_DECL_NSIREQUESTOBSERVER

  // Called to setup mPump and call AsyncRead on it.
  nsresult BeginPumpingData();

  // Called when the callbacks available to this channel may have changed.
  void CallbacksChanged() {
    mProgressSink = nullptr;
    mQueriedProgressSink = false;
    OnCallbacksChanged();
  }

  // Called when our channel is done.  This should drop no-longer-needed
  // pointers.
  void ChannelDone() {
    mListener = nullptr;
    OnChannelDone();
  }

  // Handle an async redirect callback.  This will only be called if we
  // returned success from AsyncOpen while posting a redirect runnable.
  void HandleAsyncRedirect(nsIChannel* newChannel);
  void ContinueHandleAsyncRedirect(nsresult result);
  nsresult ContinueRedirect();

  // start URI classifier if requested
  void ClassifyURI();

  class RedirectRunnable : public mozilla::Runnable {
   public:
    RedirectRunnable(nsBaseChannel* chan, nsIChannel* newChannel)
        : mozilla::Runnable("nsBaseChannel::RedirectRunnable"),
          mChannel(chan),
          mNewChannel(newChannel) {
      MOZ_ASSERT(newChannel, "Must have channel to redirect to");
    }

    NS_IMETHOD Run() override {
      mChannel->HandleAsyncRedirect(mNewChannel);
      return NS_OK;
    }

   private:
    RefPtr<nsBaseChannel> mChannel;
    nsCOMPtr<nsIChannel> mNewChannel;
  };
  friend class RedirectRunnable;

  RefPtr<nsInputStreamPump> mPump;
  RefPtr<nsIRequest> mRequest;
  nsCOMPtr<nsICancelable> mCancelableAsyncRequest;
  bool mPumpingData{false};
  nsCOMPtr<nsIProgressEventSink> mProgressSink;
  nsCOMPtr<nsIURI> mOriginalURI;
  nsCOMPtr<nsISupports> mOwner;
  nsCOMPtr<nsITransportSecurityInfo> mSecurityInfo;
  nsCOMPtr<nsIChannel> mRedirectChannel;
  uint32_t mLoadFlags{LOAD_NORMAL};
  bool mQueriedProgressSink{true};
  bool mSynthProgressEvents{false};
  bool mAllowThreadRetargeting{true};
  bool mWaitingOnAsyncRedirect{false};
  bool mOpenRedirectChannel{false};
  uint32_t mRedirectFlags{0};

 protected:
  nsCString mContentType;
  nsCString mContentCharset;
  nsCOMPtr<nsIURI> mURI;
  nsCOMPtr<nsILoadGroup> mLoadGroup;
  nsCOMPtr<nsILoadInfo> mLoadInfo;
  nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
  nsCOMPtr<nsIStreamListener> mListener;
  nsresult mStatus{NS_OK};
  uint32_t mContentDispositionHint{UINT32_MAX};
  mozilla::UniquePtr<nsString> mContentDispositionFilename;
  int64_t mContentLength{-1};
  bool mWasOpened{false};
  bool mCanceled{false};

  friend class mozilla::net::PrivateBrowsingChannel<nsBaseChannel>;
};

#endif  // !nsBaseChannel_h__