|
1 | 1 | package com.fox2code.mmm.utils; |
2 | 2 |
|
| 3 | +import android.content.Context; |
| 4 | +import android.content.SharedPreferences; |
3 | 5 | import android.util.Log; |
4 | 6 |
|
5 | 7 | import androidx.annotation.NonNull; |
|
13 | 15 | import java.net.InetAddress; |
14 | 16 | import java.net.Proxy; |
15 | 17 | import java.net.UnknownHostException; |
| 18 | +import java.util.ArrayList; |
16 | 19 | import java.util.Arrays; |
17 | 20 | import java.util.Collections; |
18 | 21 | import java.util.HashMap; |
| 22 | +import java.util.HashSet; |
19 | 23 | import java.util.Iterator; |
20 | 24 | import java.util.List; |
21 | 25 | import java.util.Objects; |
|
34 | 38 | import okhttp3.dnsoverhttps.DnsOverHttps; |
35 | 39 |
|
36 | 40 | public class Http { |
| 41 | + private static final String TAG = "Http"; |
37 | 42 | private static final OkHttpClient httpClient; |
38 | 43 | private static final OkHttpClient httpClientWithCache; |
39 | 44 |
|
@@ -70,28 +75,34 @@ public class Http { |
70 | 75 | Objects.requireNonNull(HttpUrl.parse("https://cloudflare-dns.com/dns-query"))) |
71 | 76 | .bootstrapDnsHosts(cloudflareBootstrap).resolvePrivateAddresses(true).build(); |
72 | 77 | } catch (UnknownHostException|RuntimeException e) { |
73 | | - Log.e("Http", "Failed to init DoH", e); |
| 78 | + Log.e(TAG, "Failed to init DoH", e); |
74 | 79 | } |
75 | 80 | httpclientBuilder.cookieJar(CookieJar.NO_COOKIES); |
76 | | - httpclientBuilder.dns(dns); |
77 | | - httpClient = httpclientBuilder.build(); |
78 | 81 | MainApplication mainApplication = MainApplication.getINSTANCE(); |
79 | 82 | if (mainApplication != null) { |
| 83 | + httpclientBuilder.dns(new FallBackDNS(mainApplication, dns, "github.com", |
| 84 | + "api.github.com", "raw.githubusercontent.com", "camo.githubusercontent.com", |
| 85 | + "user-images.githubusercontent.com", "cdn.jsdelivr.net", "img.shields.io", |
| 86 | + "magisk-modules-repo.github.io", "www.androidacy.com")); |
| 87 | + httpClient = httpclientBuilder.build(); |
80 | 88 | httpclientBuilder.cache(new Cache( |
81 | 89 | new File(mainApplication.getCacheDir(), "http_cache"), |
82 | 90 | 2L * 1024L * 1024L)); // 2Mib of cache |
83 | 91 | httpclientBuilder.cookieJar(new CDNCookieJar()); |
84 | 92 | httpClientWithCache = httpclientBuilder.build(); |
| 93 | + Log.i(TAG, "Initialized Http successfully!"); |
85 | 94 | } else { |
86 | | - httpClientWithCache = httpClient; |
| 95 | + httpclientBuilder.dns(dns); |
| 96 | + httpClientWithCache = httpClient = httpclientBuilder.build(); |
| 97 | + Log.e(TAG, "Initialized Http too soon!"); |
87 | 98 | } |
88 | 99 | } |
89 | 100 |
|
90 | | - public static OkHttpClient getHttpclientNoCache() { |
| 101 | + public static OkHttpClient getHttpClient() { |
91 | 102 | return httpClient; |
92 | 103 | } |
93 | 104 |
|
94 | | - public static OkHttpClient getHttpclientWithCache() { |
| 105 | + public static OkHttpClient getHttpClientWithCache() { |
95 | 106 | return httpClientWithCache; |
96 | 107 | } |
97 | 108 |
|
@@ -207,4 +218,85 @@ public void saveFromResponse(@NonNull HttpUrl httpUrl, @NonNull List<Cookie> coo |
207 | 218 | public interface ProgressListener { |
208 | 219 | void onUpdate(int downloaded,int total, boolean done); |
209 | 220 | } |
| 221 | + |
| 222 | + /** |
| 223 | + * FallBackDNS store successful DNS request to return them later |
| 224 | + * can help make the app to work later when the current DNS system |
| 225 | + * isn't functional or available. |
| 226 | + * |
| 227 | + * Note: DNS Cache is stored in user data. |
| 228 | + * */ |
| 229 | + private static class FallBackDNS implements Dns { |
| 230 | + private final Dns parent; |
| 231 | + private final SharedPreferences sharedPreferences; |
| 232 | + private final HashSet<String> fallbacks; |
| 233 | + private final HashMap<String, List<InetAddress>> fallbackCache; |
| 234 | + |
| 235 | + public FallBackDNS(Context context, Dns parent, String... fallbacks) { |
| 236 | + this.sharedPreferences = context.getSharedPreferences( |
| 237 | + "mmm_dns", Context.MODE_PRIVATE); |
| 238 | + this.parent = parent; |
| 239 | + this.fallbacks = new HashSet<>(Arrays.asList(fallbacks)); |
| 240 | + this.fallbackCache = new HashMap<>(); |
| 241 | + } |
| 242 | + |
| 243 | + @NonNull |
| 244 | + @Override |
| 245 | + public List<InetAddress> lookup(@NonNull String s) throws UnknownHostException { |
| 246 | + if (this.fallbacks.contains(s)) { |
| 247 | + List<InetAddress> addresses = this.fallbackCache.get(s); |
| 248 | + if (addresses != null) |
| 249 | + return addresses; |
| 250 | + try { |
| 251 | + addresses = this.parent.lookup(s); |
| 252 | + if (addresses.isEmpty() || addresses.get(0).isLoopbackAddress()) |
| 253 | + throw new UnknownHostException(s); |
| 254 | + this.fallbackCache.put(s, addresses); |
| 255 | + this.sharedPreferences.edit().putString( |
| 256 | + s.replace('.', '_'), toString(addresses)).apply(); |
| 257 | + } catch (UnknownHostException e) { |
| 258 | + String key = this.sharedPreferences.getString( |
| 259 | + s.replace('.', '_'), ""); |
| 260 | + if (!key.isEmpty()) try { |
| 261 | + addresses = fromString(key); |
| 262 | + this.fallbackCache.put(s, addresses); |
| 263 | + return addresses; |
| 264 | + } catch (UnknownHostException e2) { |
| 265 | + this.sharedPreferences.edit().remove( |
| 266 | + s.replace('.', '_')).apply(); |
| 267 | + } |
| 268 | + throw e; |
| 269 | + } |
| 270 | + |
| 271 | + return addresses; |
| 272 | + } else { |
| 273 | + return this.parent.lookup(s); |
| 274 | + } |
| 275 | + } |
| 276 | + |
| 277 | + @NonNull |
| 278 | + private static String toString(@NonNull List<InetAddress> inetAddresses) { |
| 279 | + if (inetAddresses.isEmpty()) return ""; |
| 280 | + Iterator<InetAddress> inetAddressIterator = inetAddresses.iterator(); |
| 281 | + StringBuilder stringBuilder = new StringBuilder(); |
| 282 | + while (true) { |
| 283 | + stringBuilder.append(inetAddressIterator.next().getHostAddress()); |
| 284 | + if (!inetAddressIterator.hasNext()) |
| 285 | + return stringBuilder.toString(); |
| 286 | + stringBuilder.append("|"); |
| 287 | + } |
| 288 | + } |
| 289 | + |
| 290 | + @NonNull |
| 291 | + private static List<InetAddress> fromString(@NonNull String string) |
| 292 | + throws UnknownHostException { |
| 293 | + if (string.isEmpty()) return Collections.emptyList(); |
| 294 | + String[] strings = string.split("\\|"); |
| 295 | + ArrayList<InetAddress> inetAddresses = new ArrayList<>(strings.length); |
| 296 | + for (String address : strings) { |
| 297 | + inetAddresses.add(InetAddress.getByName(address)); |
| 298 | + } |
| 299 | + return inetAddresses; |
| 300 | + } |
| 301 | + } |
210 | 302 | } |
0 commit comments