1 /* -*- Mode: C++; tab-width: 4; 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 nsHttpConnection_h__
7 #define nsHttpConnection_h__
8 
9 #include "HttpConnectionBase.h"
10 #include "nsHttpConnectionInfo.h"
11 #include "nsHttpResponseHead.h"
12 #include "nsAHttpTransaction.h"
13 #include "nsCOMPtr.h"
14 #include "nsProxyRelease.h"
15 #include "prinrval.h"
16 #include "TunnelUtils.h"
17 #include "mozilla/Mutex.h"
18 #include "ARefBase.h"
19 #include "TimingStruct.h"
20 #include "HttpTrafficAnalyzer.h"
21 
22 #include "nsIAsyncInputStream.h"
23 #include "nsIAsyncOutputStream.h"
24 #include "nsIInterfaceRequestor.h"
25 #include "nsISupportsPriority.h"
26 #include "nsITimer.h"
27 
28 class nsISocketTransport;
29 class nsISSLSocketControl;
30 
31 namespace mozilla {
32 namespace net {
33 
34 class nsHttpHandler;
35 class ASpdySession;
36 
37 // 1dcc863e-db90-4652-a1fe-13fea0b54e46
38 #define NS_HTTPCONNECTION_IID                        \
39   {                                                  \
40     0x1dcc863e, 0xdb90, 0x4652, {                    \
41       0xa1, 0xfe, 0x13, 0xfe, 0xa0, 0xb5, 0x4e, 0x46 \
42     }                                                \
43   }
44 
45 //-----------------------------------------------------------------------------
46 // nsHttpConnection - represents a connection to a HTTP server (or proxy)
47 //
48 // NOTE: this objects lives on the socket thread only.  it should not be
49 // accessed from any other thread.
50 //-----------------------------------------------------------------------------
51 
52 class nsHttpConnection final : public HttpConnectionBase,
53                                public nsAHttpSegmentReader,
54                                public nsAHttpSegmentWriter,
55                                public nsIInputStreamCallback,
56                                public nsIOutputStreamCallback,
57                                public nsITransportEventSink,
58                                public nsIInterfaceRequestor,
59                                public NudgeTunnelCallback {
60  private:
61   virtual ~nsHttpConnection();
62 
63  public:
64   NS_DECLARE_STATIC_IID_ACCESSOR(NS_HTTPCONNECTION_IID)
65   NS_DECL_HTTPCONNECTIONBASE
66   NS_DECL_THREADSAFE_ISUPPORTS
67   NS_DECL_NSAHTTPSEGMENTREADER
68   NS_DECL_NSAHTTPSEGMENTWRITER
69   NS_DECL_NSIINPUTSTREAMCALLBACK
70   NS_DECL_NSIOUTPUTSTREAMCALLBACK
71   NS_DECL_NSITRANSPORTEVENTSINK
72   NS_DECL_NSIINTERFACEREQUESTOR
73   NS_DECL_NUDGETUNNELCALLBACK
74 
75   nsHttpConnection();
76 
77   // Initialize the connection:
78   //  info        - specifies the connection parameters.
79   //  maxHangTime - limits the amount of time this connection can spend on a
80   //                single transaction before it should no longer be kept
81   //                alive.  a value of 0xffff indicates no limit.
82   [[nodiscard]] virtual nsresult Init(nsHttpConnectionInfo* info,
83                                       uint16_t maxHangTime, nsISocketTransport*,
84                                       nsIAsyncInputStream*,
85                                       nsIAsyncOutputStream*,
86                                       bool connectedTransport, nsresult status,
87                                       nsIInterfaceRequestor*, PRIntervalTime,
88                                       bool forWebSocket);
89 
90   //-------------------------------------------------------------------------
91   // XXX document when these are ok to call
92 
IsKeepAlive()93   bool IsKeepAlive() {
94     return (mUsingSpdyVersion != SpdyVersion::NONE) ||
95            (mKeepAliveMask && mKeepAlive);
96   }
97 
98   // Returns time in seconds for how long connection can be reused.
99   uint32_t TimeToLive();
100 
NeedSpdyTunnel()101   bool NeedSpdyTunnel() {
102     return mConnInfo->UsingHttpsProxy() && !mTLSFilter &&
103            mConnInfo->UsingConnect();
104   }
105 
106   // A connection is forced into plaintext when it is intended to be used as a
107   // CONNECT tunnel but the setup fails. The plaintext only carries the CONNECT
108   // error.
ForcePlainText()109   void ForcePlainText() { mForcePlainText = true; }
110 
IsUrgentStartPreferred()111   bool IsUrgentStartPreferred() const {
112     return mUrgentStartPreferredKnown && mUrgentStartPreferred;
113   }
114   void SetUrgentStartPreferred(bool urgent);
115 
116   void SetIsReusedAfter(uint32_t afterMilliseconds);
117 
MaxBytesRead()118   int64_t MaxBytesRead() { return mMaxBytesRead; }
GetLastHttpResponseVersion()119   HttpVersion GetLastHttpResponseVersion() { return mLastHttpResponseVersion; }
120 
121   friend class HttpConnectionForceIO;
122 
123   [[nodiscard]] static nsresult ReadFromStream(nsIInputStream*, void*,
124                                                const char*, uint32_t, uint32_t,
125                                                uint32_t*);
126 
127   // When a persistent connection is in the connection manager idle
128   // connection pool, the nsHttpConnection still reads errors and hangups
129   // on the socket so that it can be proactively released if the server
130   // initiates a termination. Only call on socket thread.
131   void BeginIdleMonitoring();
132   void EndIdleMonitoring();
133 
UsingSpdy()134   bool UsingSpdy() override { return (mUsingSpdyVersion != SpdyVersion::NONE); }
GetSpdyVersion()135   SpdyVersion GetSpdyVersion() { return mUsingSpdyVersion; }
EverUsedSpdy()136   bool EverUsedSpdy() { return mEverUsedSpdy; }
UsingHttp3()137   bool UsingHttp3() override { return false; }
138 
139   // true when connection SSL NPN phase is complete and we know
140   // authoritatively whether UsingSpdy() or not.
ReportedNPN()141   bool ReportedNPN() { return mReportedSpdy; }
142 
143   // When the connection is active this is called up to once every 1 second
144   // return the interval (in seconds) that the connection next wants to
145   // have this invoked. It might happen sooner depending on the needs of
146   // other connections.
147   uint32_t ReadTimeoutTick(PRIntervalTime now);
148 
149   // For Active and Idle connections, this will be called when
150   // mTCPKeepaliveTransitionTimer fires, to check if the TCP keepalive config
151   // should move from short-lived (fast-detect) to long-lived.
152   static void UpdateTCPKeepalive(nsITimer* aTimer, void* aClosure);
153 
154   // When the connection is active this is called every second
155   void ReadTimeoutTick();
156 
ContentBytesWritten()157   int64_t ContentBytesWritten() { return mContentBytesWritten; }
158 
159   [[nodiscard]] static nsresult MakeConnectString(nsAHttpTransaction* trans,
160                                                   nsHttpRequestHead* request,
161                                                   nsACString& result,
162                                                   bool h2ws);
163   void SetupSecondaryTLS(nsAHttpTransaction* aSpdyConnectTransaction = nullptr);
164   void SetInSpdyTunnel(bool arg);
165 
166   // Check active connections for traffic (or not). SPDY connections send a
167   // ping, ordinary HTTP connections get some time to get traffic to be
168   // considered alive.
169   void CheckForTraffic(bool check);
170 
171   // NoTraffic() returns true if there's been no traffic on the (non-spdy)
172   // connection since CheckForTraffic() was called.
NoTraffic()173   bool NoTraffic() {
174     return mTrafficStamp &&
175            (mTrafficCount == (mTotalBytesWritten + mTotalBytesRead));
176   }
177 
178   // Return true when the socket this connection is using has not been
179   // authenticated using a client certificate.  Before SSL negotiation
180   // has finished this returns false.
181   bool NoClientCertAuth() const override;
182 
183   bool CanAcceptWebsocket() override;
184 
BytesWritten()185   int64_t BytesWritten() override { return mTotalBytesWritten; }
186 
Transport()187   nsISocketTransport* Transport() override { return mSocketTransport; }
188 
189   nsresult GetSelfAddr(NetAddr* addr) override;
190   nsresult GetPeerAddr(NetAddr* addr) override;
191   bool ResolvedByTRR() override;
192   bool GetEchConfigUsed() override;
193 
IsForWebSocket()194   bool IsForWebSocket() { return mForWebSocket; }
195 
196  private:
197   // Value (set in mTCPKeepaliveConfig) indicates which set of prefs to use.
198   enum TCPKeepaliveConfig {
199     kTCPKeepaliveDisabled = 0,
200     kTCPKeepaliveShortLivedConfig,
201     kTCPKeepaliveLongLivedConfig
202   };
203 
204   // called to cause the underlying socket to start speaking SSL
205   [[nodiscard]] nsresult InitSSLParams(bool connectingToProxy,
206                                        bool ProxyStartSSL);
207   [[nodiscard]] nsresult SetupNPNList(nsISSLSocketControl* ssl, uint32_t caps);
208 
209   [[nodiscard]] nsresult OnTransactionDone(nsresult reason);
210   [[nodiscard]] nsresult OnSocketWritable();
211   [[nodiscard]] nsresult OnSocketReadable();
212 
213   [[nodiscard]] nsresult SetupProxyConnect();
214 
215   PRIntervalTime IdleTime();
216   bool IsAlive();
217 
218   // Makes certain the SSL handshake is complete and NPN negotiation
219   // has had a chance to happen
220   [[nodiscard]] bool EnsureNPNComplete(nsresult& aOut0RTTWriteHandshakeValue,
221                                        uint32_t& aOut0RTTBytesWritten);
222 
223   void SetupSSL();
224 
225   // Start the Spdy transaction handler when NPN indicates spdy/*
226   void StartSpdy(nsISSLSocketControl* ssl, SpdyVersion spdyVersion);
227   // Like the above, but do the bare minimum to do 0RTT data, so we can back
228   // it out, if necessary
229   void Start0RTTSpdy(SpdyVersion spdyVersion);
230 
231   // Helpers for Start*Spdy
232   nsresult TryTakeSubTransactions(nsTArray<RefPtr<nsAHttpTransaction> >& list);
233   nsresult MoveTransactionsToSpdy(nsresult status,
234                                   nsTArray<RefPtr<nsAHttpTransaction> >& list);
235 
236   // Directly Add a transaction to an active connection for SPDY
237   [[nodiscard]] nsresult AddTransaction(nsAHttpTransaction*, int32_t);
238 
239   // Used to set TCP keepalives for fast detection of dead connections during
240   // an initial period, and slower detection for long-lived connections.
241   [[nodiscard]] nsresult StartShortLivedTCPKeepalives();
242   [[nodiscard]] nsresult StartLongLivedTCPKeepalives();
243   [[nodiscard]] nsresult DisableTCPKeepalives();
244 
245   bool CheckCanWrite0RTTData();
246 
247  private:
248   // mTransaction only points to the HTTP Transaction callbacks if the
249   // transaction is open, otherwise it is null.
250   RefPtr<nsAHttpTransaction> mTransaction;
251 
252   nsCOMPtr<nsIAsyncInputStream> mSocketIn;
253   nsCOMPtr<nsIAsyncOutputStream> mSocketOut;
254 
255   nsresult mSocketInCondition{NS_ERROR_NOT_INITIALIZED};
256   nsresult mSocketOutCondition{NS_ERROR_NOT_INITIALIZED};
257 
258   nsCOMPtr<nsIInputStream> mProxyConnectStream;
259   nsCOMPtr<nsIInputStream> mRequestStream;
260 
261   RefPtr<TLSFilterTransaction> mTLSFilter;
262   nsWeakPtr mWeakTrans;  // SpdyConnectTransaction *
263 
264   RefPtr<nsHttpHandler> mHttpHandler;  // keep gHttpHandler alive
265 
266   PRIntervalTime mLastReadTime{0};
267   PRIntervalTime mLastWriteTime{0};
268   // max download time before dropping keep-alive status
269   PRIntervalTime mMaxHangTime{0};
270   PRIntervalTime mIdleTimeout;  // value of keep-alive: timeout=
271   PRIntervalTime mConsiderReusedAfterInterval{0};
272   PRIntervalTime mConsiderReusedAfterEpoch{0};
273   int64_t mCurrentBytesRead{0};     // data read per activation
274   int64_t mMaxBytesRead{0};         // max read in 1 activation
275   int64_t mTotalBytesRead{0};       // total data read
276   int64_t mContentBytesWritten{0};  // does not include CONNECT tunnel or TLS
277 
278   RefPtr<nsIAsyncInputStream> mInputOverflow;
279 
280   // Whether the first non-null transaction dispatched on this connection was
281   // urgent-start or not
282   bool mUrgentStartPreferred{false};
283   // A flag to prevent reset of mUrgentStartPreferred by subsequent transactions
284   bool mUrgentStartPreferredKnown{false};
285   bool mConnectedTransport{false};
286   // assume to keep-alive by default
287   bool mKeepAlive{true};
288   bool mKeepAliveMask{true};
289   bool mDontReuse{false};
290   bool mIsReused{false};
291   bool mCompletedProxyConnect{false};
292   bool mLastTransactionExpectedNoContent{false};
293   bool mIdleMonitoring{false};
294   bool mProxyConnectInProgress{false};
295   bool mInSpdyTunnel{false};
296   bool mForcePlainText{false};
297 
298   // A snapshot of current number of transfered bytes
299   int64_t mTrafficCount{0};
300   bool mTrafficStamp{false};  // true then the above is set
301 
302   // The number of <= HTTP/1.1 transactions performed on this connection. This
303   // excludes spdy transactions.
304   uint32_t mHttp1xTransactionCount{0};
305 
306   // Keep-Alive: max="mRemainingConnectionUses" provides the number of future
307   // transactions (including the current one) that the server expects to allow
308   // on this persistent connection.
309   uint32_t mRemainingConnectionUses{0xffffffff};
310 
311   // SPDY related
312   bool mNPNComplete{false};
313   bool mSetupSSLCalled{false};
314 
315   // version level in use, 0 if unused
316   SpdyVersion mUsingSpdyVersion{SpdyVersion::NONE};
317 
318   RefPtr<ASpdySession> mSpdySession;
319   int32_t mPriority{nsISupportsPriority::PRIORITY_NORMAL};
320   bool mReportedSpdy{false};
321 
322   // mUsingSpdyVersion is cleared when mSpdySession is freed, this is permanent
323   bool mEverUsedSpdy{false};
324 
325   // mLastHttpResponseVersion stores the last response's http version seen.
326   HttpVersion mLastHttpResponseVersion{HttpVersion::v1_1};
327 
328   // If a large keepalive has been requested for any trans,
329   // scale the default by this factor
330   uint32_t mDefaultTimeoutFactor{1};
331 
332   bool mResponseTimeoutEnabled{false};
333 
334   // Flag to indicate connection is in inital keepalive period (fast detect).
335   uint32_t mTCPKeepaliveConfig{kTCPKeepaliveDisabled};
336   nsCOMPtr<nsITimer> mTCPKeepaliveTransitionTimer;
337 
338  private:
339   // For ForceSend()
340   static void ForceSendIO(nsITimer* aTimer, void* aClosure);
341   [[nodiscard]] nsresult MaybeForceSendIO();
342   bool mForceSendPending{false};
343   nsCOMPtr<nsITimer> mForceSendTimer;
344 
345   // Helper variable for 0RTT handshake;
346   // Possible 0RTT has been checked.
347   bool m0RTTChecked{false};
348   // We have are sending 0RTT data and we are waiting
349   // for the end of the handsake.
350   bool mWaitingFor0RTTResponse{false};
351   int64_t mContentBytesWritten0RTT{0};
352   bool mEarlyDataNegotiated{false};  // Only used for telemetry
353   nsCString mEarlyNegotiatedALPN;
354   bool mDid0RTTSpdy{false};
355 
356   nsresult mErrorBeforeConnect = NS_OK;
357 
358   nsCOMPtr<nsISocketTransport> mSocketTransport;
359 
360   bool mForWebSocket{false};
361 
362  private:
363   bool mThroughCaptivePortal;
364   int64_t mTotalBytesWritten = 0;  // does not include CONNECT tunnel
365 };
366 
367 NS_DEFINE_STATIC_IID_ACCESSOR(nsHttpConnection, NS_HTTPCONNECTION_IID)
368 
369 }  // namespace net
370 }  // namespace mozilla
371 
372 #endif  // nsHttpConnection_h__
373