• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package android.net.http;
6 
7 import android.annotation.IntDef;
8 import android.annotation.SuppressLint;
9 import android.os.Build.VERSION_CODES;
10 
11 import androidx.annotation.NonNull;
12 import androidx.annotation.Nullable;
13 import androidx.annotation.RequiresApi;
14 
15 import java.lang.annotation.Retention;
16 import java.lang.annotation.RetentionPolicy;
17 import java.time.Duration;
18 
19 /**
20  * A class configuring the host resolution functionality. Note that while we refer to {@code
21  * DNS} as the most common mechanism being used for brevity, settings apply to other means of
22  * resolving hostnames like hosts file resolution.
23  *
24  * <p>Hostnames can be resolved in two ways - either by using the system resolver (using {@code
25  * getaddrinfo} provided by system libraries), or by using a custom resolver which is tailored
26  * for the HTTP networking stack.
27  *
28  * <p>The built-in stack provides several advantages over using the global system resolver:
29  *
30  * <ul>
31  *   <li>It has been tailored to the needs of the HTTP networking stack, particularly speed and
32  *       stability.
33  *   <li>{@code getaddrinfo} is a blocking call which requires dedicating worker threads and makes
34  *       cancellation impossible (we need to abandon the thread until the call completes)
35  *   <li>The {@code getaddrinfo} interface gives no insight into the root cause of failures
36  *   <li>{@code struct addrinfo} provides no TTL (Time To Live) of the returned addresses. This
37  *       restricts flexibility of handling caching (e.g. allowing the use of stale DNS records) and
38  *       requires us to either rely on OS DNS caches, or be extremely conservative with the TTL.
39  * </ul>
40  *
41  * <p>Most configuration in this class is only applicable if the built-in DNS resolver is used.
42  */
43 // SuppressLint to be consistent with other cronet code
44 @SuppressLint("UserHandleName")
45 public final class DnsOptions {
46     private final @DnsOptionState int mUseHttpStackDnsResolver;
47     private final @DnsOptionState int mPersistHostCache;
48     private final @DnsOptionState int mEnableStaleDns;
49     @Nullable
50     private final Duration mPersistHostCachePeriod;
51 
52     private final @DnsOptionState int mPreestablishConnectionsToStaleDnsResults;
53     @Nullable
54     private final StaleDnsOptions mStaleDnsOptions;
55 
DnsOptions(Builder builder)56     DnsOptions(Builder builder) {
57         this.mEnableStaleDns = builder.mEnableStaleDns;
58         this.mStaleDnsOptions = builder.mStaleDnsOptions;
59         this.mPersistHostCachePeriod = builder.mPersistHostCachePeriod;
60         this.mPreestablishConnectionsToStaleDnsResults =
61                 builder.mPreestablishConnectionsToStaleDnsResults;
62         this.mUseHttpStackDnsResolver = builder.mUseHttpStackDnsResolver;
63         this.mPersistHostCache = builder.mPersistHostCache;
64     }
65 
66     /**
67      * Option is unspecified, platform default value will be used.
68      */
69     public static final int DNS_OPTION_UNSPECIFIED = 0;
70 
71     /**
72      * Option is enabled.
73      */
74     public static final int DNS_OPTION_ENABLED = 1;
75 
76     /**
77      * Option is disabled.
78      */
79     public static final int DNS_OPTION_DISABLED = 2;
80 
81     /** @hide */
82     @Retention(RetentionPolicy.SOURCE)
83     @IntDef(flag = false, prefix = "DNS_OPTION_", value = {
84             DNS_OPTION_UNSPECIFIED,
85             DNS_OPTION_ENABLED,
86             DNS_OPTION_DISABLED,
87     })
88     public @interface DnsOptionState {}
89 
90     /**
91      * See {@link Builder#setUseHttpStackDnsResolver(int)}
92      */
getUseHttpStackDnsResolver()93     public @DnsOptionState int getUseHttpStackDnsResolver() {
94         return mUseHttpStackDnsResolver;
95     }
96 
97     /**
98      * See {@link Builder#setPersistHostCache(int)}
99      */
getPersistHostCache()100     public @DnsOptionState int getPersistHostCache() {
101         return mPersistHostCache;
102     }
103 
104     /**
105      * See {@link Builder#setStaleDns(int)}
106      */
getStaleDns()107     public @DnsOptionState int getStaleDns() {
108         return mEnableStaleDns;
109     }
110 
111     /**
112      * See {@link Builder#setPersistHostCachePeriod}
113      */
114     @Nullable
getPersistHostCachePeriod()115     public Duration getPersistHostCachePeriod() {
116         return mPersistHostCachePeriod;
117     }
118 
119     /**
120      * See {@link Builder#setPreestablishConnectionsToStaleDnsResults(int)}
121      */
getPreestablishConnectionsToStaleDnsResults()122     public @DnsOptionState int getPreestablishConnectionsToStaleDnsResults() {
123         return mPreestablishConnectionsToStaleDnsResults;
124     }
125 
126     /**
127      * See {@link Builder#setStaleDnsOptions}
128      */
129     @Nullable
getStaleDnsOptions()130     public StaleDnsOptions getStaleDnsOptions() {
131         return mStaleDnsOptions;
132     }
133 
134     /**
135      * Returns a new builder for {@link DnsOptions}.
136      *
137      * @hide
138      */
139     @NonNull
builder()140     public static Builder builder() {
141         return new Builder();
142     }
143 
144     /**
145      * A class configuring the stale DNS functionality.
146      *
147      * <p>DNS resolution is one of the steps on the critical path to making a URL request, but it
148      * can be slow for various reasons (underlying network latency, buffer bloat, packet loss,
149      * etc.). Depending on the use case, it might be worthwhile for an app developer to trade off
150      * guaranteed DNS record freshness for better availability of DNS records.
151      *
152      * <p>Stale results can include both:
153      *
154      * <ul>
155      *   <li>results returned from the current network's DNS server, but past their time-to-live,
156      *       and
157      *   <li>results returned from a different network's DNS server, whether expired or not.
158      * </ul>
159      *
160      * <p>For detailed explanation of the configuration options see javadoc on
161      * {@link StaleDnsOptions.Builder} methods.
162      */
163     // SuppressLint to be consistent with other cronet code
164     @SuppressLint("UserHandleName")
165     public static class StaleDnsOptions {
166         @Nullable
getFreshLookupTimeout()167         public Duration getFreshLookupTimeout() {
168             return mFreshLookupTimeout;
169         }
170 
171         @Nullable
getMaxExpiredDelay()172         public Duration getMaxExpiredDelay() {
173             return mMaxExpiredDelay;
174         }
175 
getAllowCrossNetworkUsage()176         public @DnsOptionState int getAllowCrossNetworkUsage() {
177             return mAllowCrossNetworkUsage;
178         }
getUseStaleOnNameNotResolved()179         public @DnsOptionState int getUseStaleOnNameNotResolved() {
180             return mUseStaleOnNameNotResolved;
181         }
182 
183         /**
184          * @hide
185          */
186         @NonNull
builder()187         public static Builder builder() {
188             return new Builder();
189         }
190 
191         @Nullable
192         private final Duration mFreshLookupTimeout;
193         @Nullable
194         private final Duration mMaxExpiredDelay;
195         private final @DnsOptionState int mAllowCrossNetworkUsage;
196         private final @DnsOptionState int mUseStaleOnNameNotResolved;
197 
StaleDnsOptions(@onNull Builder builder)198         StaleDnsOptions(@NonNull Builder builder) {
199             this.mFreshLookupTimeout = builder.mFreshLookupTimeout;
200             this.mMaxExpiredDelay = builder.mMaxExpiredDelay;
201             this.mAllowCrossNetworkUsage = builder.mAllowCrossNetworkUsage;
202             this.mUseStaleOnNameNotResolved = builder.mUseStaleOnNameNotResolved;
203         }
204 
205         /**
206          * Builder for {@link StaleDnsOptions}.
207          */
208         public static final class Builder {
209             private Duration mFreshLookupTimeout;
210             private Duration mMaxExpiredDelay;
211             private @DnsOptionState int mAllowCrossNetworkUsage;
212             private @DnsOptionState int mUseStaleOnNameNotResolved;
213 
Builder()214             public Builder() {}
215 
216             /**
217              * Sets how long (in milliseconds) to wait for a DNS request to return before using a
218              * stale result instead. If set to zero, returns stale results instantly but continues
219              * the DNS request in the background to update the cache.
220              *
221              * @return the builder for chaining
222              */
223             @NonNull
setFreshLookupTimeout(@onNull Duration freshLookupTimeout)224             public Builder setFreshLookupTimeout(@NonNull Duration freshLookupTimeout) {
225                 this.mFreshLookupTimeout = freshLookupTimeout;
226                 return this;
227             }
228 
229             /**
230              * Sets how long (in milliseconds) past expiration to consider using expired results.
231              * Setting the value to zero means expired records can be used indefinitely.
232              *
233              * @return the builder for chaining
234              */
235             @NonNull
setMaxExpiredDelay(@onNull Duration maxExpiredDelay)236             public Builder setMaxExpiredDelay(@NonNull Duration maxExpiredDelay) {
237                 this.mMaxExpiredDelay = maxExpiredDelay;
238                 return this;
239             }
240 
241             /**
242              * Sets whether to return results originating from other networks or not. Normally,
243              * the HTTP stack clears the DNS cache entirely when switching connections, e.g. between
244              * two Wi-Fi networks or from Wi-Fi to 4G.
245              *
246              * @param state one of the DNS_OPTION_* values
247              * @return the builder for chaining
248              */
249             @NonNull
setAllowCrossNetworkUsage(@nsOptionState int state)250             public Builder setAllowCrossNetworkUsage(@DnsOptionState int state) {
251                 this.mAllowCrossNetworkUsage = state;
252                 return this;
253             }
254 
255             /**
256              * Sets whether to allow use of stale DNS results when network resolver fails to resolve
257              * the hostname.
258              *
259              * <p>Depending on the use case, if the DNS resolver quickly sees a fresh failure, it
260              * may be desirable to use the failure as it is technically the fresher result, and we
261              * had such a fresh result quickly; or, prefer having any result (even if stale) to use
262              * over dealing with a DNS failure.
263              *
264              * @param state one of the DNS_OPTION_* values
265              * @return the builder for chaining
266              */
267             @NonNull
setUseStaleOnNameNotResolved(@nsOptionState int state)268             public Builder setUseStaleOnNameNotResolved(@DnsOptionState int state) {
269                 this.mUseStaleOnNameNotResolved = state;
270                 return this;
271             }
272 
273             /**
274              * Creates and returns the final {@link StaleDnsOptions} instance, based on the values
275              * in this builder.
276              */
277             @NonNull
build()278             public StaleDnsOptions build() {
279                 return new StaleDnsOptions(this);
280             }
281         }
282     }
283 
284     /**
285      * Builder for {@link DnsOptions}.
286      */
287     public static final class Builder {
288         private @DnsOptionState int mUseHttpStackDnsResolver;
289         private @DnsOptionState int mEnableStaleDns;
290         @Nullable
291         private StaleDnsOptions mStaleDnsOptions;
292         private @DnsOptionState int mPersistHostCache;
293         @Nullable
294         private Duration mPersistHostCachePeriod;
295         private @DnsOptionState int mPreestablishConnectionsToStaleDnsResults;
296 
Builder()297         public Builder() {}
298 
299         /**
300          * Enables the use of the HTTP-stack-specific DNS resolver.
301          *
302          * <p>Setting this to {@link #DNS_OPTION_ENABLED} is necessary for other functionality
303          * of {@link DnsOptions} to work, unless specified otherwise. See the {@link DnsOptions}
304          * documentation for more details.
305          *
306          * @param state one of the DNS_OPTION_* values
307          * @return the builder for chaining
308          */
309         @NonNull
setUseHttpStackDnsResolver(@nsOptionState int state)310         public Builder setUseHttpStackDnsResolver(@DnsOptionState int state) {
311             this.mUseHttpStackDnsResolver = state;
312             return this;
313         }
314 
315         /**
316          * Sets whether to use stale DNS results at all.
317          *
318          * @param state one of the DNS_OPTION_* values
319          * @return the builder for chaining
320          */
321         @NonNull
setStaleDns(@nsOptionState int state)322         public Builder setStaleDns(@DnsOptionState int state) {
323             this.mEnableStaleDns = state;
324             return this;
325         }
326 
327         /**
328          * Sets detailed configuration for stale DNS.
329          *
330          * Only relevant if {@link #setStaleDns(int)} is set.
331          *
332          * @return this builder for chaining.
333          */
334         @NonNull
setStaleDnsOptions(@onNull StaleDnsOptions staleDnsOptions)335         public Builder setStaleDnsOptions(@NonNull StaleDnsOptions staleDnsOptions) {
336             this.mStaleDnsOptions = staleDnsOptions;
337             return this;
338         }
339 
340         /**
341          * @see #setStaleDnsOptions(StaleDnsOptions)
342          *
343          * {@hide}
344          */
345         @Experimental
346         @NonNull
setStaleDnsOptions(@onNull StaleDnsOptions.Builder staleDnsOptionsBuilder)347         public Builder setStaleDnsOptions(@NonNull StaleDnsOptions.Builder staleDnsOptionsBuilder) {
348             return setStaleDnsOptions(staleDnsOptionsBuilder.build());
349         }
350 
351         /**
352          * Sets whether Cronet should use stale cached DNS records to pre-establish connections.
353          *
354          * <p>If enabled, Cronet will optimistically pre-establish connections to servers that
355          * matched the hostname at some point in the past and were cached but the cache entry
356          * expired. Such connections won't be used further until a new DNS lookup confirms the
357          * cached record was up to date.
358          *
359          * <p>To use cached DNS records straight away, use {@link #setStaleDns(int)} and {@link
360          * StaleDnsOptions} configuration options.
361          *
362          * <p>This option may not be available for all networking protocols.
363          *
364          * @param state one of the DNS_OPTION_* values
365          * @return the builder for chaining
366          */
367         @Experimental
368         @NonNull
setPreestablishConnectionsToStaleDnsResults( @nsOptionState int state)369         public Builder setPreestablishConnectionsToStaleDnsResults(
370                 @DnsOptionState int state) {
371             this.mPreestablishConnectionsToStaleDnsResults = state;
372             return this;
373         }
374 
375         /**
376          * Sets whether the DNS cache should be persisted to disk.
377          *
378          * <p>Only relevant if {@link HttpEngine.Builder#setStoragePath(String)} is
379          * set.
380          *
381          * @param state one of the DNS_OPTION_* values
382          * @return the builder for chaining
383          */
384         @NonNull
setPersistHostCache(@nsOptionState int state)385         public Builder setPersistHostCache(@DnsOptionState int state) {
386             this.mPersistHostCache = state;
387             return this;
388         }
389 
390         /**
391          * Sets the minimum period between subsequent writes to disk for DNS cache persistence.
392          *
393          * <p>Only relevant if {@link #setPersistHostCache(int)} is set to
394          * {@link #DNS_OPTION_ENABLED}.
395          *
396          * @return the builder for chaining
397          */
398         @NonNull
setPersistHostCachePeriod(@onNull Duration persistHostCachePeriod)399         public Builder setPersistHostCachePeriod(@NonNull Duration persistHostCachePeriod) {
400             this.mPersistHostCachePeriod = persistHostCachePeriod;
401             return this;
402         }
403 
404         /**
405          * Creates and returns the final {@link DnsOptions} instance, based on the values in this
406          * builder.
407          */
408         @NonNull
build()409         public DnsOptions build() {
410             return new DnsOptions(this);
411         }
412     }
413 
414     /**
415      * An annotation for APIs which are not considered stable yet.
416      *
417      * <p>Applications using experimental APIs must acknowledge that they're aware of using APIs
418      * that are not considered stable. The APIs might change functionality, break or cease to exist
419      * without notice.
420      *
421      * <p>It's highly recommended to reach out to Cronet maintainers ({@code net-dev@chromium.org})
422      * before using one of the APIs annotated as experimental outside of debugging
423      * and proof-of-concept code. Be ready to help to help polishing the API, or for a "sorry,
424      * really not production ready yet".
425      *
426      * <p>If you still want to use an experimental API in production, you're doing so at your
427      * own risk. You have been warned.
428      *
429      * {@hide}
430      */
431     public @interface Experimental {}
432 }
433