summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/http/AlternateServices.h
blob: 83a06a5855cbfa93a23ebd1b44326548dd1c121c (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
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* 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/. */

/*
Alt-Svc allows separation of transport routing from the origin host without
using a proxy. See https://httpwg.github.io/http-extensions/alt-svc.html and
https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-06

 Nice To Have Future Enhancements::
 * flush on network change event when we have an indicator
 * use established https channel for http instead separate of conninfo hash
 * pin via http-tls header
 * clear based on origin when a random fail happens not just 421
 * upon establishment of channel, cancel and retry trans that have not yet
     written anything
 * persistent storage (including private browsing filter)
 * memory reporter for cache, but this is rather tiny
*/

#ifndef mozilla_net_AlternateServices_h
#define mozilla_net_AlternateServices_h

#include "mozilla/DataStorage.h"
#include "nsRefPtrHashtable.h"
#include "nsString.h"
#include "nsIInterfaceRequestor.h"
#include "nsIStreamListener.h"
#include "nsISpeculativeConnect.h"
#include "mozilla/BasePrincipal.h"
#include "SpeculativeTransaction.h"

class nsILoadInfo;

namespace mozilla {
namespace net {

class nsProxyInfo;
class nsHttpConnectionInfo;
class nsHttpTransaction;
class nsHttpChannel;
class WellKnownChecker;

class AltSvcMapping {
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AltSvcMapping)

 private:  // ctor from ProcessHeader
  AltSvcMapping(DataStorage* storage, int32_t storageEpoch,
                const nsACString& originScheme, const nsACString& originHost,
                int32_t originPort, const nsACString& username,
                bool privateBrowsing, uint32_t expiresAt,
                const nsACString& alternateHost, int32_t alternatePort,
                const nsACString& npnToken,
                const OriginAttributes& originAttributes, bool aIsHttp3);

 public:
  AltSvcMapping(DataStorage* storage, int32_t storageEpoch,
                const nsCString& str);

  static void ProcessHeader(
      const nsCString& buf, const nsCString& originScheme,
      const nsCString& originHost, int32_t originPort,
      const nsACString& username, bool privateBrowsing,
      nsIInterfaceRequestor* callbacks, nsProxyInfo* proxyInfo, uint32_t caps,
      const OriginAttributes& originAttributes,
      bool aDontValidate = false);  // aDontValidate is only used for testing!

  // AcceptableProxy() decides whether a particular proxy configuration (pi) is
  // suitable for use with Alt-Svc. No proxy (including a null pi) is suitable.
  static bool AcceptableProxy(nsProxyInfo* proxyInfo);

  const nsCString& AlternateHost() const { return mAlternateHost; }
  const nsCString& OriginHost() const { return mOriginHost; }
  uint32_t OriginPort() const { return mOriginPort; }
  const nsCString& HashKey() const { return mHashKey; }
  uint32_t AlternatePort() const { return mAlternatePort; }
  bool Validated() { return mValidated; }
  int32_t GetExpiresAt() { return mExpiresAt; }
  bool RouteEquals(AltSvcMapping* map);
  bool HTTPS() { return mHttps; }

  void GetConnectionInfo(nsHttpConnectionInfo** outCI, nsProxyInfo* pi,
                         const OriginAttributes& originAttributes);

  int32_t TTL();
  int32_t StorageEpoch() { return mStorageEpoch; }
  bool Private() { return mPrivate; }

  void SetValidated(bool val);
  void SetMixedScheme(bool val);
  void SetExpiresAt(int32_t val);
  void SetExpired();
  void Sync();
  void SetSyncOnlyOnSuccess(bool aSOOS) { mSyncOnlyOnSuccess = aSOOS; }

  static void MakeHashKey(nsCString& outKey, const nsACString& originScheme,
                          const nsACString& originHost, int32_t originPort,
                          bool privateBrowsing,
                          const OriginAttributes& originAttributes,
                          bool aHttp3);

  bool IsHttp3() { return mIsHttp3; }
  const nsCString& NPNToken() const { return mNPNToken; }

 private:
  virtual ~AltSvcMapping() = default;
  void SyncString(const nsCString& str);
  RefPtr<DataStorage> mStorage;
  int32_t mStorageEpoch;
  void Serialize(nsCString& out);

  nsCString mHashKey;

  // If you change any of these members, update Serialize()
  nsCString mAlternateHost;
  int32_t mAlternatePort{-1};

  nsCString mOriginHost;
  int32_t mOriginPort{-1};

  nsCString mUsername;
  bool mPrivate{false};

  // alt-svc mappping
  uint32_t mExpiresAt{0};

  bool mValidated{false};
  // origin is https://
  MOZ_INIT_OUTSIDE_CTOR bool mHttps{false};
  // .wk allows http and https on same con
  MOZ_INIT_OUTSIDE_CTOR bool mMixedScheme{false};

  nsCString mNPNToken;

  OriginAttributes mOriginAttributes;

  bool mSyncOnlyOnSuccess{false};
  bool mIsHttp3{false};
};

class AltSvcOverride : public nsIInterfaceRequestor,
                       public nsISpeculativeConnectionOverrider {
 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSISPECULATIVECONNECTIONOVERRIDER
  NS_DECL_NSIINTERFACEREQUESTOR

  explicit AltSvcOverride(nsIInterfaceRequestor* aRequestor)
      : mCallbacks(aRequestor) {}

 private:
  virtual ~AltSvcOverride() = default;
  nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
};

class TransactionObserver final : public nsIStreamListener {
 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSISTREAMLISTENER
  NS_DECL_NSIREQUESTOBSERVER

  TransactionObserver(nsHttpChannel* channel, WellKnownChecker* checker);
  void Complete(bool versionOK, bool authOK, nsresult reason);

 private:
  friend class WellKnownChecker;
  virtual ~TransactionObserver() = default;

  nsCOMPtr<nsISupports> mChannelRef;
  nsHttpChannel* mChannel;
  WellKnownChecker* mChecker;
  nsCString mWKResponse;

  bool mRanOnce;
  bool mStatusOK;  // HTTP Status 200
  // These two values could be accessed on sts thread.
  Atomic<bool> mAuthOK;     // confirmed no TLS failure
  Atomic<bool> mVersionOK;  // connection h2
};

class AltSvcCache {
 public:
  AltSvcCache() = default;
  virtual ~AltSvcCache() = default;
  void UpdateAltServiceMapping(
      AltSvcMapping* map, nsProxyInfo* pi, nsIInterfaceRequestor*,
      uint32_t caps,
      const OriginAttributes& originAttributes);  // main thread
  void UpdateAltServiceMappingWithoutValidation(
      AltSvcMapping* map, nsProxyInfo* pi, nsIInterfaceRequestor*,
      uint32_t caps,
      const OriginAttributes& originAttributes);  // main thread
  already_AddRefed<AltSvcMapping> GetAltServiceMapping(
      const nsACString& scheme, const nsACString& host, int32_t port,
      bool privateBrowsing, const OriginAttributes& originAttributes,
      bool aHttp2Allowed, bool aHttp3Allowed);
  void ClearAltServiceMappings();
  void ClearHostMapping(const nsACString& host, int32_t port,
                        const OriginAttributes& originAttributes);
  void ClearHostMapping(nsHttpConnectionInfo* ci);
  DataStorage* GetStoragePtr() { return mStorage.get(); }
  int32_t StorageEpoch() { return mStorageEpoch; }
  nsresult GetAltSvcCacheKeys(nsTArray<nsCString>& value);

 private:
  void EnsureStorageInited();
  already_AddRefed<AltSvcMapping> LookupMapping(const nsCString& key,
                                                bool privateBrowsing);
  RefPtr<DataStorage> mStorage;
  int32_t mStorageEpoch{0};
};

// This class is used to write the validated result to AltSvcMapping when the
// AltSvcTransaction is closed and destroyed.
class AltSvcMappingValidator final {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AltSvcMappingValidator)

  explicit AltSvcMappingValidator(AltSvcMapping* aMap);

  void OnTransactionDestroy(bool aValidateResult);

  void OnTransactionClose(bool aValidateResult);

 protected:
  virtual ~AltSvcMappingValidator() = default;

  RefPtr<AltSvcMapping> mMapping;
};

// This is the asynchronous null transaction used to validate
// an alt-svc advertisement only for https://
// When http over socket process is enabled, this class should live only in
// socket process.
template <class Validator>
class AltSvcTransaction final : public SpeculativeTransaction {
 public:
  AltSvcTransaction(nsHttpConnectionInfo* ci, nsIInterfaceRequestor* callbacks,
                    uint32_t caps, Validator* aValidator, bool aIsHttp3);

  ~AltSvcTransaction() override;

  // AltSvcTransaction is used to validate the alt-svc record, so we don't want
  // to fetch HTTPS RR for this.
  virtual nsresult FetchHTTPSRR() override { return NS_ERROR_NOT_IMPLEMENTED; }

 private:
  // check on alternate route.
  // also evaluate 'reasonable assurances' for opportunistic security
  bool MaybeValidate(nsresult reason);

 public:
  void Close(nsresult reason) override;
  nsresult ReadSegments(nsAHttpSegmentReader* reader, uint32_t count,
                        uint32_t* countRead) override;

 private:
  RefPtr<Validator> mValidator;
  uint32_t mIsHttp3 : 1;
  uint32_t mRunning : 1;
  uint32_t mTriedToValidate : 1;
  uint32_t mTriedToWrite : 1;
  uint32_t mValidatedResult : 1;
};

}  // namespace net
}  // namespace mozilla

#endif  // include guard