阿里sdk
This commit is contained in:
129
EdgeHttpDNS/sdk/android/httpdns-sdk/build.gradle
Normal file
129
EdgeHttpDNS/sdk/android/httpdns-sdk/build.gradle
Normal file
@@ -0,0 +1,129 @@
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
id 'maven-publish'
|
||||
}
|
||||
apply from: 'version.gradle'
|
||||
|
||||
|
||||
android {
|
||||
compileSdk 33
|
||||
defaultConfig {
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 33
|
||||
versionCode 1
|
||||
versionName httpdnsDebugVersion
|
||||
setProperty("archivesBaseName", "alicloud-android-httpdns-$versionName")
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
buildConfigField "String", "VERSION_NAME", "\"${httpdnsDebugVersion}\""
|
||||
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
|
||||
debug {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), 'proguard-rules.pro'
|
||||
}
|
||||
|
||||
forTest {
|
||||
initWith release
|
||||
debuggable true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-for-test.pro'
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions += "version"
|
||||
|
||||
productFlavors {
|
||||
normal {
|
||||
|
||||
}
|
||||
|
||||
intl {
|
||||
|
||||
}
|
||||
|
||||
end2end {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
variantFilter { variant ->
|
||||
def names = variant.flavors*.name
|
||||
def type = variant.buildType.name
|
||||
// To check for a certain build type, use variant.buildType.name == "<buildType>"
|
||||
if ((names.contains("normal") && type.contains("forTest"))
|
||||
|| (names.contains("intl") && type.contains("forTest"))
|
||||
|| (names.contains("end2end") && type.contains("release"))
|
||||
|| (names.contains("end2end") && type.contains("debug"))
|
||||
) {
|
||||
// Gradle ignores any variants that satisfy the conditions above.
|
||||
setIgnore(true)
|
||||
}
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
all {
|
||||
jvmArgs '-noverify'
|
||||
systemProperty 'robolectric.logging.enable', true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
//noinspection GradleDependency 高版本 jdk和androidx有一些依赖,暂时不升级
|
||||
testEnd2endImplementation "org.robolectric:robolectric:3.8"
|
||||
//noinspection GradleDependency
|
||||
testEnd2endImplementation 'junit:junit:4.12'
|
||||
//noinspection GradleDependency
|
||||
testEnd2endImplementation 'org.mockito:mockito-core:2.15.0'
|
||||
testEnd2endImplementation 'com.squareup.okhttp3:mockwebserver:3.9.0'
|
||||
|
||||
implementation "com.aliyun.ams:alicloud-android-logger:${loggerVersion}"
|
||||
implementation "com.aliyun.ams:alicloud-android-crashdefend:${crashDefendVersion}"
|
||||
implementation "com.aliyun.ams:alicloud-android-ipdetector:${ipdetectorVersion}"
|
||||
}
|
||||
|
||||
ext.getIntlVersion = { version ->
|
||||
if (version.endsWith("-SNAPSHOT")) {
|
||||
return version.replace("-SNAPSHOT", "-intl-SNAPSHOT");
|
||||
} else {
|
||||
return version + "-intl";
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
publishing {
|
||||
publications {
|
||||
mavenReleaseAar(MavenPublication) {
|
||||
from components.normalRelease
|
||||
groupId 'com.aliyun.ams'
|
||||
artifactId 'alicloud-android-httpdns'
|
||||
version "$project.android.defaultConfig.versionName"
|
||||
}
|
||||
mavenIntlReleaseAar(MavenPublication) {
|
||||
from components.intlRelease
|
||||
groupId 'com.aliyun.ams'
|
||||
artifactId 'alicloud-android-httpdns'
|
||||
version getIntlVersion(project.android.defaultConfig.versionName)
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
if (project.android.defaultConfig.versionName.endsWith("-local-SNAPSHOT")) {
|
||||
// only maven local
|
||||
} else if (project.android.defaultConfig.versionName.endsWith("-SNAPSHOT")) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/ryan/Downloads/adt-bundle-mac-x86_64-20131030/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
-optimizationpasses 3
|
||||
-dontoptimize
|
||||
-dontusemixedcaseclassnames
|
||||
-dontskipnonpubliclibraryclasses
|
||||
-verbose
|
||||
-overloadaggressively
|
||||
#-allowaccessmodification
|
||||
-useuniqueclassmembernames
|
||||
|
||||
-keeppackagenames com.alibaba.sdk.android.httpdns
|
||||
-keep class com.alibaba.sdk.android.httpdns.HttpDns{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.HttpDnsService{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.SyncService{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.RequestIpType{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.net64.Net64Service{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.DegradationFilter{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.ranking.IPRankingBean{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.ILogger{*;}
|
||||
-keepclasseswithmembers class com.alibaba.sdk.android.httpdns.log.HttpDnsLog {
|
||||
public static *** setLogger(***);
|
||||
public static *** removeLogger(***);
|
||||
public static *** enable(***);
|
||||
}
|
||||
-keep class com.alibaba.sdk.android.httpdns.HTTPDNSResult{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.ApiForTest{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.test.** {*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.resolve.ResolveHostResponse{*;}
|
||||
|
||||
|
||||
-keep class com.alibaba.sdk.android.httpdns.utils.CommonUtil{
|
||||
public <methods>;
|
||||
public <fields>;
|
||||
}
|
||||
68
EdgeHttpDNS/sdk/android/httpdns-sdk/proguard-rules.pro
vendored
Normal file
68
EdgeHttpDNS/sdk/android/httpdns-sdk/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/ryan/Downloads/adt-bundle-mac-x86_64-20131030/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
-optimizationpasses 3
|
||||
-dontoptimize
|
||||
-dontusemixedcaseclassnames
|
||||
-dontskipnonpubliclibraryclasses
|
||||
-verbose
|
||||
-overloadaggressively
|
||||
#-allowaccessmodification
|
||||
-useuniqueclassmembernames
|
||||
|
||||
-dontwarn com.alibaba.sdk.android.httpdns.net.HttpDnsNetworkDetector
|
||||
|
||||
-keeppackagenames com.alibaba.sdk.android.httpdns
|
||||
-flattenpackagehierarchy com.alibaba.sdk.android.httpdns
|
||||
-keep class com.alibaba.sdk.android.httpdns.HttpDns{*;}
|
||||
-keep interface com.alibaba.sdk.android.httpdns.HttpDnsService{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.impl.ErrorImpl{*;}
|
||||
-keep interface com.alibaba.sdk.android.httpdns.SyncService{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.InitConfig{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.InitConfig$Builder{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.RequestIpType{*;}
|
||||
-keep interface com.alibaba.sdk.android.httpdns.DegradationFilter{*;}
|
||||
-keep interface com.alibaba.sdk.android.httpdns.NotUseHttpDnsFilter{*;}
|
||||
-keep interface com.alibaba.sdk.android.httpdns.HttpDnsCallback{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.ranking.IPRankingBean{*;}
|
||||
-keep interface com.alibaba.sdk.android.httpdns.ILogger{*;}
|
||||
-keep interface com.alibaba.sdk.android.httpdns.CacheTtlChanger{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.NetType{*;}
|
||||
-keepclasseswithmembers class com.alibaba.sdk.android.httpdns.log.HttpDnsLog {
|
||||
public static *** setLogger(***);
|
||||
public static *** removeLogger(***);
|
||||
public static *** enable(***);
|
||||
}
|
||||
-keep class com.alibaba.sdk.android.httpdns.HTTPDNSResult{*;}
|
||||
-keepclasseswithmembers class com.alibaba.sdk.android.httpdns.HttpDnsSettings {
|
||||
public static *** setDailyReport(***);
|
||||
public static *** setNetworkChecker(***);
|
||||
}
|
||||
|
||||
-keep class com.alibaba.sdk.android.httpdns.net.HttpDnsNetworkDetector {
|
||||
public <methods>;
|
||||
public <fields>;
|
||||
}
|
||||
|
||||
-keep interface com.alibaba.sdk.android.httpdns.HttpDnsSettings$NetworkChecker{*;}
|
||||
-keep interface com.alibaba.sdk.android.httpdns.HttpDnsSettings$NetworkDetector{*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.utils.CommonUtil{
|
||||
public <methods>;
|
||||
public <fields>;
|
||||
}
|
||||
-keep enum com.alibaba.sdk.android.httpdns.Region {*;}
|
||||
-keep class com.alibaba.sdk.android.httpdns.exception.InitException{*;}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.ranking.IPRankingTask;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* @author zonglin.nzl
|
||||
* @date 2020/10/15
|
||||
*/
|
||||
public interface ApiForTest {
|
||||
|
||||
/**
|
||||
* 指定初始服务ip
|
||||
* @param region
|
||||
* @param ips
|
||||
* @param ports
|
||||
* @param ipv6s
|
||||
* @param v6Ports
|
||||
*/
|
||||
void setInitServer(String region, String[] ips, int[] ports, String[] ipv6s, int[] v6Ports);
|
||||
|
||||
/**
|
||||
* 指定httpdns使用的线程池
|
||||
* @param scheduledExecutorService
|
||||
*/
|
||||
void setThread(ScheduledExecutorService scheduledExecutorService);
|
||||
|
||||
/**
|
||||
* 指定 测试使用的socket factory
|
||||
* @param speedTestSocketFactory
|
||||
*/
|
||||
void setSocketFactory(IPRankingTask.SpeedTestSocketFactory speedTestSocketFactory);
|
||||
|
||||
/**
|
||||
* 指定调度接口的调用间歇,避免正常的间歇过长无法测试
|
||||
* @param timeInterval
|
||||
*/
|
||||
void setUpdateServerTimeInterval(int timeInterval);
|
||||
|
||||
/**
|
||||
* 指定 sniff模式的 请求间歇
|
||||
* @param timeInterval
|
||||
*/
|
||||
void setSniffTimeInterval(int timeInterval);
|
||||
|
||||
/**
|
||||
* 获取httpdns的线程池用于控制异常操作
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
ExecutorService getWorker();
|
||||
|
||||
/**
|
||||
* 设置兜底的调度ip
|
||||
*
|
||||
* @param defaultServerIps
|
||||
* @param ports
|
||||
*/
|
||||
void setDefaultUpdateServer(String[] defaultServerIps, int[] ports);
|
||||
|
||||
/**
|
||||
* 设置ipv6的兜底调度IP
|
||||
*
|
||||
* @param defaultServerIps
|
||||
* @param ports
|
||||
*/
|
||||
void setDefaultUpdateServerIpv6(String[] defaultServerIps, int[] ports);
|
||||
|
||||
/**
|
||||
* 设置测试用的网络detector
|
||||
*
|
||||
* @param networkDetector
|
||||
*/
|
||||
void setNetworkDetector(HttpDnsSettings.NetworkDetector networkDetector);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
/**
|
||||
* 测试用的初始化接口
|
||||
* @author zonglin.nzl
|
||||
* @date 1/14/22
|
||||
*/
|
||||
public interface BeforeHttpDnsServiceInit {
|
||||
|
||||
void beforeInit(HttpDnsService service);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsInstanceHolder;
|
||||
import com.alibaba.sdk.android.httpdns.impl.InstanceCreator;
|
||||
import com.alibaba.sdk.android.httpdns.net.NetworkStateManager;
|
||||
import com.alibaba.sdk.android.httpdns.utils.CommonUtil;
|
||||
|
||||
/**
|
||||
* Httpdns实例管理
|
||||
*/
|
||||
public class HttpDns {
|
||||
|
||||
private static HttpDnsInstanceHolder sHolder = new HttpDnsInstanceHolder(new InstanceCreator());
|
||||
|
||||
/**
|
||||
* 获取HttpDnsService对象
|
||||
*
|
||||
* @param applicationContext 当前APP的Context
|
||||
* @param accountID HttpDns控制台分配的AccountID
|
||||
* @return
|
||||
*/
|
||||
public synchronized static HttpDnsService getService(final Context applicationContext, final String accountID) {
|
||||
return sHolder.get(applicationContext, accountID, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取HttpDnsService对象,并启用鉴权功能
|
||||
*
|
||||
* @param applicationContext 当前APP的Context
|
||||
* @param accountID HttpDns控制台分配的AccountID
|
||||
* @param secretKey 用户鉴权私钥
|
||||
* @return
|
||||
*/
|
||||
public synchronized static HttpDnsService getService(final Context applicationContext, final String accountID, final String secretKey) {
|
||||
return sHolder.get(applicationContext, accountID, secretKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取HttpDnsService对象,初始化时不传入任何参数,靠统一接入服务获取相关参数
|
||||
*
|
||||
* @param applicationContext 当前APP的Context
|
||||
* @return
|
||||
*/
|
||||
public synchronized static HttpDnsService getService(final Context applicationContext) {
|
||||
return sHolder.get(applicationContext, CommonUtil.getAccountId(applicationContext), CommonUtil.getSecretKey(applicationContext));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用或者禁用httpdns,理论上这个是内部接口,不给外部使用的
|
||||
* 但是已经对外暴露,所以保留
|
||||
*
|
||||
* @param enabled
|
||||
* @deprecated 启用禁用应该调用实例的方法,而不是控制全部实例的方法
|
||||
*/
|
||||
@Deprecated
|
||||
public synchronized static void switchDnsService(boolean enabled) {
|
||||
// do nothing as deprecated
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置httpdns,用于一些模拟应用重启的场景
|
||||
*/
|
||||
public static void resetInstance() {
|
||||
sHolder = new HttpDnsInstanceHolder(new InstanceCreator());
|
||||
NetworkStateManager.getInstance().reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* 增加初始化逻辑的单例
|
||||
* 用于在测试用的httpdns实例中增加测试需要的初始化逻辑
|
||||
* @author zonglin.nzl
|
||||
* @date 1/14/22
|
||||
*/
|
||||
public class InitManager {
|
||||
|
||||
private static class Holder {
|
||||
private static final InitManager instance = new InitManager();
|
||||
}
|
||||
|
||||
public static InitManager getInstance() {
|
||||
return Holder.instance;
|
||||
}
|
||||
|
||||
private InitManager() {
|
||||
}
|
||||
|
||||
private HashMap<String, BeforeHttpDnsServiceInit> initThings = new HashMap<>();
|
||||
|
||||
|
||||
public void add(String accountId, BeforeHttpDnsServiceInit beforeHttpDnsServiceInit) {
|
||||
initThings.put(accountId, beforeHttpDnsServiceInit);
|
||||
}
|
||||
|
||||
public BeforeHttpDnsServiceInit getAndRemove(String accountId) {
|
||||
return initThings.remove(accountId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.alibaba.sdk.android.httpdns.impl;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.ApiForTest;
|
||||
import com.alibaba.sdk.android.httpdns.BeforeHttpDnsServiceInit;
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsSettings;
|
||||
import com.alibaba.sdk.android.httpdns.InitManager;
|
||||
import com.alibaba.sdk.android.httpdns.ranking.IPRankingTask;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* 测试时 使用的httpdns 实例
|
||||
* 增加了一些用于测试的api和机制
|
||||
* @author zonglin.nzl
|
||||
* @date 2020/10/16
|
||||
*/
|
||||
public class HttpDnsServiceTestImpl extends HttpDnsServiceImpl implements ApiForTest {
|
||||
public HttpDnsServiceTestImpl(Context context, String accountId, String secret) {
|
||||
super(context, accountId, secret);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeInit() {
|
||||
super.beforeInit();
|
||||
// 通过InitManager 在httpdns初始化之前 进行一些测试需要前置工作
|
||||
BeforeHttpDnsServiceInit init = InitManager.getInstance().getAndRemove(mHttpDnsConfig.getAccountId());
|
||||
|
||||
if (init != null) {
|
||||
init.beforeInit(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initCrashDefend(Context context, HttpDnsConfig config) {
|
||||
// do nothing for test
|
||||
mHttpDnsConfig.crashDefend(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInitServer(String region, String[] ips, int[] ports, String[] ipv6s, int[] v6Ports) {
|
||||
mHttpDnsConfig.setInitServers(region, ips, ports, ipv6s, v6Ports);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThread(ScheduledExecutorService scheduledExecutorService) {
|
||||
mHttpDnsConfig.setWorker(scheduledExecutorService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSocketFactory(IPRankingTask.SpeedTestSocketFactory speedTestSocketFactory) {
|
||||
mIpIPRankingService.setSocketFactory(speedTestSocketFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpdateServerTimeInterval(int timeInterval) {
|
||||
mScheduleService.setTimeInterval(timeInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSniffTimeInterval(int timeInterval) {
|
||||
mRequestHandler.setSniffTimeInterval(timeInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutorService getWorker() {
|
||||
return mHttpDnsConfig.mWorker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultUpdateServer(String[] defaultServerIps, int[] ports) {
|
||||
mHttpDnsConfig.setDefaultUpdateServer(defaultServerIps, ports);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultUpdateServerIpv6(String[] defaultServerIps, int[] ports) {
|
||||
mHttpDnsConfig.setDefaultUpdateServerIpv6(defaultServerIps, ports);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNetworkDetector(HttpDnsSettings.NetworkDetector networkDetector) {
|
||||
mHttpDnsConfig.setNetworkDetector(networkDetector);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.alibaba.sdk.android.httpdns.impl;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsService;
|
||||
|
||||
/**
|
||||
* 改为使用测试实例
|
||||
* @author zonglin.nzl
|
||||
* @date 2020/12/4
|
||||
*/
|
||||
public class InstanceCreator implements HttpDnsCreator {
|
||||
@Override
|
||||
public HttpDnsService create(Context context, String accountId, String secretKey) {
|
||||
return new HttpDnsServiceTestImpl(context.getApplicationContext(), accountId, secretKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsInstanceHolder;
|
||||
import com.alibaba.sdk.android.httpdns.impl.InstanceCreator;
|
||||
import com.alibaba.sdk.android.httpdns.utils.CommonUtil;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Httpdns实例管理
|
||||
*/
|
||||
public class HttpDns {
|
||||
|
||||
private static final HttpDnsInstanceHolder HOLDER = new HttpDnsInstanceHolder(
|
||||
new InstanceCreator());
|
||||
|
||||
/**
|
||||
* 获取HttpDnsService对象
|
||||
* @param accountId HttpDns控制台分配的AccountID
|
||||
* @return
|
||||
*/
|
||||
public synchronized static HttpDnsService getService(final String accountId) {
|
||||
return HOLDER.get(null, accountId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取HttpDnsService对象
|
||||
* 该方法已弃用,建议使用{@link HttpDns#getService(String)}方法
|
||||
* @param applicationContext 当前APP的Context
|
||||
* @param accountID HttpDns控制台分配的AccountID
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
public synchronized static HttpDnsService getService(final Context applicationContext,
|
||||
final String accountID) {
|
||||
return HOLDER.get(applicationContext, accountID, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取HttpDnsService对象,并启用鉴权功能
|
||||
* 该方法已弃用,建议使用{@link HttpDns#getService(String)}方法
|
||||
* @param applicationContext 当前APP的Context
|
||||
* @param accountID HttpDns控制台分配的AccountID
|
||||
* @param secretKey 用户鉴权私钥
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
public synchronized static HttpDnsService getService(final Context applicationContext,
|
||||
final String accountID,
|
||||
final String secretKey) {
|
||||
return HOLDER.get(applicationContext, accountID, secretKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取HttpDnsService对象,初始化时不传入任何参数,靠统一接入服务获取相关参数
|
||||
* 该方法已弃用,建议使用{@link HttpDns#getService(String)}方法
|
||||
* @param applicationContext 当前APP的Context
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
public synchronized static HttpDnsService getService(final Context applicationContext) {
|
||||
return HOLDER.get(applicationContext, CommonUtil.getAccountId(applicationContext),
|
||||
CommonUtil.getSecretKey(applicationContext));
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化方法,该方法主要是保存{@link InitConfig},不会真正进行初始化。真正初始化是在{@link HttpDns#getService(Context, String, String)}中
|
||||
* 这么实现主要是为了兼容{@link InitConfig.Builder#buildFor(String)}方法,新客户使用该方法和旧的方法功能一致
|
||||
* @param accountId HttpDns控制台分配的AccountID
|
||||
* @param config {@link InitConfig}
|
||||
*/
|
||||
public static void init(String accountId, InitConfig config) {
|
||||
InitConfig.addConfig(accountId, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用或者禁用httpdns,理论上这个是内部接口,不给外部使用的
|
||||
* 但是已经对外暴露,所以保留
|
||||
*
|
||||
* @param enabled
|
||||
* @deprecated 启用禁用应该调用实例的方法,而不是控制全部实例的方法
|
||||
*/
|
||||
@Deprecated
|
||||
public synchronized static void switchDnsService(boolean enabled) {
|
||||
// do nothing as deprecated
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.alibaba.sdk.android.httpdns.impl;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsService;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class InstanceCreator implements HttpDnsCreator {
|
||||
@Override
|
||||
public HttpDnsService create(Context context, String accountId, String secretKey) {
|
||||
return new IntlImpl(context, accountId, secretKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.alibaba.sdk.android.httpdns.impl;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class IntlImpl extends HttpDnsServiceImpl {
|
||||
public IntlImpl(Context context, String accountId, String secret) {
|
||||
super(context, accountId, secret);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.alibaba.sdk.android.httpdns">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
/**
|
||||
* 修改ttl时长的接口
|
||||
* 用于用户定制ttl,以控制缓存的时长
|
||||
*/
|
||||
public interface CacheTtlChanger {
|
||||
|
||||
/**
|
||||
* 根据 域名 ip类型 和 服务的ttl 返回 定制的ttl
|
||||
*
|
||||
* @param host 域名
|
||||
* @param type ip类型
|
||||
* @param ttl 服务下发的ttl 单位秒
|
||||
* @return 定制的ttl 单位秒
|
||||
*/
|
||||
int changeCacheTtl(String host, RequestIpType type, int ttl);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
/**
|
||||
* 降级判断开关接口
|
||||
*/
|
||||
@Deprecated
|
||||
public interface DegradationFilter {
|
||||
/**
|
||||
* 是否应该不使用httpdns
|
||||
*
|
||||
*/
|
||||
boolean shouldDegradeHttpDNS(String hostName);
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.cache.HostRecord;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* sdns的解析结果
|
||||
*/
|
||||
public class HTTPDNSResult {
|
||||
String host;
|
||||
String[] ips;
|
||||
String[] ipv6s;
|
||||
String extra;
|
||||
long queryTime;
|
||||
int ttl;
|
||||
boolean fromDB;
|
||||
boolean fromLocalDns;
|
||||
|
||||
public HTTPDNSResult(String host) {
|
||||
this.host = host;
|
||||
this.ips = Constants.NO_IPS;
|
||||
this.ipv6s = Constants.NO_IPS;
|
||||
this.extra = "";
|
||||
queryTime = 0;
|
||||
ttl = 0;
|
||||
fromDB = false;
|
||||
fromLocalDns = false;
|
||||
}
|
||||
|
||||
public HTTPDNSResult(String host, String[] ips, String[] ipv6s, String extra, boolean expired, boolean isFromDB) {
|
||||
this.host = host;
|
||||
this.ips = ips;
|
||||
this.ipv6s = ipv6s;
|
||||
this.extra = extra;
|
||||
this.queryTime = System.currentTimeMillis();
|
||||
this.ttl = 60;
|
||||
this.fromDB = isFromDB;
|
||||
fromLocalDns = false;
|
||||
}
|
||||
|
||||
public HTTPDNSResult (String host, String[] ips, String[] ipv6s, String extra, boolean expired, boolean isFromDB, boolean isFromLocalDns) {
|
||||
this(host, ips, ipv6s, extra, expired, isFromDB);
|
||||
fromLocalDns = isFromLocalDns;
|
||||
}
|
||||
|
||||
public static HTTPDNSResult empty(String host) {
|
||||
return new HTTPDNSResult(host, new String[0], new String[0], "", false, false);
|
||||
}
|
||||
|
||||
public void update(HostRecord record) {
|
||||
if (record.getHost().equals(host)) {
|
||||
if (record.getType() == RequestIpType.v4.ordinal()) {
|
||||
ips = record.getIps();
|
||||
} else if (record.getType() == RequestIpType.v6.ordinal()) {
|
||||
ipv6s = record.getIps();
|
||||
}
|
||||
extra = TextUtils.isEmpty(record.getExtra()) ? "" : record.getExtra();
|
||||
queryTime = record.getQueryTime();
|
||||
ttl = record.getTtl();
|
||||
fromDB = record.isFromDB();
|
||||
}
|
||||
}
|
||||
|
||||
public void update(List<HostRecord> records) {
|
||||
String extra = null;
|
||||
long queryTime = System.currentTimeMillis();
|
||||
int ttl = Integer.MAX_VALUE;
|
||||
boolean fromDB = false;
|
||||
|
||||
for (HostRecord record : records) {
|
||||
if (record.getHost().equals(host)) {
|
||||
if (record.getType() == RequestIpType.v4.ordinal()) {
|
||||
ips = record.getIps();
|
||||
} else if (record.getType() == RequestIpType.v6.ordinal()) {
|
||||
ipv6s = record.getIps();
|
||||
}
|
||||
if (record.getExtra() != null && !record.getExtra().isEmpty()) {
|
||||
extra = record.getExtra();
|
||||
}
|
||||
if (queryTime > record.getQueryTime()) {
|
||||
queryTime = record.getQueryTime();
|
||||
}
|
||||
if (ttl > record.getTtl()) {
|
||||
ttl = record.getTtl();
|
||||
}
|
||||
fromDB |= record.isFromDB();
|
||||
}
|
||||
}
|
||||
this.extra = TextUtils.isEmpty(extra) ? "" : extra;
|
||||
this.queryTime = queryTime;
|
||||
this.ttl = ttl;
|
||||
this.fromDB = fromDB;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String sb = "host:"
|
||||
+ host
|
||||
+ ", ips:"
|
||||
+ Arrays.toString(ips)
|
||||
+ ", ipv6s:"
|
||||
+ Arrays.toString(ipv6s)
|
||||
+ ", extras:"
|
||||
+ extra
|
||||
+ ", expired:"
|
||||
+ isExpired()
|
||||
+ ", fromDB:"
|
||||
+ fromDB
|
||||
+ ", from Local Dns:"
|
||||
+ fromLocalDns;
|
||||
return sb;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public String[] getIps() {
|
||||
return ips;
|
||||
}
|
||||
|
||||
public String[] getIpv6s() {
|
||||
return ipv6s;
|
||||
}
|
||||
|
||||
public String getExtras() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public int getTtl() {
|
||||
return ttl;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return !fromLocalDns && Math.abs(System.currentTimeMillis() - queryTime) > ttl * 1000L;
|
||||
}
|
||||
|
||||
public boolean isFromDB() {
|
||||
return fromDB;
|
||||
}
|
||||
|
||||
public boolean isFromLocalDns() {
|
||||
return fromLocalDns;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.cache.HostRecord;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 对解析结果进行封装,用于可观测数据的一些解析数据携带,不对外透出
|
||||
*/
|
||||
public class HTTPDNSResultWrapper {
|
||||
private final HTTPDNSResult mHTTPDNSResult;
|
||||
private String mServerIp;
|
||||
|
||||
public HTTPDNSResultWrapper(String host) {
|
||||
mHTTPDNSResult = new HTTPDNSResult(host);
|
||||
}
|
||||
|
||||
public HTTPDNSResult getHTTPDNSResult() {
|
||||
return mHTTPDNSResult;
|
||||
}
|
||||
|
||||
public void update(HostRecord record) {
|
||||
mHTTPDNSResult.update(record);
|
||||
mServerIp = record.getServerIp();
|
||||
}
|
||||
|
||||
public void update(List<HostRecord> records) {
|
||||
mHTTPDNSResult.update(records);
|
||||
|
||||
for (HostRecord record : records) {
|
||||
if (record.getHost().equals(mHTTPDNSResult.getHost())) {
|
||||
if (!TextUtils.isEmpty(record.getServerIp())) {
|
||||
mServerIp = record.getServerIp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return mHTTPDNSResult.isExpired();
|
||||
}
|
||||
|
||||
public String[] getIps() {
|
||||
return mHTTPDNSResult.getIps();
|
||||
}
|
||||
|
||||
public String[] getIpv6s() {
|
||||
return mHTTPDNSResult.getIpv6s();
|
||||
}
|
||||
|
||||
public String getServerIp() {
|
||||
return mServerIp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mHTTPDNSResult.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
public interface HttpDnsCallback {
|
||||
void onHttpDnsCompleted(HTTPDNSResult result);
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* HttpDns服务接口
|
||||
*/
|
||||
public interface HttpDnsService {
|
||||
/**
|
||||
* 设置预解析域名列表,默认解析ipv4
|
||||
*
|
||||
* @param hostList 预解析的host域名列表
|
||||
*/
|
||||
void setPreResolveHosts(List<String> hostList);
|
||||
|
||||
/**
|
||||
* 设置预解析域名列表和解析的ip类型
|
||||
*
|
||||
* @param hostList 预解析的host列表
|
||||
* @param requestIpType {@link RequestIpType} 网络栈类型,v4,v6,both,auto(根据当前设备所连的网络自动判断网络栈)
|
||||
*/
|
||||
void setPreResolveHosts(List<String> hostList, RequestIpType requestIpType);
|
||||
|
||||
/**
|
||||
* 异步解析接口, 首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
*
|
||||
* @param host 如www.aliyun.com
|
||||
* @return 返回ip, 如果没得到解析结果, 则返回null
|
||||
* @deprecated 该接口已废弃,后续版本可能会被删除,请使用{@link #getIPv4ForHostAsync(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
String getIpByHostAsync(String host);
|
||||
|
||||
/**
|
||||
* 异步解析接口,只查询v4地址,首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @return 返回v4地址
|
||||
*/
|
||||
@Deprecated
|
||||
String getIPv4ForHostAsync(String host);
|
||||
|
||||
/**
|
||||
* 异步解析接口, 获取ipv4列表
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @return 返回v4地址的String 数组, 如果没得到解析结果, 则String 数组的长度为0
|
||||
* @deprecated 该接口已废弃,后续版本可能会被删除,请使用{@link #getIPv4ListForHostAsync(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
String[] getIpsByHostAsync(String host);
|
||||
|
||||
/**
|
||||
* 异步解析接口, 获取ipv4列表
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @return 返回v4地址列表
|
||||
*/
|
||||
@Deprecated
|
||||
String[] getIPv4ListForHostAsync(String host);
|
||||
|
||||
/**
|
||||
* 异步解析接口,获取单个ipv6, 首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @return 返回v6地址
|
||||
* @deprecated 该接口已废弃,后续版本可能会被删除,请使用{@link #getIPv6ForHostAsync(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
String getIPv6ByHostAsync(String host);
|
||||
|
||||
/**
|
||||
* 异步解析接口,获取单个ipv6, 首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @return 返回v6地址
|
||||
*/
|
||||
@Deprecated
|
||||
String getIPv6ForHostAsync(String host);
|
||||
|
||||
/**
|
||||
* 异步解析接口,获取ipv6列表, 首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @return 返回v6地址的String 数组, 如果没得到解析结果, 则String 数组的长度为0
|
||||
* @deprecated 该接口已废弃,后续版本可能会被删除,请使用{@link #getIPv6ListForHostASync(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
String[] getIPv6sByHostAsync(String host);
|
||||
|
||||
/**
|
||||
* 异步解析接口,获取ipv6列表, 首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @return 返回v6地址的String 数组, 如果没得到解析结果, 则String 数组的长度为0
|
||||
*/
|
||||
@Deprecated
|
||||
String[] getIPv6ListForHostASync(String host);
|
||||
|
||||
/**
|
||||
* 异步解析接口,获取ipv4ipv6列表
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @return {@link HTTPDNSResult}
|
||||
* @deprecated 该接口已废弃,后续版本可能会被删除,请使用{@link #getHttpDnsResultForHostAsync(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
HTTPDNSResult getAllByHostAsync(String host);
|
||||
|
||||
/**
|
||||
* 异步解析接口,获取ipv4 + ipv6列表, 首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @return {@link HTTPDNSResult}
|
||||
*/
|
||||
@Deprecated
|
||||
HTTPDNSResult getHttpDnsResultForHostAsync(String host);
|
||||
|
||||
/**
|
||||
* 异步解析接口,获取ipv4 + ipv6列表, 首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
* 支持 指定解析IP类型
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @param type {@link RequestIpType} 网络栈类型,v4,v6,both,auto(根据当前设备所连的网络自动判断网络栈)
|
||||
* @return {@link HTTPDNSResult}
|
||||
* @deprecated 该接口已废弃,后续版本可能会被删除,请使用{@link #getHttpDnsResultForHostAsync(String, RequestIpType)}
|
||||
*/
|
||||
@Deprecated
|
||||
HTTPDNSResult getIpsByHostAsync(String host, RequestIpType type);
|
||||
|
||||
/**
|
||||
* 异步解析接口,获取ipv4 + ipv6列表, 首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
* 支持 指定解析IP类型
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @param type {@link RequestIpType} 网络栈类型,v4,v6,both,auto(根据当前设备所连的网络自动判断网络栈)
|
||||
* @return {@link HTTPDNSResult}
|
||||
*/
|
||||
@Deprecated
|
||||
HTTPDNSResult getHttpDnsResultForHostAsync(String host, RequestIpType type);
|
||||
|
||||
/***
|
||||
* 校正App签名时间
|
||||
* @param time time为epoch时间戳,1970年1月1日以来的秒数
|
||||
*/
|
||||
void setAuthCurrentTime(long time);
|
||||
|
||||
/**
|
||||
* 获取会话id
|
||||
*
|
||||
* @return sid
|
||||
*/
|
||||
String getSessionId();
|
||||
|
||||
//以下针对SDNS
|
||||
|
||||
/**
|
||||
* 异步解析接口, 获取ipv4 + ipv6 列表,首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
* 支持配置sdns参数
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @return {@link HTTPDNSResult}
|
||||
* @deprecated 该接口已废弃,后续版本可能会被删除,请使用{@link #getHttpDnsResultForHostAsync(String, Map, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
HTTPDNSResult getIpsByHostAsync(String host, Map<String, String> params, String cacheKey);
|
||||
|
||||
/**
|
||||
* 异步解析接口, 获取ipv4 + ipv6列表, 首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
* 支持配置sdns参数
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @param params
|
||||
* @param cacheKey
|
||||
* @return {@link HTTPDNSResult}
|
||||
*/
|
||||
@Deprecated
|
||||
HTTPDNSResult getHttpDnsResultForHostAsync(String host, Map<String, String> params,
|
||||
String cacheKey);
|
||||
|
||||
/**
|
||||
* 异步解析接口, 获取ipv4 + ipv6列表, 首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
* 支持配置sdns参数
|
||||
* 支持指定解析类型
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @param type {@link RequestIpType} 网络栈类型,v4,v6,both,auto(根据当前设备所连的网络自动判断网络栈)
|
||||
* @param params
|
||||
* @param cacheKey
|
||||
* @return {@link HTTPDNSResult}
|
||||
* @deprecated 该接口已废弃,后续版本可能会被删除,请使用{@link #getHttpDnsResultForHostAsync(String, RequestIpType,
|
||||
* Map, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
HTTPDNSResult getIpsByHostAsync(String host, RequestIpType type, Map<String, String> params,
|
||||
String cacheKey);
|
||||
|
||||
/**
|
||||
* 异步解析接口, 获取ipv4 + ipv6列表, 首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
* 支持配置sdns参数
|
||||
* 支持指定解析类型
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @param type {@link RequestIpType} 网络栈类型,v4,v6,both,auto(根据当前设备所连的网络自动判断网络栈)
|
||||
* @param params 自定义解析参数
|
||||
* @param cacheKey 缓存的key
|
||||
* @return {@link HTTPDNSResult}
|
||||
*/
|
||||
@Deprecated
|
||||
HTTPDNSResult getHttpDnsResultForHostAsync(String host, RequestIpType type, Map<String,
|
||||
String> params, String cacheKey);
|
||||
|
||||
/**
|
||||
* 同步解析接口,支持指定解析类型
|
||||
* 支持配置sdns参数
|
||||
* 需要注意的地方:
|
||||
* 1. 该方法必须在子线程中执行,如果在主线程中调用该方法,方法内部会自动切换成异步执行
|
||||
* 2. 同步接口会阻塞当前子线程,阻塞时长可以通过{@link InitConfig.Builder#setTimeout(int)}设置,
|
||||
* 不过阻塞时长的上限是5s,如果设置的超时时长超过5s则无效
|
||||
*
|
||||
* @param host 要解析的host域名列表
|
||||
* @param type {@link RequestIpType}
|
||||
* @param params 自定义解析参数
|
||||
* @param cacheKey 缓存的key
|
||||
* @return {@link HTTPDNSResult}
|
||||
*/
|
||||
HTTPDNSResult getHttpDnsResultForHostSync(String host, RequestIpType type, Map<String,
|
||||
String> params, String cacheKey);
|
||||
|
||||
/**
|
||||
* 异步解析接口, 获取ipv4 + ipv6列表, 通过回调返回解析结果,首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
* 支持配置sdns参数
|
||||
* 支持指定解析类型
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @param type {@link RequestIpType} 网络栈类型,v4,v6,both,auto(根据当前设备所连的网络自动判断网络栈)
|
||||
* @param params 自定义解析参数
|
||||
* @param cacheKey 缓存的key
|
||||
*/
|
||||
void getHttpDnsResultForHostAsync(String host, RequestIpType type, Map<String,
|
||||
String> params, String cacheKey, HttpDnsCallback callback);
|
||||
|
||||
/**
|
||||
* 异步解析接口, 获取ipv4 + ipv6列表, 首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
* 支持配置sdns参数
|
||||
* 支持指定解析类型
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @param type {@link RequestIpType} 网络栈类型,v4,v6,both,auto(根据当前设备所连的网络自动判断网络栈)
|
||||
* @param params 自定义解析参数
|
||||
* @param cacheKey 缓存的key
|
||||
*/
|
||||
HTTPDNSResult getHttpDnsResultForHostSyncNonBlocking(String host, RequestIpType type, Map<String,
|
||||
String> params, String cacheKey);
|
||||
|
||||
//以上针对SDNS
|
||||
|
||||
/**
|
||||
* 设置region,海外节点
|
||||
* 国内版默认是中国大陆节点,国际版默认是新加坡节点
|
||||
*
|
||||
* @param region sg(新家坡), hk(中国香港), ""(中国大陆), de(德国), us(美国)
|
||||
*/
|
||||
@Deprecated
|
||||
void setRegion(String region);
|
||||
|
||||
/**
|
||||
* 设置region
|
||||
* 国内版默认是中国大陆节点
|
||||
*
|
||||
* @param region {@link Region}
|
||||
*/
|
||||
void setRegion(Region region);
|
||||
|
||||
/**
|
||||
* 立即清除域名端侧内存和本地缓存。
|
||||
* 后续调用异步接口,会先返回空,触发域名解析
|
||||
*
|
||||
* @param hosts host域名列表
|
||||
*/
|
||||
void cleanHostCache(ArrayList<String> hosts);
|
||||
|
||||
/**
|
||||
* 同步解析接口,支持指定解析类型
|
||||
* 需要注意的地方:
|
||||
* 1. 该方法必须在子线程中执行,如果在主线程中调用该方法,方法内部会自动切换成异步执行
|
||||
* 2. 同步接口会阻塞当前子线程,阻塞时长可以通过{@link InitConfig.Builder#setTimeout(int)}设置,
|
||||
* 不过阻塞时长的上限是5s,如果设置的超时时长超过5s则无效
|
||||
*
|
||||
* @param host 要解析的host域名列表
|
||||
* @param type {@link RequestIpType}
|
||||
* @return {@link HTTPDNSResult}
|
||||
*/
|
||||
HTTPDNSResult getHttpDnsResultForHostSync(String host, RequestIpType type);
|
||||
|
||||
/**
|
||||
* 异步解析接口,根据type获取ip, 通过回调返回解析结果,首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求
|
||||
* 支持 指定解析IP类型
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @param type {@link RequestIpType} 网络栈类型,v4,v6,both,auto(根据当前设备所连的网络自动判断网络栈)
|
||||
*/
|
||||
void getHttpDnsResultForHostAsync(String host, RequestIpType type, HttpDnsCallback callback);
|
||||
|
||||
/**
|
||||
* 异步解析接口,根据type获取ip, 首先查询缓存, 若存在则返回结果, 若不存在返回null 并且进行异步域名解析请求(不会通过回调给调用方)
|
||||
* 支持 指定解析IP类型
|
||||
*
|
||||
* @param host 要解析的host域名
|
||||
* @param type {@link RequestIpType} 网络栈类型,v4,v6,both,auto(根据当前设备所连的网络自动判断网络栈)
|
||||
* @return {@link HTTPDNSResult}
|
||||
*/
|
||||
HTTPDNSResult getHttpDnsResultForHostSyncNonBlocking(String host, RequestIpType type);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class HttpDnsSettings {
|
||||
|
||||
private static boolean sDailyReport = true;
|
||||
|
||||
public static void setDailyReport(boolean dailyReport) {
|
||||
HttpDnsSettings.sDailyReport = dailyReport;
|
||||
}
|
||||
|
||||
public static boolean isDailyReport() {
|
||||
return sDailyReport;
|
||||
}
|
||||
|
||||
private static NetworkChecker checker = new NetworkChecker() {
|
||||
@Override
|
||||
public boolean isIpv6Only() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@Deprecated
|
||||
public static void setNetworkChecker(NetworkChecker checker) {
|
||||
HttpDnsSettings.checker = checker;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static NetworkChecker getChecker() {
|
||||
return checker;
|
||||
}
|
||||
|
||||
/**
|
||||
* 需要外部注入的一些网络环境判断
|
||||
*/
|
||||
public interface NetworkChecker {
|
||||
boolean isIpv6Only();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网络类型的接口
|
||||
*/
|
||||
public interface NetworkDetector {
|
||||
NetType getNetType(Context context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
/**
|
||||
* 日志接口
|
||||
*/
|
||||
public interface ILogger {
|
||||
|
||||
/**
|
||||
* 日志输出
|
||||
*/
|
||||
void log(String msg);
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.exception.InitException;
|
||||
import com.alibaba.sdk.android.httpdns.ranking.IPRankingBean;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
/**
|
||||
* 初始化配置
|
||||
* 之前的初始化方式,每个配置都是单独设置的,有可能造成一些时序上的问题
|
||||
* 所以增加统一初始化配置的方法,由内部固定初始化逻辑,避免时序问题
|
||||
*/
|
||||
public class InitConfig {
|
||||
|
||||
private static final Map<String, InitConfig> configs = new ConcurrentHashMap<>();
|
||||
|
||||
static void addConfig(String accountId, InitConfig config) {
|
||||
configs.put(accountId, config);
|
||||
}
|
||||
|
||||
public static final String NOT_SET = null;
|
||||
|
||||
public static InitConfig getInitConfig(String accountId) {
|
||||
return configs.get(accountId);
|
||||
}
|
||||
|
||||
public static void removeConfig(String accountId) {
|
||||
if (accountId == null || accountId.isEmpty()) {
|
||||
configs.clear();
|
||||
} else {
|
||||
configs.remove(accountId);
|
||||
}
|
||||
}
|
||||
|
||||
private final boolean mEnableExpiredIp;
|
||||
private final boolean mEnableCacheIp;
|
||||
private final long mExpiredThresholdMillis;
|
||||
private final int mTimeout;
|
||||
private final boolean mEnableHttps;
|
||||
private final List<IPRankingBean> mIPRankingList;
|
||||
private final String mRegion;
|
||||
private final CacheTtlChanger mCacheTtlChanger;
|
||||
private final List<String> mHostListWithFixedIp;
|
||||
private final boolean mResolveAfterNetworkChange;
|
||||
private final DegradationFilter mDegradationFilter;
|
||||
private final NotUseHttpDnsFilter mNotUseHttpDnsFilter;
|
||||
private final boolean mEnableCrashDefend;
|
||||
private final Map<String, String> mSdnsGlobalParams;
|
||||
private final boolean mEnableDegradationLocalDns;
|
||||
private final boolean mEnableObservable;
|
||||
private final String mBizTags;
|
||||
private final String aesSecretKey;
|
||||
private final String secretKey;
|
||||
private final Context context;
|
||||
|
||||
private InitConfig(Builder builder) {
|
||||
mEnableExpiredIp = builder.enableExpiredIp;
|
||||
mEnableCacheIp = builder.enableCacheIp;
|
||||
mExpiredThresholdMillis = builder.expiredThresholdMillis;
|
||||
mTimeout = builder.timeout;
|
||||
mEnableHttps = builder.enableHttps;
|
||||
mIPRankingList = builder.ipRankingList;
|
||||
mRegion = builder.region;
|
||||
mCacheTtlChanger = builder.cacheTtlChanger;
|
||||
mHostListWithFixedIp = builder.hostListWithFixedIp;
|
||||
mResolveAfterNetworkChange = builder.resolveAfterNetworkChange;
|
||||
mDegradationFilter = builder.degradationFilter;
|
||||
mNotUseHttpDnsFilter = builder.notUseHttpDnsFilter;
|
||||
mEnableCrashDefend = builder.enableCrashDefend;
|
||||
mSdnsGlobalParams = builder.sdnsGlobalParams;
|
||||
mEnableDegradationLocalDns = builder.enableDegradationLocalDns;
|
||||
mEnableObservable = builder.enableObservable;
|
||||
mBizTags = builder.bizTags;
|
||||
aesSecretKey = builder.aesSecretKey;
|
||||
context = builder.context;
|
||||
secretKey = builder.secretKey;
|
||||
}
|
||||
|
||||
public boolean isEnableExpiredIp() {
|
||||
return mEnableExpiredIp;
|
||||
}
|
||||
|
||||
public boolean isEnableCacheIp() {
|
||||
return mEnableCacheIp;
|
||||
}
|
||||
|
||||
public long getExpiredThresholdMillis() {
|
||||
return mExpiredThresholdMillis;
|
||||
}
|
||||
|
||||
public boolean isResolveAfterNetworkChange() {
|
||||
return mResolveAfterNetworkChange;
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
return mTimeout;
|
||||
}
|
||||
|
||||
public boolean isEnableHttps() {
|
||||
return mEnableHttps;
|
||||
}
|
||||
|
||||
public boolean isEnableDegradationLocalDns() {
|
||||
return mEnableDegradationLocalDns;
|
||||
}
|
||||
|
||||
public List<IPRankingBean> getIPRankingList() {
|
||||
return mIPRankingList;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return mRegion;
|
||||
}
|
||||
|
||||
public CacheTtlChanger getCacheTtlChanger() {
|
||||
return mCacheTtlChanger;
|
||||
}
|
||||
|
||||
public List<String> getHostListWithFixedIp() {
|
||||
return mHostListWithFixedIp;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public DegradationFilter getDegradationFilter() {
|
||||
return mDegradationFilter;
|
||||
}
|
||||
|
||||
public NotUseHttpDnsFilter getNotUseHttpDnsFilter() {
|
||||
return mNotUseHttpDnsFilter;
|
||||
}
|
||||
|
||||
public boolean isEnableCrashDefend() {
|
||||
return mEnableCrashDefend;
|
||||
}
|
||||
|
||||
public Map<String, String> getSdnsGlobalParams() {
|
||||
return mSdnsGlobalParams;
|
||||
}
|
||||
|
||||
public boolean isEnableObservable() {
|
||||
return mEnableObservable;
|
||||
}
|
||||
|
||||
public String getBizTags() {
|
||||
return mBizTags;
|
||||
}
|
||||
|
||||
public String getAesSecretKey() {
|
||||
return aesSecretKey;
|
||||
}
|
||||
|
||||
public Context getContext() {return context;}
|
||||
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private boolean enableExpiredIp = Constants.DEFAULT_ENABLE_EXPIRE_IP;
|
||||
private boolean enableCacheIp = Constants.DEFAULT_ENABLE_CACHE_IP;
|
||||
private long expiredThresholdMillis = 0L;
|
||||
private int timeout = Constants.DEFAULT_TIMEOUT;
|
||||
private boolean enableDegradationLocalDns = Constants.DEFAULT_ENABLE_DEGRADATION_LOCAL_DNS;
|
||||
private boolean enableHttps = Constants.DEFAULT_ENABLE_HTTPS;
|
||||
private List<IPRankingBean> ipRankingList = null;
|
||||
private String region = NOT_SET;
|
||||
private CacheTtlChanger cacheTtlChanger = null;
|
||||
private List<String> hostListWithFixedIp = null;
|
||||
private boolean resolveAfterNetworkChange = true;
|
||||
private DegradationFilter degradationFilter = null;
|
||||
private NotUseHttpDnsFilter notUseHttpDnsFilter = null;
|
||||
private boolean enableCrashDefend = false;
|
||||
private boolean enableObservable = true;
|
||||
private Map<String, String> sdnsGlobalParams = null;
|
||||
private String bizTags = null;
|
||||
private String aesSecretKey = null;
|
||||
private Context context = null;
|
||||
private String secretKey = null;
|
||||
|
||||
/**
|
||||
* 设置是否允许返回超过ttl 的ip
|
||||
* @param enableExpiredIp 是否允许返回超过ttl 的ip
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
public Builder setEnableExpiredIp(boolean enableExpiredIp) {
|
||||
this.enableExpiredIp = enableExpiredIp;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否允许使用DB缓存,默认不允许
|
||||
* @param enableCacheIp 是否允许使用DB缓存
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
public Builder setEnableCacheIp(boolean enableCacheIp) {
|
||||
this.enableCacheIp = enableCacheIp;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setEnableCacheIp(boolean enableCacheIp, long expiredThresholdMillis) {
|
||||
this.enableCacheIp = enableCacheIp;
|
||||
if (expiredThresholdMillis >= 0 && expiredThresholdMillis <= DateUtils.YEAR_IN_MILLIS) {
|
||||
this.expiredThresholdMillis = expiredThresholdMillis;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置请求超时时间,单位ms,默认为2s
|
||||
* @param timeoutMillis 超时时间,单位ms
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
public Builder setTimeoutMillis(int timeoutMillis) {
|
||||
timeout = timeoutMillis;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置请求超时时间,单位ms,默认为2s
|
||||
* @param timeout 超时时间,单位ms
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder setTimeout(int timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置开启/关闭降级到Local Dns,在httpdns解析失败或者域名被过滤不走httpdns的时候,开启降级会走local dns解析
|
||||
* @param enableDegradation true, 开启 | false, 关闭
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
public Builder setEnableDegradationLocalDns(boolean enableDegradation) {
|
||||
enableDegradationLocalDns = enableDegradation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置HTTPDNS域名解析请求类型(HTTP/HTTPS),若不调用该接口,默认为HTTP请求
|
||||
* @param enableHttps 是否使用https
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
public Builder setEnableHttps(boolean enableHttps) {
|
||||
this.enableHttps = enableHttps;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置要探测的域名列表,默认只会对ipv4的地址进行ip优选
|
||||
* @param ipRankingList {@link IPRankingBean}
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
public Builder setIPRankingList(List<IPRankingBean> ipRankingList) {
|
||||
this.ipRankingList = ipRankingList;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Builder setRegion(String region) {
|
||||
this.region = region;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRegion(Region region) {
|
||||
if (region != null) {
|
||||
this.region = region.getRegion();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置自定义ttl的逻辑
|
||||
*
|
||||
* @param cacheTtlChanger 修改ttl的接口
|
||||
*/
|
||||
public Builder configCacheTtlChanger(CacheTtlChanger cacheTtlChanger) {
|
||||
this.cacheTtlChanger = cacheTtlChanger;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置主站域名列表
|
||||
*
|
||||
* @param hostListWithFixedIp 主站域名列表
|
||||
*/
|
||||
public Builder configHostWithFixedIp(List<String> hostListWithFixedIp) {
|
||||
this.hostListWithFixedIp = hostListWithFixedIp;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置网络切换时是否自动刷新所有域名解析结果,默认自动刷新
|
||||
* @param enable 是否允许自动刷新域名解析结果
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
public Builder setPreResolveAfterNetworkChanged(boolean enable) {
|
||||
resolveAfterNetworkChange = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置降级策略, 用户可定制规则降级为原生DNS解析方式
|
||||
* @param filter {@link DegradationFilter}
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder setDegradationFilter(DegradationFilter filter) {
|
||||
degradationFilter = filter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置不使用HttpDns的策略, 用户可定制规则指定不走httpdns的域名
|
||||
* @param filter {@link NotUseHttpDnsFilter}
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
public Builder setNotUseHttpDnsFilter(NotUseHttpDnsFilter filter) {
|
||||
notUseHttpDnsFilter = filter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否开启sdk内部的崩溃保护机制,默认是关闭的
|
||||
* @param enabled 开启/关闭
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
public Builder enableCrashDefend(boolean enabled) {
|
||||
enableCrashDefend = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置sdns全局参数(该全局参数不影响异步解析任务,只用于解析接口调用时进行参数合并)
|
||||
* @param params sdn的全局参数
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
public Builder setSdnsGlobalParams(Map<String, String> params) {
|
||||
sdnsGlobalParams = params;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder enableObservable(boolean enabled) {
|
||||
enableObservable = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setBizTags(List<String> tags) {
|
||||
if (tags == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (tags.size() > 5) {
|
||||
throw new InitException("The number of tags cannot be greater than 5");
|
||||
}
|
||||
|
||||
Pattern p = Pattern.compile("[^a-zA-Z0-9-]");
|
||||
Matcher matcher;
|
||||
StringBuilder tmpTag = new StringBuilder();
|
||||
for (int i = 0; i != tags.size(); ++i) {
|
||||
String tag = tags.get(i);
|
||||
if (TextUtils.isEmpty(tag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
matcher = p.matcher(tag);
|
||||
if (matcher.find()) {
|
||||
throw new InitException("tag can only contain a-z and A-Z and 0-9 and -");
|
||||
}
|
||||
|
||||
if (tmpTag.indexOf(tag) != -1) {
|
||||
//去重
|
||||
continue;
|
||||
}
|
||||
|
||||
tmpTag.append(tag);
|
||||
if (i != tags.size() - 1) {
|
||||
tmpTag.append(",");
|
||||
}
|
||||
}
|
||||
|
||||
int lastCommaIndex = tmpTag.lastIndexOf(",");
|
||||
//最后一位逗号要去掉
|
||||
if (lastCommaIndex == tmpTag.length() - 1) {
|
||||
tmpTag.deleteCharAt(lastCommaIndex);
|
||||
}
|
||||
|
||||
if (tmpTag.length() > 64) {
|
||||
throw new InitException("The length of all tags cannot be greater than 64");
|
||||
}
|
||||
|
||||
bizTags = tmpTag.toString();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置aes加密密钥
|
||||
* @param aesSecretKey 加密密钥
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
public Builder setAesSecretKey(String aesSecretKey) {
|
||||
this.aesSecretKey = aesSecretKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置context
|
||||
* @param context 上下文
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
public Builder setContext(Context context) {
|
||||
if (context instanceof Application) {
|
||||
this.context = context;
|
||||
} else {
|
||||
if (context != null) {
|
||||
this.context = context.getApplicationContext();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置加签密钥
|
||||
* @param secretKey 加签密钥
|
||||
* @return {@link Builder}
|
||||
*/
|
||||
public Builder setSecretKey(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InitConfig build() {
|
||||
return new InitConfig(this);
|
||||
}
|
||||
|
||||
public InitConfig buildFor(String accountId) {
|
||||
InitConfig config = new InitConfig(this);
|
||||
addConfig(accountId, config);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
/**
|
||||
* 网络类型
|
||||
*/
|
||||
public enum NetType {
|
||||
none,
|
||||
v4,
|
||||
v6,
|
||||
both
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
/**
|
||||
* 不使用HttpDns的配置接口
|
||||
*/
|
||||
public interface NotUseHttpDnsFilter {
|
||||
/**
|
||||
* 是否应该不使用httpdns
|
||||
* @param hostName 域名
|
||||
* @return true 不走httpdns解析 | false 走httpdns解析
|
||||
*
|
||||
*/
|
||||
boolean notUseHttpDns(String hostName);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
public enum Region {
|
||||
DEFAULT(""),
|
||||
HK(Constants.REGION_HK),
|
||||
SG(Constants.REGION_SG),
|
||||
DE(Constants.REGION_DE),
|
||||
US(Constants.REGION_US);
|
||||
|
||||
private final String region;
|
||||
Region(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
/**
|
||||
* 请求的ip类型
|
||||
*/
|
||||
public enum RequestIpType {
|
||||
v4,
|
||||
v6,
|
||||
/**
|
||||
* 表示 两个都要
|
||||
*/
|
||||
both,
|
||||
/**
|
||||
* 表示根据网络情况自动判断
|
||||
*/
|
||||
auto
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.alibaba.sdk.android.httpdns;
|
||||
|
||||
public interface SyncService {
|
||||
/**
|
||||
* 同步解析接口,必须在子线程中执行,否则没有效果
|
||||
*
|
||||
* @deprecated 该接口已废弃,后续版本可能会删除,请使用
|
||||
* {@link HttpDnsService#getHttpDnsResultForHostSync(String, RequestIpType)}
|
||||
*/
|
||||
@Deprecated
|
||||
HTTPDNSResult getByHost(String host, RequestIpType type);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package com.alibaba.sdk.android.httpdns.cache;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.utils.CommonUtil;
|
||||
|
||||
/**
|
||||
* ip解析结果记录
|
||||
* 注意计算hash 和 equal实现,没有使用fromDB字段
|
||||
*/
|
||||
public class HostRecord {
|
||||
private long id = -1;
|
||||
private String region;
|
||||
private String host;
|
||||
private String[] ips;
|
||||
private int type;
|
||||
private int ttl;
|
||||
private long queryTime;
|
||||
private String extra;
|
||||
private String cacheKey;
|
||||
private boolean fromDB = false;
|
||||
private String serverIp;
|
||||
private String noIpCode;
|
||||
|
||||
public static HostRecord create(String region, String host, RequestIpType type, String extra,
|
||||
String cacheKey, String[] ips, int ttl, String serverIp, String noIpCode) {
|
||||
HostRecord record = new HostRecord();
|
||||
record.region = region;
|
||||
record.host = host;
|
||||
record.type = type.ordinal();
|
||||
record.ips = ips;
|
||||
record.ttl = ttl;
|
||||
record.queryTime = System.currentTimeMillis();
|
||||
record.extra = extra;
|
||||
record.cacheKey = cacheKey;
|
||||
record.serverIp = serverIp;
|
||||
record.noIpCode = noIpCode;
|
||||
return record;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return System.currentTimeMillis() > queryTime + ttl * 1000L;
|
||||
}
|
||||
|
||||
public void setFromDB(boolean fromDB) {
|
||||
this.fromDB = fromDB;
|
||||
}
|
||||
|
||||
public boolean isFromDB() {
|
||||
return fromDB;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
public void setRegion(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public String[] getIps() {
|
||||
return ips;
|
||||
}
|
||||
|
||||
public void setIps(String[] ips) {
|
||||
this.ips = ips;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getTtl() {
|
||||
return ttl;
|
||||
}
|
||||
|
||||
public void setTtl(int ttl) {
|
||||
this.ttl = ttl;
|
||||
}
|
||||
|
||||
public long getQueryTime() {
|
||||
return queryTime;
|
||||
}
|
||||
|
||||
public void setQueryTime(long queryTime) {
|
||||
this.queryTime = queryTime;
|
||||
}
|
||||
|
||||
public String getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public void setExtra(String extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public String getCacheKey() {
|
||||
return cacheKey;
|
||||
}
|
||||
|
||||
public void setCacheKey(String cacheKey) {
|
||||
this.cacheKey = cacheKey;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setServerIp(String ip) {
|
||||
serverIp = ip;
|
||||
}
|
||||
|
||||
public String getServerIp() {
|
||||
return serverIp;
|
||||
}
|
||||
|
||||
public void setNoIpCode(String noIpCode) {
|
||||
this.noIpCode = noIpCode;
|
||||
}
|
||||
|
||||
public String getNoIpCode() {
|
||||
return noIpCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {return true;}
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
HostRecord that = (HostRecord)o;
|
||||
return id == that.id &&
|
||||
type == that.type &&
|
||||
ttl == that.ttl &&
|
||||
queryTime == that.queryTime &&
|
||||
region.equals(that.region) &&
|
||||
host.equals(that.host) &&
|
||||
Arrays.equals(ips, that.ips) &&
|
||||
CommonUtil.equals(extra, that.extra) &&
|
||||
CommonUtil.equals(cacheKey, that.cacheKey) &&
|
||||
CommonUtil.equals(noIpCode, that.noIpCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Arrays.hashCode(
|
||||
new Object[] {id, region, host, type, ttl, queryTime, extra, cacheKey, noIpCode});
|
||||
result = 31 * result + Arrays.hashCode(ips);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HostRecord{" +
|
||||
"id=" + id +
|
||||
", region='" + region + '\'' +
|
||||
", host='" + host + '\'' +
|
||||
", ips=" + Arrays.toString(ips) +
|
||||
", type=" + type +
|
||||
", ttl=" + ttl +
|
||||
", queryTime=" + queryTime +
|
||||
", extra='" + extra + '\'' +
|
||||
", cacheKey='" + cacheKey + '\'' +
|
||||
", fromDB=" + fromDB +
|
||||
", noIpCode=" + noIpCode +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
package com.alibaba.sdk.android.httpdns.cache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.utils.CommonUtil;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
/**
|
||||
* 数据库存取操作
|
||||
*/
|
||||
public class RecordDBHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final String DB_NAME = "aliclound_httpdns_v3_";
|
||||
private static final int DB_VERSION = 0x03;
|
||||
|
||||
static class HOST {
|
||||
|
||||
static final String TABLE_NAME = "host";
|
||||
|
||||
static final String COL_ID = "id";
|
||||
|
||||
static final String COL_REGION = "region";
|
||||
|
||||
static final String COL_HOST = "host";
|
||||
|
||||
static final String COL_IPS = "ips";
|
||||
|
||||
static final String COL_TYPE = "type";
|
||||
|
||||
static final String COL_TIME = "time";
|
||||
|
||||
static final String COL_TTL = "ttl";
|
||||
|
||||
static final String COL_EXTRA = "extra";
|
||||
|
||||
static final String COL_CACHE_KEY = "cache_key";
|
||||
|
||||
// 旧版本 用于存储网络标识的字段,由于合规的影响,删除了相关代码,此字段变为固定字段,目前已经没有意义
|
||||
static final String COL_SP = "sp";
|
||||
|
||||
static final String COL_NO_IP_CODE = "no_ip_code";
|
||||
|
||||
static final String CREATE_HOST_TABLE_SQL = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
+ COL_ID + " INTEGER PRIMARY KEY,"
|
||||
+ COL_REGION + " TEXT,"
|
||||
+ COL_HOST + " TEXT,"
|
||||
+ COL_IPS + " TEXT,"
|
||||
+ COL_TYPE + " INTEGER,"
|
||||
+ COL_TIME + " INTEGER,"
|
||||
+ COL_TTL + " INTEGER,"
|
||||
+ COL_EXTRA + " TEXT,"
|
||||
+ COL_CACHE_KEY + " TEXT,"
|
||||
+ COL_SP + " TEXT,"
|
||||
+ COL_NO_IP_CODE + " TEXT"
|
||||
+ ");";
|
||||
}
|
||||
|
||||
private final String mAccountId;
|
||||
private final Object mLock = new Object();
|
||||
private SQLiteDatabase mDb;
|
||||
|
||||
public RecordDBHelper(Context context, String accountId) {
|
||||
super(context, DB_NAME + accountId + ".db", null, DB_VERSION);
|
||||
this.mAccountId = accountId;
|
||||
}
|
||||
|
||||
private SQLiteDatabase getDB() {
|
||||
if (mDb == null) {
|
||||
try {
|
||||
mDb = getWritableDatabase();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
return mDb;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
if (mDb != null) {
|
||||
try {
|
||||
mDb.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库获取全部数据
|
||||
*
|
||||
* @param region
|
||||
* @return
|
||||
*/
|
||||
public List<HostRecord> readFromDb(String region) {
|
||||
synchronized (mLock) {
|
||||
ArrayList<HostRecord> hosts = new ArrayList<>();
|
||||
|
||||
SQLiteDatabase db = null;
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
db = getDB();
|
||||
cursor = db.query(HOST.TABLE_NAME, null, HOST.COL_REGION + " = ?",
|
||||
new String[] {region}, null, null, null);
|
||||
if (cursor != null && cursor.getCount() > 0) {
|
||||
cursor.moveToFirst();
|
||||
do {
|
||||
HostRecord hostRecord = new HostRecord();
|
||||
hostRecord.setId(cursor.getLong(cursor.getColumnIndex(HOST.COL_ID)));
|
||||
hostRecord.setRegion(
|
||||
cursor.getString(cursor.getColumnIndex(HOST.COL_REGION)));
|
||||
hostRecord.setHost(cursor.getString(cursor.getColumnIndex(HOST.COL_HOST)));
|
||||
hostRecord.setIps(CommonUtil.parseStringArray(
|
||||
cursor.getString(cursor.getColumnIndex(HOST.COL_IPS))));
|
||||
hostRecord.setType(cursor.getInt(cursor.getColumnIndex(HOST.COL_TYPE)));
|
||||
hostRecord.setTtl(cursor.getInt(cursor.getColumnIndex(HOST.COL_TTL)));
|
||||
hostRecord.setQueryTime(
|
||||
cursor.getLong(cursor.getColumnIndex(HOST.COL_TIME)));
|
||||
hostRecord.setExtra(
|
||||
cursor.getString(cursor.getColumnIndex(HOST.COL_EXTRA)));
|
||||
hostRecord.setCacheKey(
|
||||
cursor.getString(cursor.getColumnIndex(HOST.COL_CACHE_KEY)));
|
||||
hostRecord.setFromDB(true);
|
||||
hostRecord.setNoIpCode(cursor.getString(cursor.getColumnIndex(HOST.COL_NO_IP_CODE)));
|
||||
hosts.add(hostRecord);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
HttpDnsLog.w("read from db fail " + mAccountId, e);
|
||||
} finally {
|
||||
try {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return hosts;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库删除数据
|
||||
*/
|
||||
public void delete(List<HostRecord> records) {
|
||||
if (records == null || records.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
SQLiteDatabase db = null;
|
||||
try {
|
||||
db = getDB();
|
||||
db.beginTransaction();
|
||||
for (HostRecord record : records) {
|
||||
db.delete(HOST.TABLE_NAME, HOST.COL_ID + " = ? ",
|
||||
new String[] {String.valueOf(record.getId())});
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} catch (Exception e) {
|
||||
HttpDnsLog.w("delete record fail " + mAccountId, e);
|
||||
} finally {
|
||||
if (db != null) {
|
||||
try {
|
||||
db.endTransaction();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
*/
|
||||
public void insertOrUpdate(List<HostRecord> records) {
|
||||
synchronized (mLock) {
|
||||
SQLiteDatabase db = null;
|
||||
try {
|
||||
db = getDB();
|
||||
db.beginTransaction();
|
||||
for (HostRecord record : records) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(HOST.COL_REGION, record.getRegion());
|
||||
cv.put(HOST.COL_HOST, record.getHost());
|
||||
cv.put(HOST.COL_IPS, CommonUtil.translateStringArray(record.getIps()));
|
||||
cv.put(HOST.COL_CACHE_KEY, record.getCacheKey());
|
||||
cv.put(HOST.COL_EXTRA, record.getExtra());
|
||||
cv.put(HOST.COL_TIME, record.getQueryTime());
|
||||
cv.put(HOST.COL_TYPE, record.getType());
|
||||
cv.put(HOST.COL_TTL, record.getTtl());
|
||||
cv.put(HOST.COL_NO_IP_CODE, record.getNoIpCode());
|
||||
|
||||
if (record.getId() != -1) {
|
||||
db.update(HOST.TABLE_NAME, cv, HOST.COL_ID + " = ?",
|
||||
new String[] {String.valueOf(record.getId())});
|
||||
} else {
|
||||
long id = db.insert(HOST.TABLE_NAME, null, cv);
|
||||
record.setId(id);
|
||||
}
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} catch (Exception e) {
|
||||
HttpDnsLog.w("insertOrUpdate record fail " + mAccountId, e);
|
||||
} finally {
|
||||
if (db != null) {
|
||||
try {
|
||||
db.endTransaction();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
try {
|
||||
db.execSQL(HOST.CREATE_HOST_TABLE_SQL);
|
||||
} catch (Exception e) {
|
||||
HttpDnsLog.w("create db fail " + mAccountId, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
if (oldVersion != newVersion) {
|
||||
try {
|
||||
db.beginTransaction();
|
||||
db.execSQL("DROP TABLE IF EXISTS " + HOST.TABLE_NAME + ";");
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
onCreate(db);
|
||||
} catch (Exception e) {
|
||||
HttpDnsLog.w("upgrade db fail " + mAccountId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
if (oldVersion != newVersion) {
|
||||
try {
|
||||
db.beginTransaction();
|
||||
db.execSQL("DROP TABLE IF EXISTS " + HOST.TABLE_NAME + ";");
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
onCreate(db);
|
||||
} catch (Exception e) {
|
||||
HttpDnsLog.w("downgrade db fail " + mAccountId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.alibaba.sdk.android.httpdns.config;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsConfig;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
/**
|
||||
* 辅助配置的缓存写入和读取
|
||||
*/
|
||||
public class ConfigCacheHelper {
|
||||
|
||||
private final AtomicBoolean mSaving = new AtomicBoolean(false);
|
||||
|
||||
public void restoreFromCache(Context context, HttpDnsConfig config) {
|
||||
SharedPreferences sp = context.getSharedPreferences(
|
||||
Constants.CONFIG_CACHE_PREFIX + config.getAccountId(), Context.MODE_PRIVATE);
|
||||
SpCacheItem[] items = config.getCacheItem();
|
||||
for (SpCacheItem item : items) {
|
||||
item.restoreFromCache(sp);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveConfigToCache(Context context, HttpDnsConfig config) {
|
||||
if (mSaving.compareAndSet(false, true)) {
|
||||
try {
|
||||
config.getWorker().execute(new WriteCacheTask(context, config, this));
|
||||
} catch (Exception ignored) {
|
||||
mSaving.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class WriteCacheTask implements Runnable {
|
||||
|
||||
private final Context mContext;
|
||||
private final HttpDnsConfig mHttpDnsConfig;
|
||||
private final ConfigCacheHelper mCacheHelper;
|
||||
|
||||
public WriteCacheTask(Context context, HttpDnsConfig config, ConfigCacheHelper helper) {
|
||||
this.mContext = context;
|
||||
this.mHttpDnsConfig = config;
|
||||
this.mCacheHelper = helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mCacheHelper.mSaving.set(false);
|
||||
|
||||
SharedPreferences.Editor editor = mContext.getSharedPreferences(
|
||||
Constants.CONFIG_CACHE_PREFIX + mHttpDnsConfig.getAccountId(),
|
||||
Context.MODE_PRIVATE)
|
||||
.edit();
|
||||
SpCacheItem[] items = mHttpDnsConfig.getCacheItem();
|
||||
for (SpCacheItem item : items) {
|
||||
item.saveToCache(editor);
|
||||
}
|
||||
// 虽然提示建议使用apply,但是实践证明,apply是把写文件操作推迟到了一些界面切换等时机,反而影响了UI线程。不如直接在子线程写文件
|
||||
editor.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.alibaba.sdk.android.httpdns.config;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.utils.CommonUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class RegionServer {
|
||||
/**
|
||||
* HttpDns的服务IP
|
||||
*/
|
||||
private String[] mServerIps;
|
||||
/**
|
||||
* HttpDns的服务端口,线上都是默认端口 80 或者 443
|
||||
* 此处是为了测试场景指定端口
|
||||
* 下标和{@link #mServerIps} 对应
|
||||
* 如果为null 表示没有指定端口
|
||||
*/
|
||||
private int[] mPorts;
|
||||
private String mRegion;
|
||||
|
||||
private String[] mIpv6ServerIps;
|
||||
private int[] mIpv6Ports;
|
||||
|
||||
public RegionServer(String[] serverIps, int[] ports, String[] ipv6ServerIps, int[] ipv6Ports, String region) {
|
||||
this.mServerIps = serverIps == null ? new String[0] : serverIps;
|
||||
this.mPorts = ports;
|
||||
this.mRegion = region;
|
||||
this.mIpv6ServerIps = ipv6ServerIps == null ? new String[0] : ipv6ServerIps;
|
||||
this.mIpv6Ports = ipv6Ports;
|
||||
}
|
||||
|
||||
public String[] getServerIps() {
|
||||
return mServerIps;
|
||||
}
|
||||
|
||||
public int[] getPorts() {
|
||||
return mPorts;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return mRegion;
|
||||
}
|
||||
|
||||
public String[] getIpv6ServerIps() {
|
||||
return mIpv6ServerIps;
|
||||
}
|
||||
|
||||
public int[] getIpv6Ports() {
|
||||
return mIpv6Ports;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RegionServer that = (RegionServer) o;
|
||||
return Arrays.equals(mServerIps, that.mServerIps) &&
|
||||
Arrays.equals(mPorts, that.mPorts) &&
|
||||
Arrays.equals(mIpv6ServerIps, that.mIpv6ServerIps) &&
|
||||
Arrays.equals(mIpv6Ports, that.mIpv6Ports) &&
|
||||
CommonUtil.equals(mRegion, that.mRegion);
|
||||
}
|
||||
|
||||
public boolean serverEquals(RegionServer that) {
|
||||
return Arrays.equals(mServerIps, that.mServerIps) &&
|
||||
Arrays.equals(mPorts, that.mPorts) &&
|
||||
Arrays.equals(mIpv6ServerIps, that.mIpv6ServerIps) &&
|
||||
Arrays.equals(mIpv6Ports, that.mIpv6Ports) &&
|
||||
CommonUtil.equals(mRegion, that.mRegion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Arrays.hashCode(new Object[]{mRegion});
|
||||
result = 31 * result + Arrays.hashCode(mServerIps);
|
||||
result = 31 * result + Arrays.hashCode(mPorts);
|
||||
result = 31 * result + Arrays.hashCode(mIpv6ServerIps);
|
||||
result = 31 * result + Arrays.hashCode(mIpv6Ports);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean updateIpv6(String[] ips, int[] ports) {
|
||||
boolean same = CommonUtil.isSameServer(this.mIpv6ServerIps, this.mIpv6Ports, ips, ports);
|
||||
if (same) {
|
||||
return false;
|
||||
}
|
||||
this.mIpv6ServerIps = ips;
|
||||
this.mIpv6Ports = ports;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean updateRegionAndIpv4(String region, String[] ips, int[] ports) {
|
||||
boolean same = CommonUtil.isSameServer(this.mServerIps, this.mPorts, ips, ports);
|
||||
if (same && region.equals(this.mRegion)) {
|
||||
return false;
|
||||
}
|
||||
this.mRegion = region;
|
||||
this.mServerIps = ips;
|
||||
this.mPorts = ports;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public boolean updateAll(String region, String[] ips, int[] ports, String[] ipv6s, int[] v6ports) {
|
||||
boolean same = CommonUtil.isSameServer(this.mServerIps, this.mPorts, ips, ports);
|
||||
boolean v6same = CommonUtil.isSameServer(this.mIpv6ServerIps, this.mIpv6Ports, ipv6s, v6ports);
|
||||
if (same && v6same && region.equals(this.mRegion)) {
|
||||
return false;
|
||||
}
|
||||
this.mRegion = region;
|
||||
this.mServerIps = ips;
|
||||
this.mPorts = ports;
|
||||
this.mIpv6ServerIps = ipv6s;
|
||||
this.mIpv6Ports = v6ports;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
package com.alibaba.sdk.android.httpdns.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsConfig;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.utils.CommonUtil;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* 服务节点配置
|
||||
* 维护 服务节点的一些状态
|
||||
*/
|
||||
public class ServerConfig extends RegionServer implements SpCacheItem {
|
||||
|
||||
private final HttpDnsConfig mHttpDnsConfig;
|
||||
private int mLastOkServerIndex = 0;
|
||||
private int mCurrentServerIndex = 0;
|
||||
private long mServerIpsLastUpdatedTime = 0;
|
||||
|
||||
private int mLastOkServerIndexForV6 = 0;
|
||||
private int mCurrentServerIndexForV6 = 0;
|
||||
|
||||
public ServerConfig(HttpDnsConfig config, String[] serverIps, int[] ports, String[] ipv6ServerIps, int[] ipv6Ports) {
|
||||
super(serverIps, ports, ipv6ServerIps, ipv6Ports, config.getRegion());
|
||||
mHttpDnsConfig = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前使用的服务IP
|
||||
*/
|
||||
public String getServerIp() {
|
||||
final String[] serverIps = getServerIps();
|
||||
if (serverIps == null || mCurrentServerIndex >= serverIps.length
|
||||
|| mCurrentServerIndex < 0) {
|
||||
return null;
|
||||
}
|
||||
return serverIps[mCurrentServerIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前使用的服务IP ipv6
|
||||
*/
|
||||
public String getServerIpForV6() {
|
||||
final String[] serverIps = getIpv6ServerIps();
|
||||
if (serverIps == null || mCurrentServerIndexForV6 >= serverIps.length
|
||||
|| mCurrentServerIndexForV6 < 0) {
|
||||
return null;
|
||||
}
|
||||
return serverIps[mCurrentServerIndexForV6];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前使用的服务端口
|
||||
*/
|
||||
public int getPort() {
|
||||
final int[] ports = getPorts();
|
||||
if (ports == null || mCurrentServerIndex >= ports.length || mCurrentServerIndex < 0) {
|
||||
return CommonUtil.getPort(-1, mHttpDnsConfig.getSchema());
|
||||
}
|
||||
return CommonUtil.getPort(ports[mCurrentServerIndex], mHttpDnsConfig.getSchema());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前使用的服务端口
|
||||
*/
|
||||
public int getPortForV6() {
|
||||
final int[] ports = getIpv6Ports();
|
||||
if (ports == null || mCurrentServerIndexForV6 >= ports.length
|
||||
|| mCurrentServerIndexForV6 < 0) {
|
||||
return CommonUtil.getPort(-1, mHttpDnsConfig.getSchema());
|
||||
}
|
||||
return CommonUtil.getPort(ports[mCurrentServerIndexForV6], mHttpDnsConfig.getSchema());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否应该更新服务IP
|
||||
*/
|
||||
public boolean shouldUpdateServerIp() {
|
||||
return System.currentTimeMillis() - mServerIpsLastUpdatedTime >= 24 * 60 * 60 * 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置服务IP
|
||||
*
|
||||
* @return false 表示 前后服务一直,没有更新
|
||||
*/
|
||||
public synchronized boolean setServerIps(String region, String[] serverIps, int[] ports,
|
||||
String[] serverV6Ips, int[] v6Ports) {
|
||||
region = CommonUtil.fixRegion(region);
|
||||
if (serverIps == null || serverIps.length == 0) {
|
||||
serverIps = mHttpDnsConfig.getInitServer().getServerIps();
|
||||
ports = mHttpDnsConfig.getInitServer().getPorts();
|
||||
}
|
||||
if (serverV6Ips == null || serverV6Ips.length == 0) {
|
||||
serverV6Ips = mHttpDnsConfig.getInitServer().getIpv6ServerIps();
|
||||
v6Ports = mHttpDnsConfig.getInitServer().getIpv6Ports();
|
||||
}
|
||||
boolean changed = updateRegionAndIpv4(region, serverIps, ports);
|
||||
boolean v6changed = updateIpv6(serverV6Ips, v6Ports);
|
||||
if (changed) {
|
||||
this.mLastOkServerIndex = 0;
|
||||
this.mCurrentServerIndex = 0;
|
||||
}
|
||||
if (v6changed) {
|
||||
this.mLastOkServerIndexForV6 = 0;
|
||||
this.mCurrentServerIndexForV6 = 0;
|
||||
}
|
||||
if (!CommonUtil.isSameServer(serverIps, ports,
|
||||
mHttpDnsConfig.getInitServer().getServerIps(),
|
||||
mHttpDnsConfig.getInitServer().getPorts())
|
||||
|| !CommonUtil.isSameServer(serverV6Ips, v6Ports,
|
||||
mHttpDnsConfig.getInitServer().getIpv6ServerIps(),
|
||||
mHttpDnsConfig.getInitServer().getIpv6Ports())) {
|
||||
// 非初始化IP,才认为是真正的更新了服务IP
|
||||
this.mServerIpsLastUpdatedTime = System.currentTimeMillis();
|
||||
// 非初始IP才有缓存的必要
|
||||
mHttpDnsConfig.saveToCache();
|
||||
}
|
||||
return changed || v6changed;
|
||||
}
|
||||
|
||||
public synchronized void updateServerIpv4sRank(String[] sortedIps, int[] ports) {
|
||||
String[] serverIps = getServerIps();
|
||||
int[] serverPorts = getPorts();
|
||||
String region = getRegion();
|
||||
|
||||
//对比和当前的region server是否是同一批,避免测速完已经被更新
|
||||
if (serverIps.length != sortedIps.length) {
|
||||
//ip数量不一致,数据已经被更新
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("abort rank server ip count changed, current ips: " + Arrays.toString(serverIps)
|
||||
+ ", sorted ips: " + Arrays.toString(sortedIps));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boolean contain;
|
||||
//如果排序的ip都在当前Server ip列表中,认为是一批服务ip,ip和端口需要一起判断
|
||||
for (int i = 0; i != sortedIps.length; ++i) {
|
||||
contain = isContainServiceIp(serverIps, serverPorts, sortedIps[i], ports == null ? -1 : ports[i]);
|
||||
if (!contain) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("abort rank server ip as changed, current ips: " + Arrays.toString(serverIps)
|
||||
+ ", ports: " + Arrays.toString(serverPorts)
|
||||
+ ", sorted ips: " + Arrays.toString(sortedIps)
|
||||
+ ", ports: " + Arrays.toString(ports)
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("update ranked server ips: " + Arrays.toString(sortedIps)
|
||||
+ ", ports: " + Arrays.toString(ports));
|
||||
}
|
||||
//仅更新内存
|
||||
boolean changed = updateRegionAndIpv4(region, sortedIps, ports);
|
||||
if (changed) {
|
||||
this.mLastOkServerIndex = 0;
|
||||
this.mCurrentServerIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void updateServerIpv6sRank(String[] sortedIps, int[] ports) {
|
||||
//和当前ip进行对比,看看是不是已经被更新了,如果被更新了那此次排序结果不使用
|
||||
String[] serverIps = getIpv6ServerIps();
|
||||
int[] serverPorts = getIpv6Ports();
|
||||
|
||||
//对比和当前的region server是否是同一批,避免测速完已经被更新
|
||||
if (serverIps.length != sortedIps.length) {
|
||||
//ip数量不一致,数据已经被更新
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("abort rank server ip count changed, current ipv6s: " + Arrays.toString(serverIps)
|
||||
+ ", sorted ipv6s: " + Arrays.toString(sortedIps));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boolean contain;
|
||||
//如果排序的ip都在当前Server ip列表中,认为是一批服务ip
|
||||
for (int i = 0; i != sortedIps.length; ++i) {
|
||||
contain = isContainServiceIp(serverIps, serverPorts, sortedIps[i], ports == null ? -1 : ports[i]);
|
||||
if (!contain) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("abort rank server ip as changed, current ipv6s: " + Arrays.toString(serverIps)
|
||||
+ ", ports: " + Arrays.toString(serverPorts)
|
||||
+ ", sorted ipv6s: " + Arrays.toString(sortedIps)
|
||||
+ ", ports: " + Arrays.toString(ports)
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("update ranked server ipv6s: " + Arrays.toString(sortedIps)
|
||||
+ ", ports: " + Arrays.toString(ports));
|
||||
}
|
||||
|
||||
//仅更新内存
|
||||
boolean v6changed = updateIpv6(sortedIps, ports);
|
||||
if (v6changed) {
|
||||
mLastOkServerIndexForV6 = 0;
|
||||
mCurrentServerIndexForV6 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isContainServiceIp(String[] sourceIps, int[] sourcePorts, String targetIp, int targetPort) {
|
||||
if (sourceIps == null || sourceIps.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i != sourceIps.length; ++i) {
|
||||
if (TextUtils.equals(sourceIps[i], targetIp)) {
|
||||
if (sourcePorts == null) {
|
||||
return targetPort <= 0;
|
||||
}
|
||||
|
||||
if (i < sourcePorts.length) {
|
||||
if (sourcePorts[i] == targetPort) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (targetPort <= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换域名解析服务
|
||||
*
|
||||
* @param ip 请求失败的服务IP
|
||||
* @param port 请求失败的服务端口
|
||||
* @return 是否切换回了最开始的服务。当请求切换的ip和port不是当前ip和port时,说明这个切换请求是无效的,不切换,返回false 认为没有切换回最开始的ip
|
||||
*/
|
||||
public boolean shiftServer(String ip, int port) {
|
||||
return shiftServerV4(ip, port);
|
||||
}
|
||||
|
||||
private boolean shiftServerV4(String ip, int port) {
|
||||
final String[] serverIps = getServerIps();
|
||||
final int[] ports = getPorts();
|
||||
if (serverIps == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(ip.equals(serverIps[mCurrentServerIndex]) && (ports == null
|
||||
|| ports[mCurrentServerIndex] == port))) {
|
||||
return false;
|
||||
}
|
||||
mCurrentServerIndex++;
|
||||
if (mCurrentServerIndex >= serverIps.length) {
|
||||
mCurrentServerIndex = 0;
|
||||
}
|
||||
return mCurrentServerIndex == mLastOkServerIndex;
|
||||
}
|
||||
|
||||
public boolean shiftServerV6(String ip, int port) {
|
||||
final String[] serverIps = getIpv6ServerIps();
|
||||
final int[] ports = getIpv6Ports();
|
||||
if (serverIps == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(ip.equals(serverIps[mCurrentServerIndexForV6]) && (ports == null
|
||||
|| ports[mCurrentServerIndexForV6] == port))) {
|
||||
return false;
|
||||
}
|
||||
mCurrentServerIndexForV6++;
|
||||
if (mCurrentServerIndexForV6 >= serverIps.length) {
|
||||
mCurrentServerIndexForV6 = 0;
|
||||
}
|
||||
return mCurrentServerIndexForV6 == mLastOkServerIndexForV6;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记当前好用的域名解析服务
|
||||
*
|
||||
* @return 标记成功与否
|
||||
*/
|
||||
public boolean markOkServer(String serverIp, int port) {
|
||||
final String[] serverIps = getServerIps();
|
||||
final int[] ports = getPorts();
|
||||
if (serverIps == null) {
|
||||
return false;
|
||||
}
|
||||
if (serverIps[mCurrentServerIndex].equals(serverIp) && (ports == null
|
||||
|| ports[mCurrentServerIndex] == port)) {
|
||||
if (mLastOkServerIndex != mCurrentServerIndex) {
|
||||
mLastOkServerIndex = mCurrentServerIndex;
|
||||
mHttpDnsConfig.saveToCache();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记当前好用的域名解析服务
|
||||
*
|
||||
* @return 标记成功与否
|
||||
*/
|
||||
public boolean markOkServerV6(String serverIp, int port) {
|
||||
final String[] serverIps = getIpv6ServerIps();
|
||||
final int[] ports = getIpv6Ports();
|
||||
if (serverIps == null) {
|
||||
return false;
|
||||
}
|
||||
if (serverIps[mCurrentServerIndexForV6].equals(serverIp) && (ports == null
|
||||
|| ports[mCurrentServerIndexForV6] == port)) {
|
||||
if (mLastOkServerIndexForV6 != mCurrentServerIndexForV6) {
|
||||
mLastOkServerIndexForV6 = mCurrentServerIndexForV6;
|
||||
mHttpDnsConfig.saveToCache();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {return true;}
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
if (!super.equals(o)) {return false;}
|
||||
ServerConfig that = (ServerConfig)o;
|
||||
return mLastOkServerIndex == that.mLastOkServerIndex &&
|
||||
mCurrentServerIndex == that.mCurrentServerIndex &&
|
||||
mLastOkServerIndexForV6 == that.mLastOkServerIndexForV6 &&
|
||||
mCurrentServerIndexForV6 == that.mCurrentServerIndexForV6 &&
|
||||
mServerIpsLastUpdatedTime == that.mServerIpsLastUpdatedTime &&
|
||||
mHttpDnsConfig.equals(that.mHttpDnsConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(
|
||||
new Object[] {super.hashCode(), mHttpDnsConfig, mLastOkServerIndex,
|
||||
mCurrentServerIndex,
|
||||
mLastOkServerIndexForV6, mCurrentServerIndexForV6, mServerIpsLastUpdatedTime});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFromCache(SharedPreferences sp) {
|
||||
String cachedServerRegion = sp.getString(Constants.CONFIG_CURRENT_SERVER_REGION,
|
||||
getRegion());
|
||||
//初始化region和缓存server region一致的情况,使用缓存的服务IP。否则初始化的region优先级更高
|
||||
if (CommonUtil.regionEquals(cachedServerRegion, getRegion())) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("restore service ip of " + (TextUtils.isEmpty(cachedServerRegion) ? "default" : cachedServerRegion));
|
||||
}
|
||||
String[] serverIps = CommonUtil.parseStringArray(sp.getString(Constants.CONFIG_KEY_SERVERS,
|
||||
CommonUtil.translateStringArray(getServerIps())));
|
||||
int[] ports = CommonUtil.parsePorts(
|
||||
sp.getString(Constants.CONFIG_KEY_PORTS, CommonUtil.translateIntArray(getPorts())));
|
||||
String[] serverV6Ips = CommonUtil.parseStringArray(
|
||||
sp.getString(Constants.CONFIG_KEY_SERVERS_IPV6,
|
||||
CommonUtil.translateStringArray(getIpv6ServerIps())));
|
||||
int[] v6Ports = CommonUtil.parsePorts(sp.getString(Constants.CONFIG_KEY_PORTS_IPV6,
|
||||
CommonUtil.translateIntArray(getIpv6Ports())));
|
||||
|
||||
updateAll(cachedServerRegion, serverIps, ports, serverV6Ips, v6Ports);
|
||||
mServerIpsLastUpdatedTime = sp.getLong(Constants.CONFIG_SERVERS_LAST_UPDATED_TIME, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToCache(SharedPreferences.Editor editor) {
|
||||
editor.putString(Constants.CONFIG_KEY_SERVERS,
|
||||
CommonUtil.translateStringArray(getServerIps()));
|
||||
editor.putString(Constants.CONFIG_KEY_PORTS, CommonUtil.translateIntArray(getPorts()));
|
||||
editor.putInt(Constants.CONFIG_CURRENT_INDEX, mCurrentServerIndex);
|
||||
editor.putInt(Constants.CONFIG_LAST_INDEX, mLastOkServerIndex);
|
||||
editor.putString(Constants.CONFIG_KEY_SERVERS_IPV6,
|
||||
CommonUtil.translateStringArray(getIpv6ServerIps()));
|
||||
editor.putString(Constants.CONFIG_KEY_PORTS_IPV6,
|
||||
CommonUtil.translateIntArray(getIpv6Ports()));
|
||||
editor.putInt(Constants.CONFIG_CURRENT_INDEX_IPV6, mCurrentServerIndexForV6);
|
||||
editor.putInt(Constants.CONFIG_LAST_INDEX_IPV6, mLastOkServerIndexForV6);
|
||||
editor.putLong(Constants.CONFIG_SERVERS_LAST_UPDATED_TIME, mServerIpsLastUpdatedTime);
|
||||
editor.putString(Constants.CONFIG_CURRENT_SERVER_REGION, getRegion());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.alibaba.sdk.android.httpdns.config;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public interface SpCacheItem {
|
||||
void restoreFromCache(SharedPreferences sp);
|
||||
|
||||
void saveToCache(SharedPreferences.Editor editor);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.alibaba.sdk.android.httpdns.config.region;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.config.RegionServer;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
public class AmericaRegionServer {
|
||||
private static final String [] SERVER_IPS = new String [] {
|
||||
"47.246.131.175",
|
||||
"47.246.131.141"
|
||||
};
|
||||
private static final int[] PORTS = null;
|
||||
private static final String[] IPV6_SERVER_IPS = new String [] {
|
||||
"2404:2280:4000::2bb",
|
||||
"2404:2280:4000::23e"
|
||||
};
|
||||
private static final int[] IPV6_PORTS = null;
|
||||
|
||||
private static final String[] UPDATE_SERVER = new String[] {
|
||||
"resolvers-us.httpdns.aliyuncs.com"
|
||||
};
|
||||
private static final String[] IPV6_UPDATE_SERVER = new String[] {
|
||||
"resolvers-us.httpdns.aliyuncs.com"
|
||||
};
|
||||
|
||||
public static RegionServer getInitServer() {
|
||||
return new RegionServer(SERVER_IPS, PORTS, IPV6_SERVER_IPS, IPV6_PORTS, Constants.REGION_US);
|
||||
}
|
||||
|
||||
public static RegionServer getUpdateServer() {
|
||||
return new RegionServer(UPDATE_SERVER, Constants.NO_PORTS, IPV6_UPDATE_SERVER, Constants.NO_PORTS, Constants.REGION_US);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.alibaba.sdk.android.httpdns.config.region;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.config.RegionServer;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
public final class DefaultRegionServer {
|
||||
private static final String [] SERVER_IPS = new String [] {
|
||||
"203.107.1.1",
|
||||
"203.107.1.97",
|
||||
"203.107.1.100",
|
||||
"203.119.238.240",
|
||||
"106.11.25.239",
|
||||
"59.82.99.47"
|
||||
};
|
||||
private static final int[] PORTS = null;
|
||||
private static final String[] IPV6_SERVER_IPS = new String [] {
|
||||
"2401:b180:7001::31d",
|
||||
"2408:4003:1f40::30a",
|
||||
"2401:b180:2000:20::10",
|
||||
"2401:b180:2000:30::1c"
|
||||
};
|
||||
private static final int[] IPV6_PORTS = null;
|
||||
|
||||
private static final String[] UPDATE_SERVER = new String[] {
|
||||
"resolvers-cn.httpdns.aliyuncs.com"
|
||||
};
|
||||
private static final String[] IPV6_UPDATE_SERVER = new String[] {
|
||||
"resolvers-cn.httpdns.aliyuncs.com"
|
||||
};
|
||||
|
||||
public static RegionServer getInitServer() {
|
||||
return new RegionServer(SERVER_IPS, PORTS, IPV6_SERVER_IPS, IPV6_PORTS, Constants.REGION_DEFAULT);
|
||||
}
|
||||
|
||||
public static RegionServer getUpdateServer() {
|
||||
return new RegionServer(UPDATE_SERVER, Constants.NO_PORTS, IPV6_UPDATE_SERVER, Constants.NO_PORTS, Constants.REGION_DEFAULT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.alibaba.sdk.android.httpdns.config.region;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.config.RegionServer;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
public class GermanyRegionServer {
|
||||
private static final String [] SERVER_IPS = new String [] {
|
||||
"47.89.80.182",
|
||||
"47.246.146.77"
|
||||
};
|
||||
private static final int[] PORTS = null;
|
||||
|
||||
private static final String[] IPV6_SERVER_IPS = new String [] {
|
||||
"2404:2280:3000::176",
|
||||
"2404:2280:3000::188"
|
||||
};
|
||||
private static final int[] IPV6_PORTS = null;
|
||||
|
||||
private static final String[] UPDATE_SERVER = new String[] {
|
||||
"resolvers-de.httpdns.aliyuncs.com"
|
||||
};
|
||||
private static final String[] IPV6_UPDATE_SERVER = new String[] {
|
||||
"resolvers-de.httpdns.aliyuncs.com"
|
||||
};
|
||||
|
||||
public static RegionServer getInitServer() {
|
||||
return new RegionServer(SERVER_IPS, PORTS, IPV6_SERVER_IPS, IPV6_PORTS, Constants.REGION_DE);
|
||||
}
|
||||
|
||||
public static RegionServer getUpdateServer() {
|
||||
return new RegionServer(UPDATE_SERVER, Constants.NO_PORTS, IPV6_UPDATE_SERVER, Constants.NO_PORTS, Constants.REGION_DE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.alibaba.sdk.android.httpdns.config.region;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.config.RegionServer;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
public class HongKongRegionServer {
|
||||
private static final String[] SERVER_IPS = new String[] {
|
||||
"47.56.234.194",
|
||||
"47.56.119.115"
|
||||
};
|
||||
private static final int[] PORTS = null;
|
||||
private static final String[] IPV6_SERVER_IPS = new String[] {
|
||||
"240b:4000:f10::178",
|
||||
"240b:4000:f10::188"
|
||||
};
|
||||
private static final int[] IPV6_PORTS = null;
|
||||
|
||||
private static final String[] UPDATE_SERVER = new String[] {
|
||||
"resolvers-hk.httpdns.aliyuncs.com"
|
||||
};
|
||||
private static final String[] IPV6_UPDATE_SERVER = new String[] {
|
||||
"resolvers-hk.httpdns.aliyuncs.com"
|
||||
};
|
||||
|
||||
public static RegionServer getInitServer() {
|
||||
return new RegionServer(SERVER_IPS, PORTS, IPV6_SERVER_IPS, IPV6_PORTS, Constants.REGION_HK);
|
||||
}
|
||||
|
||||
public static RegionServer getUpdateServer() {
|
||||
return new RegionServer(UPDATE_SERVER, Constants.NO_PORTS, IPV6_UPDATE_SERVER, Constants.NO_PORTS, Constants.REGION_HK);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.alibaba.sdk.android.httpdns.config.region;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.config.RegionServer;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
public class PreReleaseRegionServer {
|
||||
private static final String [] SERVER_IPS = new String [] {
|
||||
"106.11.190.18"
|
||||
};
|
||||
private static final int[] PORTS = null;
|
||||
private static final String[] IPV6_SERVER_IPS = new String [] {
|
||||
"resolvers-cn-pre.httpdns.aliyuncs.com"
|
||||
};
|
||||
private static final int[] IPV6_PORTS = null;
|
||||
|
||||
private static final String[] UPDATE_SERVER = new String[] {
|
||||
"resolvers-cn-pre.httpdns.aliyuncs.com"
|
||||
};
|
||||
private static final String[] IPV6_UPDATE_SERVER = new String[] {
|
||||
"resolvers-cn-pre.httpdns.aliyuncs.com"
|
||||
};
|
||||
|
||||
public static RegionServer getInitServer() {
|
||||
return new RegionServer(SERVER_IPS, PORTS, IPV6_SERVER_IPS, IPV6_PORTS, Constants.REGION_DEBUG_PRE);
|
||||
}
|
||||
|
||||
public static RegionServer getUpdateServer() {
|
||||
return new RegionServer(UPDATE_SERVER, Constants.NO_PORTS, IPV6_UPDATE_SERVER, Constants.NO_PORTS, Constants.REGION_DEBUG_PRE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.alibaba.sdk.android.httpdns.config.region;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.config.RegionServer;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
public class RegionServerManager {
|
||||
public static RegionServer getInitServer(String region) {
|
||||
RegionServer regionServer = null;
|
||||
switch (region) {
|
||||
case Constants.REGION_HK:
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("use hk region");
|
||||
}
|
||||
regionServer = HongKongRegionServer.getInitServer();
|
||||
break;
|
||||
case Constants.REGION_SG:
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("use sg region");
|
||||
}
|
||||
regionServer = SingaporeRegionServer.getInitServer();
|
||||
break;
|
||||
case Constants.REGION_DE:
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("use de region");
|
||||
}
|
||||
regionServer = GermanyRegionServer.getInitServer();
|
||||
break;
|
||||
case Constants.REGION_US:
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("use us region");
|
||||
}
|
||||
regionServer = AmericaRegionServer.getInitServer();
|
||||
break;
|
||||
case Constants.REGION_DEBUG_PRE:
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("use pre region");
|
||||
}
|
||||
regionServer = PreReleaseRegionServer.getInitServer();
|
||||
break;
|
||||
default:
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("use default region");
|
||||
}
|
||||
regionServer = DefaultRegionServer.getInitServer();
|
||||
break;
|
||||
}
|
||||
|
||||
return regionServer;
|
||||
}
|
||||
|
||||
public static RegionServer getUpdateServer(String region) {
|
||||
RegionServer regionServer = null;
|
||||
switch (region) {
|
||||
case Constants.REGION_HK:
|
||||
regionServer = HongKongRegionServer.getUpdateServer();
|
||||
break;
|
||||
case Constants.REGION_SG:
|
||||
regionServer = SingaporeRegionServer.getUpdateServer();
|
||||
break;
|
||||
case Constants.REGION_DE:
|
||||
regionServer = GermanyRegionServer.getUpdateServer();
|
||||
break;
|
||||
case Constants.REGION_US:
|
||||
regionServer = AmericaRegionServer.getUpdateServer();
|
||||
break;
|
||||
case Constants.REGION_DEBUG_PRE:
|
||||
regionServer = PreReleaseRegionServer.getUpdateServer();
|
||||
break;
|
||||
default:
|
||||
regionServer = DefaultRegionServer.getUpdateServer();
|
||||
}
|
||||
|
||||
return regionServer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.alibaba.sdk.android.httpdns.config.region;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.config.RegionServer;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
public class SingaporeRegionServer {
|
||||
private static final String[] SERVER_IPS = new String[] {
|
||||
"161.117.200.122",
|
||||
"47.74.222.190"
|
||||
};
|
||||
private static final int[] PORTS = null;
|
||||
private static final String[] IPV6_SERVER_IPS = new String[] {
|
||||
"240b:4000:f10::178",
|
||||
"240b:4000:f10::188"
|
||||
};
|
||||
private static final int[] IPV6_PORTS = null;
|
||||
|
||||
private static final String[] UPDATE_SERVER = new String[] {
|
||||
"resolvers-sg.httpdns.aliyuncs.com"
|
||||
};
|
||||
private static final String[] IPV6_UPDATE_SERVER = new String[] {
|
||||
"resolvers-sg.httpdns.aliyuncs.com"
|
||||
};
|
||||
|
||||
public static RegionServer getInitServer() {
|
||||
return new RegionServer(SERVER_IPS, PORTS, IPV6_SERVER_IPS, IPV6_PORTS, Constants.REGION_SG);
|
||||
}
|
||||
|
||||
public static RegionServer getUpdateServer() {
|
||||
return new RegionServer(UPDATE_SERVER, Constants.NO_PORTS, IPV6_UPDATE_SERVER, Constants.NO_PORTS, Constants.REGION_SG);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.alibaba.sdk.android.httpdns.exception;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
|
||||
public class HttpDnsUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
// 处理所有未捕获异常
|
||||
public void uncaughtException(Thread thread, Throwable ex) {
|
||||
try {
|
||||
HttpDnsLog.e("Catch an uncaught exception, " + thread.getName() + ", error message: "
|
||||
+ ex.getMessage(), ex);
|
||||
reportUncaughtError(ex);
|
||||
ex.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void reportUncaughtError(Throwable ex) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.alibaba.sdk.android.httpdns.exception;
|
||||
|
||||
public class InitException extends RuntimeException {
|
||||
|
||||
public InitException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.alibaba.sdk.android.httpdns.impl;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.utils.CommonUtil;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.GCMParameterSpec;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* AES 加密解密工具类
|
||||
* @author renwei
|
||||
* @date 2025/3/26
|
||||
*/
|
||||
public class AESEncryptService {
|
||||
private static final int GCM_IV_LENGTH = 12;
|
||||
private static final int CBC_IV_LENGTH = 16;
|
||||
private static final int GCM_TAG_LENGTH = 128;
|
||||
|
||||
private String aesSecretKey;
|
||||
|
||||
/**
|
||||
* 加密方法
|
||||
*/
|
||||
public String encrypt(String data, EncryptionMode mode) {
|
||||
if (EncryptionMode.PLAIN == mode) {
|
||||
return "";
|
||||
}
|
||||
if (TextUtils.isEmpty(aesSecretKey)) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("aesSecretKey为空");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
if (TextUtils.isEmpty(data)) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("待加密数据为空");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 生成IV
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte[] iv = new byte[mode == EncryptionMode.AES_GCM ? GCM_IV_LENGTH : CBC_IV_LENGTH];
|
||||
random.nextBytes(iv);
|
||||
|
||||
String encryptStr = "";
|
||||
try {
|
||||
byte[] encrypted = mode == EncryptionMode.AES_GCM ? aesGcmEncrypt(data, iv) : aesCbcEncrypt(data, iv);
|
||||
// 组合IV和密文
|
||||
byte[] combined = new byte[iv.length + encrypted.length];
|
||||
System.arraycopy(iv, 0, combined, 0, iv.length);
|
||||
System.arraycopy(encrypted, 0, combined, iv.length, encrypted.length);
|
||||
encryptStr = CommonUtil.encodeHexString(combined);
|
||||
} catch (Exception e) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.e("加密失败, 加密内容:" + data);
|
||||
}
|
||||
}
|
||||
return encryptStr;
|
||||
}
|
||||
|
||||
private byte[] aesGcmEncrypt(String plainText, byte[] iv) throws Exception {
|
||||
byte[] key = CommonUtil.decodeHex(aesSecretKey);
|
||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
||||
SecretKey secretKey = new SecretKeySpec(key, "AES");
|
||||
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv); // 认证标签长度为 128 位
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, spec);
|
||||
return cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private byte[] aesCbcEncrypt(String plainText, byte[] iv) throws Exception {
|
||||
byte[] key = CommonUtil.decodeHex(aesSecretKey);
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //
|
||||
SecretKey secretKey = new SecretKeySpec(key, "AES");
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(iv); // IV 必须为 16 字节
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
|
||||
return cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密方法
|
||||
*/
|
||||
public String decrypt(String encryptData, EncryptionMode mode) {
|
||||
if (EncryptionMode.PLAIN == mode) {
|
||||
return encryptData;
|
||||
}
|
||||
if (TextUtils.isEmpty(aesSecretKey)) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("aesSecretKey为空");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
if (TextUtils.isEmpty(encryptData)) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("待解密数据为空");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
String plainData = "";
|
||||
try {
|
||||
byte[] binaryencrypted = CommonUtil.decodeBase64(encryptData);
|
||||
plainData = EncryptionMode.AES_GCM == mode ? aesGcmDecrypt(binaryencrypted): aesCbcDecrypt(binaryencrypted);
|
||||
} catch (Exception e) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.e("解密失败, 待解密数据: " + encryptData);
|
||||
}
|
||||
}
|
||||
return plainData;
|
||||
}
|
||||
|
||||
private String aesCbcDecrypt(byte[] binaryencrypted) throws Exception {
|
||||
byte[] key = CommonUtil.decodeHex(aesSecretKey);
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(Arrays.copyOfRange(binaryencrypted, 0, 16));
|
||||
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
|
||||
byte[] decrypted = cipher.doFinal(Arrays.copyOfRange(binaryencrypted, 16, binaryencrypted.length));
|
||||
return new String(decrypted, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private String aesGcmDecrypt(byte[] binaryencrypted) throws Exception {
|
||||
byte[] key = CommonUtil.decodeHex(aesSecretKey);
|
||||
SecretKey secretKey = new SecretKeySpec(key, "AES");
|
||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
||||
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, Arrays.copyOfRange(binaryencrypted, 0, 12)); // 认证标签长度为 128 位
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
|
||||
byte[] decrypted = cipher.doFinal(Arrays.copyOfRange(binaryencrypted, 12, binaryencrypted.length));
|
||||
return new String(decrypted, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public Boolean isEncryptionMode(){
|
||||
return !TextUtils.isEmpty(aesSecretKey);
|
||||
}
|
||||
|
||||
public void setAesSecretKey(String aesSecretKey) {
|
||||
this.aesSecretKey = aesSecretKey;
|
||||
}
|
||||
|
||||
public enum EncryptionMode {
|
||||
PLAIN("0"), AES_CBC("1"), AES_GCM("2");
|
||||
|
||||
private final String mode;
|
||||
|
||||
EncryptionMode(String mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public String getMode(){
|
||||
return mode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package com.alibaba.sdk.android.httpdns.impl;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HTTPDNSResult;
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsCallback;
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsService;
|
||||
import com.alibaba.sdk.android.httpdns.Region;
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.SyncService;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ErrorImpl implements HttpDnsService, SyncService {
|
||||
|
||||
@Override
|
||||
public void setPreResolveHosts(List<String> hostList) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreResolveHosts(List<String> hostList, RequestIpType requestIpType) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIpByHostAsync(String host) {
|
||||
HttpDnsLog.w("init error");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIPv4ForHostAsync(String host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getIpsByHostAsync(String host) {
|
||||
HttpDnsLog.w("init error");
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getIPv4ListForHostAsync(String host) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getIPv6sByHostAsync(String host) {
|
||||
HttpDnsLog.w("init error");
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getIPv6ListForHostASync(String host) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getAllByHostAsync(String host) {
|
||||
HttpDnsLog.w("init error");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostAsync(String host) {
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getIpsByHostAsync(String host, RequestIpType type) {
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostAsync(String host, RequestIpType type) {
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthCurrentTime(long time) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSessionId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getIpsByHostAsync(String host, Map<String, String> params,
|
||||
String cacheKey) {
|
||||
HttpDnsLog.w("init error");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostAsync(String host, Map<String, String> params,
|
||||
String cacheKey) {
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getIpsByHostAsync(String host, RequestIpType type,
|
||||
Map<String, String> params, String cacheKey) {
|
||||
HttpDnsLog.w("init error");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostAsync(String host, RequestIpType type,
|
||||
Map<String, String> params, String cacheKey) {
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostSync(String host, RequestIpType type, Map<String, String> params, String cacheKey) {
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getHttpDnsResultForHostAsync(String host, RequestIpType type, Map<String, String> params, String cacheKey, HttpDnsCallback callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostSyncNonBlocking(String host, RequestIpType type, Map<String, String> params, String cacheKey) {
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegion(String region) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegion(Region region) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIPv6ByHostAsync(String host) {
|
||||
HttpDnsLog.w("init error");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIPv6ForHostAsync(String host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getByHost(String host, RequestIpType type) {
|
||||
HttpDnsLog.w("init error");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanHostCache(ArrayList<String> hosts) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostSync(String host, RequestIpType type) {
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getHttpDnsResultForHostAsync(String host, RequestIpType type, HttpDnsCallback callback) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostSyncNonBlocking(String host, RequestIpType type) {
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package com.alibaba.sdk.android.httpdns.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
|
||||
public class HostResolveLocker {
|
||||
|
||||
private static class Recorder {
|
||||
private final HashSet<String> mV4ResolvingHost = new HashSet<>();
|
||||
private final HashSet<String> mV6ResolvingHost = new HashSet<>();
|
||||
private final HashSet<String> mBothResolvingHost = new HashSet<>();
|
||||
private final HashMap<String, CountDownLatch> mV4Latchs = new HashMap<>();
|
||||
private final HashMap<String, CountDownLatch> mV6Latchs = new HashMap<>();
|
||||
private final HashMap<String, CountDownLatch> mBothLatchs = new HashMap<>();
|
||||
private final Object mLock = new Object();
|
||||
|
||||
public boolean beginResolve(String host, RequestIpType type) {
|
||||
|
||||
if (type == RequestIpType.both) {
|
||||
if (mBothResolvingHost.contains(host)) {
|
||||
// 正在解析
|
||||
return false;
|
||||
} else {
|
||||
synchronized (mLock) {
|
||||
if (mBothResolvingHost.contains(host)) {
|
||||
// 正在解析
|
||||
return false;
|
||||
} else {
|
||||
mBothResolvingHost.add(host);
|
||||
createLatch(host, mBothLatchs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (type == RequestIpType.v4) {
|
||||
if (mV4ResolvingHost.contains(host)) {
|
||||
return false;
|
||||
} else {
|
||||
synchronized (mLock) {
|
||||
if (mV4ResolvingHost.contains(host)) {
|
||||
return false;
|
||||
} else {
|
||||
mV4ResolvingHost.add(host);
|
||||
createLatch(host, mV4Latchs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (type == RequestIpType.v6) {
|
||||
if (mV6ResolvingHost.contains(host)) {
|
||||
return false;
|
||||
} else {
|
||||
synchronized (mLock) {
|
||||
if (mV6ResolvingHost.contains(host)) {
|
||||
return false;
|
||||
} else {
|
||||
mV6ResolvingHost.add(host);
|
||||
createLatch(host, mV6Latchs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void createLatch(String host, HashMap<String, CountDownLatch> latchs) {
|
||||
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
latchs.put(host, countDownLatch);
|
||||
}
|
||||
|
||||
private void countDownLatch(String host, HashMap<String, CountDownLatch> latchs) {
|
||||
CountDownLatch latch = latchs.get(host);
|
||||
if (latch != null) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
private CountDownLatch getLatch(String host, RequestIpType type) {
|
||||
switch (type) {
|
||||
case v4:
|
||||
return mV4Latchs.get(host);
|
||||
case v6:
|
||||
return mV6Latchs.get(host);
|
||||
case both:
|
||||
return mBothLatchs.get(host);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void endResolve(String host, RequestIpType type) {
|
||||
switch (type) {
|
||||
case v4:
|
||||
mV4ResolvingHost.remove(host);
|
||||
countDownLatch(host, mV4Latchs);
|
||||
break;
|
||||
case v6:
|
||||
mV6ResolvingHost.remove(host);
|
||||
countDownLatch(host, mV6Latchs);
|
||||
break;
|
||||
case both:
|
||||
mBothResolvingHost.remove(host);
|
||||
countDownLatch(host, mBothLatchs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean await(String host, RequestIpType type, long timeout, TimeUnit unit)
|
||||
throws InterruptedException {
|
||||
CountDownLatch countDownLatch = getLatch(host, type);
|
||||
if (countDownLatch != null) {
|
||||
return countDownLatch.await(timeout, unit);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final Recorder mDefaultRecorder = new Recorder();
|
||||
private final HashMap<String, Recorder> mRecorders = new HashMap<>();
|
||||
|
||||
public boolean beginResolve(String host, RequestIpType type, String cacheKey) {
|
||||
Recorder recorder = getRecorder(cacheKey);
|
||||
return recorder.beginResolve(host, type);
|
||||
}
|
||||
|
||||
private Recorder getRecorder(String cacheKey) {
|
||||
Recorder recorder;
|
||||
if (cacheKey == null || cacheKey.isEmpty()) {
|
||||
recorder = mDefaultRecorder;
|
||||
} else {
|
||||
recorder = mRecorders.get(cacheKey);
|
||||
if (recorder == null) {
|
||||
synchronized (mLock) {
|
||||
recorder = mRecorders.get(cacheKey);
|
||||
if (recorder == null) {
|
||||
recorder = new Recorder();
|
||||
mRecorders.put(cacheKey, recorder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return recorder;
|
||||
}
|
||||
|
||||
public void endResolve(String host, RequestIpType type, String cacheKey) {
|
||||
Recorder recorder = getRecorder(cacheKey);
|
||||
recorder.endResolve(host, type);
|
||||
}
|
||||
|
||||
public boolean await(String host, RequestIpType type, String cacheKey, long timeout,
|
||||
TimeUnit unit) throws InterruptedException {
|
||||
Recorder recorder = getRecorder(cacheKey);
|
||||
return recorder.await(host, type, timeout, unit);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.alibaba.sdk.android.httpdns.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
|
||||
public class HostResolveRecorder {
|
||||
private static class Recorder {
|
||||
private final HashSet<String> mV4ResolvingHost = new HashSet<>();
|
||||
private final HashSet<String> mV6ResolvingHost = new HashSet<>();
|
||||
private final HashSet<String> mBothResolvingHost = new HashSet<>();
|
||||
private final Object mLock = new Object();
|
||||
|
||||
public boolean beginResolve(String host, RequestIpType type) {
|
||||
|
||||
if (type == RequestIpType.both) {
|
||||
if (mBothResolvingHost.contains(host) || (mV4ResolvingHost.contains(host)
|
||||
&& mV6ResolvingHost.contains(host))) {
|
||||
// 正在解析
|
||||
return false;
|
||||
} else {
|
||||
synchronized (mLock) {
|
||||
if (mBothResolvingHost.contains(host) || (mV4ResolvingHost.contains(host)
|
||||
&& mV6ResolvingHost.contains(host))) {
|
||||
// 正在解析
|
||||
return false;
|
||||
} else {
|
||||
mBothResolvingHost.add(host);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (type == RequestIpType.v4) {
|
||||
if (mV4ResolvingHost.contains(host) || mBothResolvingHost.contains(host)) {
|
||||
return false;
|
||||
} else {
|
||||
synchronized (mLock) {
|
||||
if (mV4ResolvingHost.contains(host) || mBothResolvingHost.contains(host)) {
|
||||
return false;
|
||||
} else {
|
||||
mV4ResolvingHost.add(host);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (type == RequestIpType.v6) {
|
||||
if (mV6ResolvingHost.contains(host) || mBothResolvingHost.contains(host)) {
|
||||
return false;
|
||||
} else {
|
||||
synchronized (mLock) {
|
||||
if (mV6ResolvingHost.contains(host) || mBothResolvingHost.contains(host)) {
|
||||
return false;
|
||||
} else {
|
||||
mV6ResolvingHost.add(host);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void endResolve(String host, RequestIpType type) {
|
||||
switch (type) {
|
||||
case v4:
|
||||
mV4ResolvingHost.remove(host);
|
||||
break;
|
||||
case v6:
|
||||
mV6ResolvingHost.remove(host);
|
||||
break;
|
||||
case both:
|
||||
mBothResolvingHost.remove(host);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final Recorder mDefaultRecorder = new Recorder();
|
||||
private final HashMap<String, Recorder> mRecorderHashMap = new HashMap<>();
|
||||
|
||||
public boolean beginResolve(String host, RequestIpType type) {
|
||||
return beginResolve(host, type, null);
|
||||
}
|
||||
|
||||
public void endResolve(String host, RequestIpType type) {
|
||||
endResolve(host, type, null);
|
||||
}
|
||||
|
||||
public boolean beginResolve(String host, RequestIpType type, String cacheKey) {
|
||||
Recorder recorder = getRecorder(cacheKey);
|
||||
return recorder.beginResolve(host, type);
|
||||
}
|
||||
|
||||
private Recorder getRecorder(String cacheKey) {
|
||||
Recorder recorder = null;
|
||||
if (cacheKey == null || cacheKey.isEmpty()) {
|
||||
recorder = mDefaultRecorder;
|
||||
} else {
|
||||
recorder = mRecorderHashMap.get(cacheKey);
|
||||
if (recorder == null) {
|
||||
synchronized (mLock) {
|
||||
recorder = mRecorderHashMap.get(cacheKey);
|
||||
if (recorder == null) {
|
||||
recorder = new Recorder();
|
||||
mRecorderHashMap.put(cacheKey, recorder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return recorder;
|
||||
}
|
||||
|
||||
public void endResolve(String host, RequestIpType type, String cacheKey) {
|
||||
Recorder recorder = getRecorder(cacheKey);
|
||||
recorder.endResolve(host, type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
package com.alibaba.sdk.android.httpdns.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.BuildConfig;
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsSettings;
|
||||
import com.alibaba.sdk.android.httpdns.InitConfig;
|
||||
import com.alibaba.sdk.android.httpdns.config.ConfigCacheHelper;
|
||||
import com.alibaba.sdk.android.httpdns.config.RegionServer;
|
||||
import com.alibaba.sdk.android.httpdns.config.ServerConfig;
|
||||
import com.alibaba.sdk.android.httpdns.config.region.RegionServerManager;
|
||||
import com.alibaba.sdk.android.httpdns.config.SpCacheItem;
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableConfig;
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableConstants;
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableManager;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequestConfig;
|
||||
import com.alibaba.sdk.android.httpdns.utils.CommonUtil;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
import com.alibaba.sdk.android.httpdns.utils.ThreadUtil;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
|
||||
/**
|
||||
* httpdns的配置
|
||||
*/
|
||||
public class HttpDnsConfig implements SpCacheItem {
|
||||
private final Context mContext;
|
||||
private boolean mEnabled = Constants.DEFAULT_SDK_ENABLE;
|
||||
/**
|
||||
* 初始服务节点
|
||||
*/
|
||||
private RegionServer mInitServer;
|
||||
|
||||
/**
|
||||
* 兜底的调度服务IP,用于应对国际版服务IP有可能不稳定的情况
|
||||
*/
|
||||
private RegionServer mDefaultUpdateServer;
|
||||
|
||||
/**
|
||||
* 当前服务节点
|
||||
*/
|
||||
private final ServerConfig mCurrentServer;
|
||||
|
||||
/**
|
||||
* 用户的accountId
|
||||
*/
|
||||
private final String mAccountId;
|
||||
/**
|
||||
* 当前请求使用的schema
|
||||
*/
|
||||
private String mSchema = Constants.DEFAULT_SCHEMA;
|
||||
/**
|
||||
* 当前region
|
||||
*/
|
||||
private String mRegion;
|
||||
/**
|
||||
* 超时时长
|
||||
*/
|
||||
private int mTimeout = Constants.DEFAULT_TIMEOUT;
|
||||
|
||||
/**
|
||||
* 是否禁用服务,以避免崩溃
|
||||
*/
|
||||
private boolean mHitCrashDefend;
|
||||
/**
|
||||
* 是否远程禁用服务
|
||||
*/
|
||||
private boolean mRemoteDisabled = false;
|
||||
/**
|
||||
* 是否禁用probe能力
|
||||
*/
|
||||
private boolean mIPRankingDisabled = false;
|
||||
/**
|
||||
* 是否开启降级到Local Dns
|
||||
*/
|
||||
private boolean mEnableDegradationLocalDns = Constants.DEFAULT_ENABLE_DEGRADATION_LOCAL_DNS;
|
||||
|
||||
/**
|
||||
* 网络探测接口
|
||||
*/
|
||||
private HttpDnsSettings.NetworkDetector mNetworkDetector = null;
|
||||
|
||||
private final ConfigCacheHelper mCacheHelper;
|
||||
|
||||
protected ExecutorService mWorker = ThreadUtil.createExecutorService();
|
||||
protected ExecutorService mResolveWorker = ThreadUtil.createResolveExecutorService();
|
||||
protected ExecutorService mDbWorker = ThreadUtil.createDBExecutorService();
|
||||
private ObservableConfig mObservableConfig;
|
||||
private ObservableManager mObservableManager;
|
||||
private float mObservableSampleBenchMarks;
|
||||
private String mBizTags;
|
||||
private String mUA;
|
||||
|
||||
public HttpDnsConfig(Context context, String accountId, String secret) {
|
||||
mContext = context;
|
||||
mAccountId = accountId;
|
||||
if (mContext != null) {
|
||||
mUA = buildUA();
|
||||
}
|
||||
|
||||
//region提前设置
|
||||
mRegion = getInitRegion(accountId);
|
||||
mInitServer = RegionServerManager.getInitServer(mRegion);
|
||||
mDefaultUpdateServer = RegionServerManager.getUpdateServer(mRegion);
|
||||
|
||||
mCurrentServer = new ServerConfig(this, mInitServer.getServerIps(), mInitServer.getPorts(), mInitServer.getIpv6ServerIps(), mInitServer.getIpv6Ports());
|
||||
mObservableConfig = new ObservableConfig();
|
||||
// 先从缓存读取数据,再赋值cacheHelper, 避免在读取缓存过程中,触发写缓存操作
|
||||
ConfigCacheHelper helper = new ConfigCacheHelper();
|
||||
if (context != null) {
|
||||
helper.restoreFromCache(context, this);
|
||||
}
|
||||
mCacheHelper = helper;
|
||||
|
||||
mObservableManager = new ObservableManager(this, secret);
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public String getAccountId() {
|
||||
return mAccountId;
|
||||
}
|
||||
|
||||
public ServerConfig getCurrentServer() {
|
||||
return mCurrentServer;
|
||||
}
|
||||
|
||||
public ObservableConfig getObservableConfig() {
|
||||
return mObservableConfig;
|
||||
}
|
||||
|
||||
public ObservableManager getObservableManager() {
|
||||
return mObservableManager;
|
||||
}
|
||||
|
||||
public float getObservableSampleBenchMarks() {
|
||||
return mObservableSampleBenchMarks;
|
||||
}
|
||||
|
||||
public void setObservableSampleBenchMarks(float benchMarks) {
|
||||
if (mObservableSampleBenchMarks != benchMarks) {
|
||||
mObservableSampleBenchMarks = benchMarks;
|
||||
saveToCache();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCurrentRegionMatch() {
|
||||
return CommonUtil.regionEquals(mRegion, mCurrentServer.getRegion());
|
||||
}
|
||||
|
||||
public boolean isAllInitServer() {
|
||||
return mInitServer.serverEquals((RegionServer)mCurrentServer);
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return mRegion;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return mEnabled && !mHitCrashDefend && !mRemoteDisabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用httpdns
|
||||
* <p>
|
||||
* 注意是 永久禁用,因为缓存的原因,一旦禁用,就没有机会启用了
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
if (this.mEnabled != enabled) {
|
||||
this.mEnabled = enabled;
|
||||
saveToCache();
|
||||
}
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
return mTimeout;
|
||||
}
|
||||
|
||||
public void setTimeout(int timeout) {
|
||||
if (this.mTimeout != timeout) {
|
||||
this.mTimeout = timeout;
|
||||
saveToCache();
|
||||
}
|
||||
}
|
||||
|
||||
public String getBizTags() {
|
||||
return mBizTags;
|
||||
}
|
||||
|
||||
public void setBizTags(String tags) {
|
||||
mBizTags = tags;
|
||||
}
|
||||
|
||||
public String getSchema() {
|
||||
return mSchema;
|
||||
}
|
||||
|
||||
public ExecutorService getWorker() {
|
||||
return mWorker;
|
||||
}
|
||||
|
||||
public ExecutorService getResolveWorker() {
|
||||
return mResolveWorker;
|
||||
}
|
||||
|
||||
public ExecutorService getDbWorker() {
|
||||
return mDbWorker;
|
||||
}
|
||||
|
||||
public void setWorker(ExecutorService worker) {
|
||||
// 给测试使用
|
||||
this.mWorker = worker;
|
||||
this.mDbWorker = worker;
|
||||
mResolveWorker = worker;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换https
|
||||
*
|
||||
* @return 配置是否变化
|
||||
*/
|
||||
public boolean setHTTPSRequestEnabled(boolean enabled) {
|
||||
String oldSchema = mSchema;
|
||||
if (enabled) {
|
||||
mSchema = HttpRequestConfig.HTTPS_SCHEMA;
|
||||
} else {
|
||||
mSchema = HttpRequestConfig.HTTP_SCHEMA;
|
||||
}
|
||||
if (!mSchema.equals(oldSchema)) {
|
||||
saveToCache();
|
||||
}
|
||||
return !mSchema.equals(oldSchema);
|
||||
}
|
||||
|
||||
public void setEnableDegradationLocalDns(boolean enable) {
|
||||
mEnableDegradationLocalDns = enable;
|
||||
}
|
||||
|
||||
public boolean isEnableDegradationLocalDns() {
|
||||
return mEnableDegradationLocalDns;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户切换的region
|
||||
*/
|
||||
public boolean setRegion(String region) {
|
||||
if (!mRegion.equals(region)) {
|
||||
mRegion = region;
|
||||
mInitServer = RegionServerManager.getInitServer(mRegion);
|
||||
mDefaultUpdateServer = RegionServerManager.getUpdateServer(mRegion);
|
||||
mCurrentServer.setServerIps(mRegion, mInitServer.getServerIps(), mInitServer.getPorts(), mInitServer.getIpv6ServerIps(), mInitServer.getIpv6Ports());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取ipv6的服务节点
|
||||
*/
|
||||
public String[] getIpv6ServerIps() {
|
||||
return this.mCurrentServer.getIpv6ServerIps();
|
||||
}
|
||||
|
||||
public RegionServer getInitServer() {
|
||||
return this.mInitServer;
|
||||
}
|
||||
|
||||
public RegionServer getDefaultUpdateServer() {
|
||||
return mDefaultUpdateServer;
|
||||
}
|
||||
|
||||
public String[] getDefaultIpv6UpdateServer() {
|
||||
return mDefaultUpdateServer.getIpv6ServerIps();
|
||||
}
|
||||
|
||||
public String getUA() {
|
||||
return mUA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {return true;}
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
HttpDnsConfig that = (HttpDnsConfig)o;
|
||||
return mEnabled == that.mEnabled &&
|
||||
mTimeout == that.mTimeout &&
|
||||
mHitCrashDefend == that.mHitCrashDefend &&
|
||||
mRemoteDisabled == that.mRemoteDisabled &&
|
||||
mIPRankingDisabled == that.mIPRankingDisabled &&
|
||||
CommonUtil.equals(mContext, that.mContext) &&
|
||||
CommonUtil.equals(mInitServer, that.mInitServer) &&
|
||||
CommonUtil.equals(mDefaultUpdateServer, that.mDefaultUpdateServer) &&
|
||||
CommonUtil.equals(mCurrentServer, that.mCurrentServer) &&
|
||||
CommonUtil.equals(mAccountId, that.mAccountId) &&
|
||||
CommonUtil.equals(mSchema, that.mSchema) &&
|
||||
CommonUtil.equals(mRegion, that.mRegion) &&
|
||||
CommonUtil.equals(mCacheHelper, that.mCacheHelper) &&
|
||||
CommonUtil.equals(mWorker, that.mWorker) &&
|
||||
CommonUtil.equals(mResolveWorker, that.mResolveWorker) &&
|
||||
CommonUtil.equals(mDbWorker, that.mDbWorker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(
|
||||
new Object[] {mContext, mEnabled, mInitServer, mDefaultUpdateServer, mCurrentServer,
|
||||
mAccountId, mSchema, mRegion, mTimeout, mHitCrashDefend, mRemoteDisabled,
|
||||
mIPRankingDisabled,
|
||||
mCacheHelper, mWorker, mResolveWorker, mDbWorker});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置初始服务IP
|
||||
* <p>
|
||||
* 线上SDK 初始化服务IP是内置写死的。
|
||||
* 本API主要用于一些测试代码使用
|
||||
*
|
||||
*/
|
||||
public void setInitServers(String initRegion, String[] initIps, int[] initPorts,
|
||||
String[] initIpv6s, int[] initV6Ports) {
|
||||
this.mRegion = initRegion;
|
||||
if (initIps == null) {
|
||||
return;
|
||||
}
|
||||
String[] oldInitServerIps = this.mInitServer.getServerIps();
|
||||
int[] oldInitPorts = this.mInitServer.getPorts();
|
||||
String[] oldInitIpv6ServerIps = this.mInitServer.getIpv6ServerIps();
|
||||
int[] oldInitIpv6Ports = this.mInitServer.getIpv6Ports();
|
||||
this.mInitServer.updateAll(initRegion, initIps, initPorts, initIpv6s, initV6Ports);
|
||||
if (mCurrentServer.getServerIps() == null
|
||||
|| mCurrentServer.getIpv6ServerIps() == null
|
||||
|| (CommonUtil.isSameServer(oldInitServerIps, oldInitPorts,
|
||||
mCurrentServer.getServerIps(), mCurrentServer.getPorts())
|
||||
&& CommonUtil.isSameServer(oldInitIpv6ServerIps, oldInitIpv6Ports,
|
||||
mCurrentServer.getIpv6ServerIps(), mCurrentServer.getIpv6Ports()))) {
|
||||
mCurrentServer.setServerIps(initRegion, initIps, initPorts, initIpv6s, initV6Ports);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置兜底的调度IP,
|
||||
* 测试代码使用
|
||||
*/
|
||||
public void setDefaultUpdateServer(String[] ips, int[] ports) {
|
||||
this.mDefaultUpdateServer.updateRegionAndIpv4(this.mInitServer.getRegion(), ips, ports);
|
||||
}
|
||||
|
||||
public void setDefaultUpdateServerIpv6(String[] defaultServerIps, int[] ports) {
|
||||
this.mDefaultUpdateServer.updateIpv6(defaultServerIps, ports);
|
||||
}
|
||||
|
||||
public void crashDefend(boolean crashDefend) {
|
||||
this.mHitCrashDefend = crashDefend;
|
||||
}
|
||||
|
||||
public void remoteDisable(boolean disable) {
|
||||
this.mRemoteDisabled = disable;
|
||||
}
|
||||
|
||||
public void ipRankingDisable(boolean disable) {
|
||||
this.mIPRankingDisabled = disable;
|
||||
}
|
||||
|
||||
public boolean isIPRankingDisabled() {
|
||||
return mIPRankingDisabled;
|
||||
}
|
||||
|
||||
public HttpDnsSettings.NetworkDetector getNetworkDetector() {
|
||||
return mNetworkDetector;
|
||||
}
|
||||
|
||||
public void setNetworkDetector(HttpDnsSettings.NetworkDetector networkDetector) {
|
||||
this.mNetworkDetector = networkDetector;
|
||||
}
|
||||
|
||||
// 缓存相关的 处理,暂时放这里
|
||||
|
||||
public void saveToCache() {
|
||||
if (mCacheHelper != null && mContext != null) {
|
||||
mCacheHelper.saveConfigToCache(mContext, this);
|
||||
}
|
||||
}
|
||||
|
||||
public SpCacheItem[] getCacheItem() {
|
||||
return new SpCacheItem[] {this, mCurrentServer, mObservableConfig};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFromCache(SharedPreferences sp) {
|
||||
mEnabled = sp.getBoolean(Constants.CONFIG_ENABLE, Constants.DEFAULT_SDK_ENABLE);
|
||||
mObservableSampleBenchMarks = sp.getFloat(Constants.CONFIG_OBSERVABLE_BENCH_MARKS, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToCache(SharedPreferences.Editor editor) {
|
||||
editor.putBoolean(Constants.CONFIG_ENABLE, mEnabled);
|
||||
editor.putFloat(Constants.CONFIG_OBSERVABLE_BENCH_MARKS, mObservableSampleBenchMarks);
|
||||
}
|
||||
|
||||
private String getInitRegion(String accountId) {
|
||||
InitConfig config = InitConfig.getInitConfig(accountId);
|
||||
if (config == null) {
|
||||
return Constants.REGION_DEFAULT;
|
||||
}
|
||||
|
||||
return CommonUtil.fixRegion(config.getRegion());
|
||||
}
|
||||
|
||||
private String buildUA() {
|
||||
if (mContext == null) {
|
||||
return "";
|
||||
}
|
||||
String versionName = ObservableConstants.UNKNOWN;
|
||||
try {
|
||||
versionName = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionName;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
|
||||
}
|
||||
|
||||
return Build.BRAND + "/" + Build.MODEL
|
||||
+ ";" + "Android" + "/" + Build.VERSION.RELEASE
|
||||
+ ";" + mContext.getPackageName() + "/" + versionName
|
||||
+ ";" + "HTTPDNS" + "/" + BuildConfig.VERSION_NAME;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.alibaba.sdk.android.httpdns.impl;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsService;
|
||||
|
||||
/**
|
||||
* httpdns服务创建接口
|
||||
*/
|
||||
public interface HttpDnsCreator {
|
||||
HttpDnsService create(Context context, String accountId, String secretKey);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.alibaba.sdk.android.httpdns.impl;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsService;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* HttpDnsService 实例持有者
|
||||
*/
|
||||
public class HttpDnsInstanceHolder {
|
||||
|
||||
private final HttpDnsCreator mHttpDnsCreator;
|
||||
private final HashMap<String, HttpDnsService> mInstances;
|
||||
private final ErrorImpl mError = new ErrorImpl();
|
||||
|
||||
|
||||
public HttpDnsInstanceHolder(HttpDnsCreator creator) {
|
||||
this.mHttpDnsCreator = creator;
|
||||
mInstances = new HashMap<>();
|
||||
}
|
||||
|
||||
public HttpDnsService get(Context context, String account, String secretKey) {
|
||||
if (account == null || account.equals("")) {
|
||||
HttpDnsLog.e("init httpdns with emtpy account!!");
|
||||
return mError;
|
||||
}
|
||||
HttpDnsService service = mInstances.get(account);
|
||||
if (service == null) {
|
||||
|
||||
service = mHttpDnsCreator.create(context, account, secretKey);
|
||||
mInstances.put(account, service);
|
||||
} else {
|
||||
if (service instanceof HttpDnsServiceImpl) {
|
||||
((HttpDnsServiceImpl) service).setSecret(secretKey);
|
||||
}
|
||||
}
|
||||
return service;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,909 @@
|
||||
package com.alibaba.sdk.android.httpdns.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.sdk.android.crashdefend.CrashDefendApi;
|
||||
import com.alibaba.sdk.android.crashdefend.CrashDefendCallback;
|
||||
import com.alibaba.sdk.android.httpdns.BuildConfig;
|
||||
import com.alibaba.sdk.android.httpdns.HTTPDNSResult;
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsCallback;
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsService;
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsSettings;
|
||||
import com.alibaba.sdk.android.httpdns.InitConfig;
|
||||
import com.alibaba.sdk.android.httpdns.Region;
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.SyncService;
|
||||
import com.alibaba.sdk.android.httpdns.cache.RecordDBHelper;
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableConstants;
|
||||
import com.alibaba.sdk.android.httpdns.observable.event.CleanHostCacheEvent;
|
||||
import com.alibaba.sdk.android.httpdns.resolve.HostFilter;
|
||||
import com.alibaba.sdk.android.httpdns.resolve.ResolveHostCache;
|
||||
import com.alibaba.sdk.android.httpdns.resolve.ResolveHostCacheGroup;
|
||||
import com.alibaba.sdk.android.httpdns.resolve.ResolveHostRequestHandler;
|
||||
import com.alibaba.sdk.android.httpdns.resolve.ResolveHostResultRepo;
|
||||
import com.alibaba.sdk.android.httpdns.resolve.ResolveHostService;
|
||||
import com.alibaba.sdk.android.httpdns.resolve.BatchResolveHostService;
|
||||
import com.alibaba.sdk.android.httpdns.HTTPDNSResultWrapper;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.net.HttpDnsNetworkDetector;
|
||||
import com.alibaba.sdk.android.httpdns.net.NetworkStateManager;
|
||||
import com.alibaba.sdk.android.httpdns.ranking.IPRankingService;
|
||||
import com.alibaba.sdk.android.httpdns.serverip.RegionServerScheduleService;
|
||||
import com.alibaba.sdk.android.httpdns.serverip.RegionServerScheduleService.OnRegionServerIpUpdate;
|
||||
import com.alibaba.sdk.android.httpdns.serverip.ranking.RegionServerRankingService;
|
||||
import com.alibaba.sdk.android.httpdns.track.SessionTrackMgr;
|
||||
import com.alibaba.sdk.android.httpdns.utils.CommonUtil;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Looper;
|
||||
|
||||
/**
|
||||
* 域名解析服务 httpdns接口的实现
|
||||
*/
|
||||
public class HttpDnsServiceImpl implements HttpDnsService, OnRegionServerIpUpdate,
|
||||
NetworkStateManager.OnNetworkChange, SyncService {
|
||||
|
||||
protected HttpDnsConfig mHttpDnsConfig;
|
||||
private ResolveHostResultRepo mResultRepo;
|
||||
protected ResolveHostRequestHandler mRequestHandler;
|
||||
protected RegionServerScheduleService mScheduleService;
|
||||
protected IPRankingService mIpIPRankingService;
|
||||
protected RegionServerRankingService mRegionServerRankingService;
|
||||
protected ResolveHostService mResolveHostService;
|
||||
protected BatchResolveHostService mBatchResolveHostService;
|
||||
private HostFilter mFilter;
|
||||
private SignService mSignService;
|
||||
private AESEncryptService mAESEncryptService;
|
||||
private boolean resolveAfterNetworkChange = true;
|
||||
/**
|
||||
* crash defend 默认关闭
|
||||
*/
|
||||
private boolean mCrashDefendEnabled = false;
|
||||
public static Context sContext;
|
||||
|
||||
public HttpDnsServiceImpl(Context context, final String accountId, String secret) {
|
||||
try {
|
||||
InitConfig config = InitConfig.getInitConfig(accountId);
|
||||
sContext = (config != null && config.getContext() != null) ? config.getContext() : context;
|
||||
secret = (config != null && config.getSecretKey() != null) ? config.getSecretKey() : secret;
|
||||
mHttpDnsConfig = new HttpDnsConfig(sContext, accountId, secret);
|
||||
if (sContext == null) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.e("init httpdns with null context!!");
|
||||
}
|
||||
mHttpDnsConfig.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
mFilter = new HostFilter();
|
||||
mSignService = new SignService(secret);
|
||||
mAESEncryptService = new AESEncryptService();
|
||||
mIpIPRankingService = new IPRankingService(this.mHttpDnsConfig);
|
||||
mRegionServerRankingService = new RegionServerRankingService(mHttpDnsConfig);
|
||||
mResultRepo = new ResolveHostResultRepo(this.mHttpDnsConfig, this.mIpIPRankingService,
|
||||
new RecordDBHelper(this.mHttpDnsConfig.getContext(), this.mHttpDnsConfig.getAccountId()),
|
||||
new ResolveHostCacheGroup());
|
||||
mScheduleService = new RegionServerScheduleService(this.mHttpDnsConfig, this);
|
||||
mRequestHandler = new ResolveHostRequestHandler(mHttpDnsConfig, mScheduleService,
|
||||
mSignService, mAESEncryptService);
|
||||
HostResolveLocker asyncLocker = new HostResolveLocker();
|
||||
mResolveHostService = new ResolveHostService(this.mHttpDnsConfig,
|
||||
mIpIPRankingService,
|
||||
mRequestHandler, mResultRepo, mFilter, asyncLocker);
|
||||
mBatchResolveHostService = new BatchResolveHostService(this.mHttpDnsConfig, mResultRepo,
|
||||
mRequestHandler,
|
||||
mIpIPRankingService, mFilter, asyncLocker);
|
||||
mHttpDnsConfig.setNetworkDetector(HttpDnsNetworkDetector.getInstance());
|
||||
|
||||
beforeInit();
|
||||
|
||||
setupInitConfig(accountId);
|
||||
|
||||
if (mCrashDefendEnabled) {
|
||||
initCrashDefend(sContext, mHttpDnsConfig);
|
||||
}
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.w("init fail, crash defend");
|
||||
return;
|
||||
}
|
||||
NetworkStateManager.getInstance().init(sContext);
|
||||
NetworkStateManager.getInstance().addListener(this);
|
||||
|
||||
tryUpdateRegionServer(sContext, accountId);
|
||||
|
||||
mRegionServerRankingService.rankServiceIp(mHttpDnsConfig.getCurrentServer());
|
||||
favorInit(sContext, accountId);
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("httpdns service is init " + accountId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupInitConfig(final String accountId) {
|
||||
InitConfig config = InitConfig.getInitConfig(accountId);
|
||||
|
||||
if (config != null) {
|
||||
// 先设置和网络相关的内容
|
||||
this.mHttpDnsConfig.setTimeout(config.getTimeout());
|
||||
this.mHttpDnsConfig.setHTTPSRequestEnabled(config.isEnableHttps());
|
||||
mHttpDnsConfig.setBizTags(config.getBizTags());
|
||||
mHttpDnsConfig.setEnableDegradationLocalDns(config.isEnableDegradationLocalDns());
|
||||
// 再设置一些可以提前,没有副作用的内容
|
||||
mResolveHostService.setEnableExpiredIp(config.isEnableExpiredIp());
|
||||
if (config.getIPRankingList() != null) {
|
||||
mIpIPRankingService.setIPRankingList(config.getIPRankingList());
|
||||
}
|
||||
// 设置region 必须在 读取缓存之前。2.4.1版本开始region初始化提前到HttpDnsConfig初始化
|
||||
|
||||
// 设置 主站域名 需要在 读取缓存之前
|
||||
this.mResultRepo.setHostListWhichIpFixed(config.getHostListWithFixedIp());
|
||||
// 设置缓存控制,并读取缓存
|
||||
mResultRepo.setCachedIPEnabled(config.isEnableCacheIp(), config.getExpiredThresholdMillis());
|
||||
this.mResultRepo.setCacheTtlChanger(config.getCacheTtlChanger());
|
||||
resolveAfterNetworkChange = config.isResolveAfterNetworkChange();
|
||||
|
||||
if (config.getDegradationFilter() != null) {
|
||||
mFilter.setFilter(config.getDegradationFilter());
|
||||
}
|
||||
|
||||
if (config.getNotUseHttpDnsFilter() != null) {
|
||||
mFilter.setFilter(config.getNotUseHttpDnsFilter());
|
||||
}
|
||||
|
||||
mHttpDnsConfig.getObservableManager().positiveEnableObservable(config.isEnableObservable());
|
||||
|
||||
mCrashDefendEnabled = config.isEnableCrashDefend();
|
||||
|
||||
mRequestHandler.setSdnsGlobalParams(config.getSdnsGlobalParams());
|
||||
mAESEncryptService.setAesSecretKey(config.getAesSecretKey());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void beforeInit() {
|
||||
// only for test
|
||||
}
|
||||
|
||||
protected void favorInit(Context context, String accountId) {
|
||||
// for different favor init
|
||||
}
|
||||
|
||||
protected void initCrashDefend(Context context, final HttpDnsConfig config) {
|
||||
CrashDefendApi.registerCrashDefendSdk(context, "httpdns", BuildConfig.VERSION_NAME, 2, 7,
|
||||
new CrashDefendCallback() {
|
||||
@Override
|
||||
public void onSdkStart(int limitCount, int crashCount, int restoreCount) {
|
||||
config.crashDefend(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSdkStop(int limitCount, int crashCount, int restoreCount,
|
||||
long nextRestoreInterval) {
|
||||
config.crashDefend(true);
|
||||
HttpDnsLog.w("sdk is not safe to run");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSdkClosed(int restoreCount) {
|
||||
config.crashDefend(true);
|
||||
HttpDnsLog.e("sdk will not run any more");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setSecret(String secret) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
InitConfig config = InitConfig.getInitConfig(mHttpDnsConfig.getAccountId());
|
||||
secret = (config != null && config.getSecretKey() != null) ? config.getSecretKey() : secret;
|
||||
this.mSignService.setSecretKey(secret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serverIpUpdated(boolean regionUpdated) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
if (regionUpdated) {
|
||||
mResultRepo.clearMemoryCache();
|
||||
}
|
||||
mRequestHandler.resetStatus();
|
||||
|
||||
//服务IP更新,触发服务IP测速
|
||||
mRegionServerRankingService.rankServiceIp(mHttpDnsConfig.getCurrentServer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreResolveHosts(List<String> hostList) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return;
|
||||
}
|
||||
if (hostList == null || hostList.size() == 0) {
|
||||
HttpDnsLog.i("setPreResolveHosts empty list");
|
||||
return;
|
||||
}
|
||||
setPreResolveHosts(hostList, RequestIpType.v4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreResolveHosts(List<String> hostList, RequestIpType requestIpType) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return;
|
||||
}
|
||||
if (hostList == null || hostList.size() == 0) {
|
||||
HttpDnsLog.i("setPreResolveHosts empty list");
|
||||
return;
|
||||
}
|
||||
requestIpType = changeTypeWithNetType(mHttpDnsConfig.getNetworkDetector(), requestIpType);
|
||||
mBatchResolveHostService.batchResolveHostAsync(hostList, requestIpType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIpByHostAsync(String host) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return null;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return null;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return null;
|
||||
}
|
||||
String[] ips = getIPv4ListForHostAsync(host);
|
||||
if (ips == null || ips.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return ips[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIPv4ForHostAsync(String host) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return null;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return null;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return null;
|
||||
}
|
||||
String[] ips = getIPv4ListForHostAsync(host);
|
||||
if (ips == null || ips.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return ips[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getIpsByHostAsync(final String host) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return new String[0];
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return new String[0];
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return new String[0];
|
||||
}
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, RequestIpType.v4, null, getCurrentNetworkKey()).getIps();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getIPv4ListForHostAsync(String host) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return new String[0];
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return new String[0];
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return new String[0];
|
||||
}
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, RequestIpType.v4, null, getCurrentNetworkKey()).getIps();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getIPv6sByHostAsync(String host) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return new String[0];
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return new String[0];
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return new String[0];
|
||||
}
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, RequestIpType.v6, null, getCurrentNetworkKey())
|
||||
.getIpv6s();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getIPv6ListForHostASync(String host) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return new String[0];
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return new String[0];
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return new String[0];
|
||||
}
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, RequestIpType.v6, null, getCurrentNetworkKey())
|
||||
.getIpv6s();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getAllByHostAsync(String host) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, RequestIpType.both, null, getCurrentNetworkKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostAsync(String host) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, RequestIpType.both, null, getCurrentNetworkKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getIpsByHostAsync(String host, RequestIpType type) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
type = changeTypeWithNetType(mHttpDnsConfig.getNetworkDetector(), type);
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, type, null, getCurrentNetworkKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostAsync(String host, RequestIpType type) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
type = changeTypeWithNetType(mHttpDnsConfig.getNetworkDetector(), type);
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, type, null, getCurrentNetworkKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthCurrentTime(long time) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
mSignService.setCurrentTimestamp(time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSessionId() {
|
||||
return SessionTrackMgr.getInstance().getSessionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getIpsByHostAsync(String host, Map<String, String> params,
|
||||
String cacheKey) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, RequestIpType.v4, params, cacheKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostAsync(String host, Map<String, String> params,
|
||||
String cacheKey) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, RequestIpType.v4, params, cacheKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getIpsByHostAsync(String host, RequestIpType type,
|
||||
Map<String, String> params, String cacheKey) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
type = changeTypeWithNetType(mHttpDnsConfig.getNetworkDetector(), type);
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, type, params, cacheKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostAsync(String host, RequestIpType type,
|
||||
Map<String, String> params, String cacheKey) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
type = changeTypeWithNetType(mHttpDnsConfig.getNetworkDetector(), type);
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, type, params, cacheKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostSync(String host, RequestIpType type, Map<String, String> params, String cacheKey) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
type = changeTypeWithNetType(mHttpDnsConfig.getNetworkDetector(), type);
|
||||
if (Looper.getMainLooper() == Looper.myLooper()) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("request in main thread, use async request");
|
||||
}
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, type, null, getCurrentNetworkKey());
|
||||
}
|
||||
return mResolveHostService.resolveHostSync(host, type, params, cacheKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getHttpDnsResultForHostAsync(String host, RequestIpType type, Map<String, String> params, String cacheKey, HttpDnsCallback callback) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
if (callback != null) {
|
||||
callback.onHttpDnsCompleted(Constants.EMPTY);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
if (callback != null) {
|
||||
callback.onHttpDnsCompleted(Constants.EMPTY);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
if (callback != null) {
|
||||
callback.onHttpDnsCompleted(Constants.EMPTY);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
type = changeTypeWithNetType(mHttpDnsConfig.getNetworkDetector(), type);
|
||||
mResolveHostService.resolveHostAsync(host, type, params, cacheKey, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostSyncNonBlocking(String host, RequestIpType type, Map<String, String> params, String cacheKey) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
type = changeTypeWithNetType(mHttpDnsConfig.getNetworkDetector(), type);
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, type, params, cacheKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegion(String region) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return;
|
||||
}
|
||||
region = CommonUtil.fixRegion(region);
|
||||
if (CommonUtil.regionEquals(this.mHttpDnsConfig.getRegion(), region)
|
||||
&& !this.mHttpDnsConfig.isAllInitServer()) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("region " + region + " is same, do not update serverIps");
|
||||
}
|
||||
return;
|
||||
}
|
||||
boolean changed = mHttpDnsConfig.setRegion(region);
|
||||
if (changed) {
|
||||
mResultRepo.clearMemoryCache();
|
||||
//region变化,服务IP变成对应的预置IP,触发测速
|
||||
mRegionServerRankingService.rankServiceIp(mHttpDnsConfig.getCurrentServer());
|
||||
}
|
||||
mScheduleService.updateRegionServerIps(region, Constants.UPDATE_REGION_SERVER_SCENES_REGION_CHANGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegion(Region region) {
|
||||
setRegion(region == null ? "" : region.getRegion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIPv6ByHostAsync(String host) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return null;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return null;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return null;
|
||||
}
|
||||
String[] ips = getIPv6sByHostAsync(host);
|
||||
if (ips == null || ips.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return ips[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIPv6ForHostAsync(String host) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return null;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return null;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return null;
|
||||
}
|
||||
String[] ips = getIPv6sByHostAsync(host);
|
||||
if (ips == null || ips.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return ips[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkChange(String networkType) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mHttpDnsConfig.getWorker().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// 获取当前网络标识
|
||||
String requestNetworkKey = getCurrentNetworkKey();
|
||||
|
||||
// 获取历史域名
|
||||
HashMap<String, RequestIpType> allHost = mResultRepo.getAllHostWithoutFixedIP();
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("network change to " + requestNetworkKey + ", smart resolve hosts");
|
||||
}
|
||||
|
||||
// 智能增量解析
|
||||
if (resolveAfterNetworkChange && mHttpDnsConfig.isEnabled()) {
|
||||
smartBatchResolve(allHost, requestNetworkKey);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//网络变化,触发服务IP测速
|
||||
mRegionServerRankingService.rankServiceIp(mHttpDnsConfig.getCurrentServer());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getByHost(String host, RequestIpType type) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
type = changeTypeWithNetType(mHttpDnsConfig.getNetworkDetector(), type);
|
||||
if (Looper.getMainLooper() == Looper.myLooper()) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("request in main thread, use async request");
|
||||
}
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, type, null, getCurrentNetworkKey());
|
||||
}
|
||||
return mResolveHostService.resolveHostSync(host, type, null, getCurrentNetworkKey());
|
||||
}
|
||||
|
||||
private RequestIpType changeTypeWithNetType(HttpDnsSettings.NetworkDetector networkDetector,
|
||||
RequestIpType type) {
|
||||
if (type == RequestIpType.auto) {
|
||||
if (networkDetector != null) {
|
||||
switch (networkDetector.getNetType(mHttpDnsConfig.getContext())) {
|
||||
case v4:
|
||||
return RequestIpType.v4;
|
||||
default:
|
||||
return RequestIpType.both;
|
||||
}
|
||||
}
|
||||
return RequestIpType.both;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public void cleanHostCache(ArrayList<String> hosts) {
|
||||
CleanHostCacheEvent cleanHostCacheEvent = new CleanHostCacheEvent();
|
||||
if (hosts == null || hosts.size() == 0) {
|
||||
// 清理所有host
|
||||
mResultRepo.clear();
|
||||
cleanHostCacheEvent.setTag(ObservableConstants.CLEAN_ALL_HOST_CACHE);
|
||||
} else {
|
||||
// 清理选中的host
|
||||
cleanHostCacheEvent.setTag(ObservableConstants.CLEAN_SPECIFY_HOST_CACHE);
|
||||
mResultRepo.clear(hosts);
|
||||
}
|
||||
cleanHostCacheEvent.setStatusCode(200);
|
||||
cleanHostCacheEvent.setCostTime((int) (System.currentTimeMillis() - cleanHostCacheEvent.getTimestamp()));
|
||||
mHttpDnsConfig.getObservableManager().addObservableEvent(cleanHostCacheEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostSync(String host, RequestIpType type) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
type = changeTypeWithNetType(mHttpDnsConfig.getNetworkDetector(), type);
|
||||
if (Looper.getMainLooper() == Looper.myLooper()) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("request in main thread, use async request");
|
||||
}
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, type, null, getCurrentNetworkKey());
|
||||
}
|
||||
return mResolveHostService.resolveHostSync(host, type, null, getCurrentNetworkKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getHttpDnsResultForHostAsync(String host, RequestIpType type, HttpDnsCallback callback) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
if (callback != null) {
|
||||
callback.onHttpDnsCompleted(Constants.EMPTY);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
if (callback != null) {
|
||||
callback.onHttpDnsCompleted(Constants.EMPTY);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
if (callback != null) {
|
||||
callback.onHttpDnsCompleted(Constants.EMPTY);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
type = changeTypeWithNetType(mHttpDnsConfig.getNetworkDetector(), type);
|
||||
|
||||
mResolveHostService.resolveHostAsync(host, type, null, getCurrentNetworkKey(), callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HTTPDNSResult getHttpDnsResultForHostSyncNonBlocking(String host, RequestIpType type) {
|
||||
if (!mHttpDnsConfig.isEnabled()) {
|
||||
HttpDnsLog.i("service is disabled");
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (!CommonUtil.isAHost(host)) {
|
||||
HttpDnsLog.i("host is invalid. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
if (CommonUtil.isAnIP(host)) {
|
||||
HttpDnsLog.i("host is ip. " + host);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
type = changeTypeWithNetType(mHttpDnsConfig.getNetworkDetector(), type);
|
||||
return mResolveHostService.resolveHostSyncNonBlocking(host, type, null, getCurrentNetworkKey());
|
||||
}
|
||||
|
||||
private void tryUpdateRegionServer(Context context, String accountId) {
|
||||
|
||||
if (mHttpDnsConfig.getCurrentServer().shouldUpdateServerIp()) {
|
||||
mScheduleService.updateRegionServerIps(Constants.UPDATE_REGION_SERVER_SCENES_INIT);
|
||||
} else {
|
||||
InitConfig config = InitConfig.getInitConfig(accountId);
|
||||
String initRegion = Constants.REGION_DEFAULT;
|
||||
if (config != null) {
|
||||
initRegion = CommonUtil.fixRegion(config.getRegion());
|
||||
}
|
||||
SharedPreferences sp = context.getSharedPreferences(
|
||||
Constants.CONFIG_CACHE_PREFIX + accountId, Context.MODE_PRIVATE);
|
||||
String cachedRegion = sp.getString(Constants.CONFIG_CURRENT_SERVER_REGION,
|
||||
Constants.REGION_DEFAULT);
|
||||
|
||||
if (!CommonUtil.regionEquals(cachedRegion, initRegion)) {
|
||||
mScheduleService.updateRegionServerIps(Constants.UPDATE_REGION_SERVER_SCENES_REGION_CHANGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能增量解析:只解析当前网络环境下缺失的域名
|
||||
*
|
||||
* @param allHosts 所有历史域名
|
||||
* @param requestNetworkKey 请求时的网络标识
|
||||
*/
|
||||
private void smartBatchResolve(HashMap<String, RequestIpType> allHosts, String requestNetworkKey) {
|
||||
ArrayList<String> v4List = new ArrayList<>();
|
||||
ArrayList<String> v6List = new ArrayList<>();
|
||||
ArrayList<String> bothList = new ArrayList<>();
|
||||
|
||||
// 检查当前网络环境下是否需要解析
|
||||
for (Map.Entry<String, RequestIpType> entry : allHosts.entrySet()) {
|
||||
String host = entry.getKey();
|
||||
RequestIpType type = entry.getValue();
|
||||
|
||||
// 使用请求时的网络标识检查缓存
|
||||
if (needsResolveInNetwork(host, type, requestNetworkKey)) {
|
||||
if (type == RequestIpType.v4) {
|
||||
v4List.add(host);
|
||||
} else if (type == RequestIpType.v6) {
|
||||
v6List.add(host);
|
||||
} else {
|
||||
bothList.add(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用带网络标识的批量解析方法
|
||||
if (v4List.size() > 0) {
|
||||
mBatchResolveHostService.batchResolveHostAsync(v4List, RequestIpType.v4);
|
||||
}
|
||||
if (v6List.size() > 0) {
|
||||
mBatchResolveHostService.batchResolveHostAsync(v6List, RequestIpType.v6);
|
||||
}
|
||||
if (bothList.size() > 0) {
|
||||
mBatchResolveHostService.batchResolveHostAsync(bothList, RequestIpType.both);
|
||||
}
|
||||
|
||||
if (v4List.size() > 0 || v6List.size() > 0 || bothList.size() > 0) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("smart resolve " + (v4List.size() + v6List.size() + bothList.size()) + " hosts for network " + requestNetworkKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定网络环境下是否需要解析域名
|
||||
*
|
||||
* @param host 域名
|
||||
* @param type 解析类型
|
||||
* @param networkKey 网络标识
|
||||
* @return 是否需要解析
|
||||
*/
|
||||
private boolean needsResolveInNetwork(String host, RequestIpType type, String networkKey) {
|
||||
// 检查指定网络环境下是否有有效缓存
|
||||
ResolveHostCache cache = mResultRepo.getCacheGroup().getCache(networkKey);
|
||||
HTTPDNSResultWrapper result = cache.getResult(host, type);
|
||||
return result == null || result.isExpired();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前网络标识,用于网络隔离缓存
|
||||
*
|
||||
* @return 当前网络标识
|
||||
*/
|
||||
private String getCurrentNetworkKey() {
|
||||
return NetworkStateManager.getInstance().getCurrentNetworkKey();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.alibaba.sdk.android.httpdns.impl;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.utils.CommonUtil;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class SignService {
|
||||
private static final String ALGORITHM = "HmacSHA256";
|
||||
public static final int EXPIRATION_TIME = 10 * 60;
|
||||
|
||||
private static final String SDNS_PREFIX = "sdns-";
|
||||
private static final Set<String> includeParamKeys = new HashSet<>();
|
||||
|
||||
private String mSecretKey;
|
||||
|
||||
private long mOffset = 0L;
|
||||
|
||||
public SignService(String secretKey) {
|
||||
mSecretKey = secretKey;
|
||||
includeParamKeys.addAll(Arrays.asList("id", "m", "dn", "cip", "q", "enc", "exp", "tags", "v"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置密钥
|
||||
*/
|
||||
public void setSecretKey(String secretKey) {
|
||||
mSecretKey = secretKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 HMAC-SHA256 生成十六进制格式的签名
|
||||
*/
|
||||
public String sign(Map<String, String> param) {
|
||||
if (TextUtils.isEmpty(mSecretKey)) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("secretKey为空.");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
String signStr;
|
||||
try {
|
||||
signStr = generateV2SignContent(param);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("signStr:" + signStr);
|
||||
}
|
||||
signStr = hmacSha256(signStr);
|
||||
} catch (Exception e) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.e("sign fail.", e);
|
||||
}
|
||||
signStr = "";
|
||||
}
|
||||
return signStr;
|
||||
}
|
||||
|
||||
private static String generateV2SignContent(Map<String, String> map) {
|
||||
Map<String, String> sortedMap = new TreeMap<>();
|
||||
for(Map.Entry<String, String> entry : map.entrySet()) {
|
||||
String paramKey = entry.getKey();
|
||||
String paramValue = entry.getValue();
|
||||
if (includeParamKeys.contains(paramKey) || paramKey.startsWith(SDNS_PREFIX)) {
|
||||
if(!TextUtils.isEmpty(paramValue)) {
|
||||
sortedMap.put(paramKey, paramValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
StringBuilder signContent = new StringBuilder();
|
||||
for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
|
||||
if (signContent.length() > 0) {
|
||||
signContent.append("&");
|
||||
}
|
||||
signContent.append(entry.getKey()).append("=").append(entry.getValue());
|
||||
}
|
||||
return signContent.toString();
|
||||
}
|
||||
|
||||
private String hmacSha256(String content)
|
||||
throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
Mac mac = Mac.getInstance(ALGORITHM);
|
||||
mac.init(new SecretKeySpec(CommonUtil.decodeHex(mSecretKey), ALGORITHM));
|
||||
byte[] signedBytes = mac.doFinal(content.getBytes(StandardCharsets.UTF_8));
|
||||
return CommonUtil.encodeHexString(signedBytes);
|
||||
}
|
||||
|
||||
public void setCurrentTimestamp(long serverTime) {
|
||||
mOffset = serverTime - System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
public String getExpireTime(){
|
||||
return Long.toString(System.currentTimeMillis() / 1000 + EXPIRATION_TIME + mOffset);
|
||||
}
|
||||
|
||||
public Boolean isSignMode(){
|
||||
return !TextUtils.isEmpty(mSecretKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.alibaba.sdk.android.httpdns.log;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.ILogger;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* 日志工具类
|
||||
*/
|
||||
public class HttpDnsLog {
|
||||
|
||||
public static final String TAG = "httpdns";
|
||||
private static boolean printToLogcat = false;
|
||||
private static final HashSet<ILogger> LOGGERS = new HashSet<>();
|
||||
|
||||
/**
|
||||
* 设置日志接口
|
||||
* 不受{@link #printToLogcat} 控制
|
||||
*/
|
||||
public static void setLogger(ILogger logger) {
|
||||
if (logger != null) {
|
||||
LOGGERS.add(logger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除日志接口
|
||||
*/
|
||||
public static void removeLogger(ILogger logger) {
|
||||
if (logger != null) {
|
||||
LOGGERS.remove(logger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* logcat开关
|
||||
*
|
||||
* @param enable
|
||||
*/
|
||||
public static void enable(boolean enable) {
|
||||
HttpDnsLog.printToLogcat = enable;
|
||||
}
|
||||
|
||||
public static boolean isPrint() {
|
||||
return HttpDnsLog.printToLogcat;
|
||||
}
|
||||
|
||||
public static void e(String errLog) {
|
||||
if (HttpDnsLog.printToLogcat) {
|
||||
Log.e(TAG, errLog);
|
||||
}
|
||||
if (HttpDnsLog.LOGGERS.size() > 0) {
|
||||
for (ILogger logger : HttpDnsLog.LOGGERS) {
|
||||
logger.log("[E]" + errLog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void i(String infoLog) {
|
||||
if (HttpDnsLog.printToLogcat) {
|
||||
Log.i(TAG, infoLog);
|
||||
}
|
||||
if (HttpDnsLog.LOGGERS.size() > 0) {
|
||||
for (ILogger logger : HttpDnsLog.LOGGERS) {
|
||||
logger.log("[I]" + infoLog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void d(String debugLog) {
|
||||
if (HttpDnsLog.printToLogcat) {
|
||||
Log.d(TAG, debugLog);
|
||||
}
|
||||
if (HttpDnsLog.LOGGERS.size() > 0) {
|
||||
for (ILogger logger : HttpDnsLog.LOGGERS) {
|
||||
logger.log("[D]" + debugLog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void w(String warnLog) {
|
||||
if (HttpDnsLog.printToLogcat) {
|
||||
Log.w(TAG, warnLog);
|
||||
}
|
||||
if (HttpDnsLog.LOGGERS.size() > 0) {
|
||||
for (ILogger logger : HttpDnsLog.LOGGERS) {
|
||||
logger.log("[W]" + warnLog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void e(String errLog, Throwable throwable) {
|
||||
if (HttpDnsLog.printToLogcat) {
|
||||
Log.e(TAG, errLog, throwable);
|
||||
}
|
||||
if (HttpDnsLog.LOGGERS.size() > 0) {
|
||||
for (ILogger logger : HttpDnsLog.LOGGERS) {
|
||||
logger.log("[E]" + errLog);
|
||||
}
|
||||
printStackTrace(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
public static void w(String warnLog, Throwable throwable) {
|
||||
if (HttpDnsLog.printToLogcat) {
|
||||
Log.e(TAG, warnLog, throwable);
|
||||
}
|
||||
if (HttpDnsLog.LOGGERS.size() > 0) {
|
||||
for (ILogger logger : HttpDnsLog.LOGGERS) {
|
||||
logger.log("[W]" + warnLog);
|
||||
}
|
||||
printStackTrace(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printStackTrace(Throwable throwable) {
|
||||
if (HttpDnsLog.LOGGERS.size() > 0) {
|
||||
for (ILogger logger : HttpDnsLog.LOGGERS) {
|
||||
logger.log(Log.getStackTraceString(throwable));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ip数组转成字符串方便输出
|
||||
*
|
||||
* @param ips
|
||||
* @return
|
||||
*/
|
||||
public static String wrap(String[] ips) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("[");
|
||||
for (int i = 0; i < ips.length; i++) {
|
||||
if (i != 0) {
|
||||
stringBuilder.append(",");
|
||||
}
|
||||
stringBuilder.append(ips[i]);
|
||||
}
|
||||
stringBuilder.append("]");
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.alibaba.sdk.android.httpdns.net;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsSettings;
|
||||
import com.alibaba.sdk.android.httpdns.NetType;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.utils.ThreadUtil;
|
||||
|
||||
import android.content.Context;
|
||||
import com.aliyun.ams.ipdetector.Inet64Util;
|
||||
|
||||
public class HttpDnsNetworkDetector implements HttpDnsSettings.NetworkDetector {
|
||||
|
||||
private static class Holder {
|
||||
private static final HttpDnsNetworkDetector INSTANCE = new HttpDnsNetworkDetector();
|
||||
}
|
||||
|
||||
public static HttpDnsNetworkDetector getInstance() {
|
||||
return Holder.INSTANCE;
|
||||
}
|
||||
|
||||
private HttpDnsNetworkDetector() {
|
||||
}
|
||||
|
||||
private final ExecutorService mWorker = ThreadUtil.createSingleThreadService("NetType");
|
||||
private boolean mCheckInterface = true;
|
||||
private String mHostToCheckNetType = "www.taobao.com";
|
||||
private NetType mCache = NetType.none;
|
||||
private boolean mDisableCache = false;
|
||||
private Context mContext;
|
||||
|
||||
/**
|
||||
* 网络变化时,清除缓存
|
||||
*/
|
||||
public void cleanCache(final boolean connected) {
|
||||
if (mDisableCache) {
|
||||
return;
|
||||
}
|
||||
mCache = NetType.none;
|
||||
if (connected && this.mContext != null) {
|
||||
mWorker.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// 异步探测一下
|
||||
if (mContext != null) {
|
||||
mCache = detectNetType(mContext);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否禁用缓存,默认不禁用
|
||||
* 不确定是否存在网络链接不变的情况下,网络情况会发生变化的情况,所以提供了此开关
|
||||
*/
|
||||
public void disableCache(boolean disable) {
|
||||
this.mDisableCache = disable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果不能检查本地网关ip,可以调用此接口关闭
|
||||
*/
|
||||
public void setCheckInterface(boolean checkInterface) {
|
||||
this.mCheckInterface = checkInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* 有些场景需要通过本地解析来确认网络类型,默认使用 www.taobao.com
|
||||
*/
|
||||
public void setHostToCheckNetType(String hostToCheckNetType) {
|
||||
this.mHostToCheckNetType = hostToCheckNetType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetType getNetType(Context context) {
|
||||
if (mDisableCache) {
|
||||
NetType tmp = detectNetType(context);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("ipdetector type is " + tmp.name());
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
if (mCache != NetType.none) {
|
||||
return mCache;
|
||||
}
|
||||
mCache = detectNetType(context);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("ipdetector type is " + mCache.name());
|
||||
}
|
||||
return mCache;
|
||||
}
|
||||
|
||||
private NetType detectNetType(Context context) {
|
||||
this.mContext = context.getApplicationContext();
|
||||
try {
|
||||
int type;// 不检查本地IP的情况下,无法过滤ipv6只有本地ip的情况,需要通过其它方式检测下。
|
||||
// 没有网络?
|
||||
if (mCheckInterface) {
|
||||
type = Inet64Util.getIpStack(context);
|
||||
} else {
|
||||
type = Inet64Util.getIpStackCheckLocal(context);
|
||||
}
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("ip detector type is " + type);
|
||||
}
|
||||
if (type == IP_DUAL_STACK) {
|
||||
return NetType.both;
|
||||
} else if (type == IPV4_ONLY) {
|
||||
return NetType.v4;
|
||||
} else if (type == IPV6_ONLY) {
|
||||
return NetType.v6;
|
||||
} else {
|
||||
// 没有网络?
|
||||
return NetType.none;
|
||||
}
|
||||
|
||||
} catch (Throwable e) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i("ip detector not exist.");
|
||||
}
|
||||
return NetType.none;
|
||||
}
|
||||
}
|
||||
|
||||
private final static int IPV4_ONLY = 1;
|
||||
private final static int IPV6_ONLY = 2;
|
||||
private final static int IP_DUAL_STACK = 3;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
package com.alibaba.sdk.android.httpdns.net;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.utils.ThreadUtil;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Network;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.LinkAddress;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
|
||||
public class NetworkStateManager {
|
||||
public static final String NONE_NETWORK = "None_Network";
|
||||
private static final String NETWORK_KEY_PREFIX = "emasInner_";
|
||||
private Context mContext;
|
||||
private String mLastConnectedNetwork = NONE_NETWORK;
|
||||
private volatile String mCurrentNetworkKey = NONE_NETWORK;
|
||||
private final ArrayList<OnNetworkChange> mListeners = new ArrayList<>();
|
||||
|
||||
private final ExecutorService mWorker = ThreadUtil.createSingleThreadService("network");
|
||||
|
||||
private static class Holder {
|
||||
private static final NetworkStateManager instance = new NetworkStateManager();
|
||||
}
|
||||
|
||||
public static NetworkStateManager getInstance() {
|
||||
return Holder.instance;
|
||||
}
|
||||
|
||||
private NetworkStateManager() {
|
||||
}
|
||||
|
||||
public void init(Context ctx) {
|
||||
if (ctx == null) {
|
||||
throw new IllegalStateException("Context can't be null");
|
||||
}
|
||||
if (this.mContext != null) {
|
||||
// inited
|
||||
return;
|
||||
}
|
||||
this.mContext = ctx.getApplicationContext();
|
||||
|
||||
// 立即检测当前网络状态
|
||||
mWorker.execute(() -> {
|
||||
try {
|
||||
String currentNetwork = detectCurrentNetwork();
|
||||
if (!currentNetwork.equals(NONE_NETWORK)) {
|
||||
mLastConnectedNetwork = currentNetwork;
|
||||
}
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("[NetworkStateManager.init] - Initial network detected: " + currentNetwork);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.e("[NetworkStateManager.init] - Failed to detect initial network", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BroadcastReceiver bcReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
try {
|
||||
mWorker.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (isInitialStickyBroadcast()) { // no need to handle initial
|
||||
// sticky broadcast
|
||||
return;
|
||||
}
|
||||
String action = intent.getAction();
|
||||
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)
|
||||
&& hasNetInfoPermission(context)) {
|
||||
String currentNetwork = detectCurrentNetwork();
|
||||
HttpDnsNetworkDetector.getInstance().cleanCache(
|
||||
!currentNetwork.equals(NONE_NETWORK));
|
||||
if (!currentNetwork.equals(NONE_NETWORK)
|
||||
&& !currentNetwork.equalsIgnoreCase(mLastConnectedNetwork)) {
|
||||
for (OnNetworkChange onNetworkChange : mListeners) {
|
||||
onNetworkChange.onNetworkChange(currentNetwork);
|
||||
}
|
||||
}
|
||||
if (!currentNetwork.equals(NONE_NETWORK)) {
|
||||
mLastConnectedNetwork = currentNetwork;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
if (hasNetInfoPermission(mContext)) {
|
||||
IntentFilter mFilter = new IntentFilter();
|
||||
mFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
mContext.registerReceiver(bcReceiver, mFilter);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private String detectCurrentNetwork() {
|
||||
try {
|
||||
ConnectivityManager connectivityManager = (ConnectivityManager)mContext
|
||||
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo info = connectivityManager.getActiveNetworkInfo();
|
||||
if (info != null && info.isAvailable() && info.isConnected()) {
|
||||
String name = info.getTypeName();
|
||||
|
||||
String ipPrefix = getLocalIpPrefix();
|
||||
if (ipPrefix != null) {
|
||||
name = name + "_" + ipPrefix;
|
||||
}
|
||||
// 增加前缀,防止与用户cacheKey冲突
|
||||
mCurrentNetworkKey = (name == null) ? NONE_NETWORK : (NETWORK_KEY_PREFIX + name);
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("[detectCurrentNetwork] - Network key:" + mCurrentNetworkKey + " subType "
|
||||
+ "name: "
|
||||
+ info.getSubtypeName());
|
||||
}
|
||||
return mCurrentNetworkKey;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
mCurrentNetworkKey = NONE_NETWORK;
|
||||
return NONE_NETWORK;
|
||||
}
|
||||
|
||||
private String getLocalIpPrefix() {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ConnectivityManager connectivityManager = (ConnectivityManager) mContext
|
||||
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
Network activeNetwork = connectivityManager.getActiveNetwork();
|
||||
|
||||
if (activeNetwork == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LinkProperties linkProperties = connectivityManager.getLinkProperties(activeNetwork);
|
||||
if (linkProperties == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String ipv6Backup = null;
|
||||
for (LinkAddress linkAddress : linkProperties.getLinkAddresses()) {
|
||||
InetAddress address = linkAddress.getAddress();
|
||||
if (address.isLoopbackAddress() || address.isLinkLocalAddress()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (address instanceof Inet4Address) {
|
||||
String ip = address.getHostAddress();
|
||||
String[] parts = ip.split("\\.");
|
||||
if (parts.length >= 3) {
|
||||
return parts[0] + "." + parts[1] + "." + parts[2];
|
||||
}
|
||||
} else if (address instanceof Inet6Address && ipv6Backup == null) {
|
||||
String ipv6 = address.getHostAddress();
|
||||
if (ipv6.contains("%")) {
|
||||
ipv6 = ipv6.substring(0, ipv6.indexOf("%"));
|
||||
}
|
||||
String[] parts = ipv6.split(":");
|
||||
if (parts.length >= 4) {
|
||||
ipv6Backup = parts[0] + ":" + parts[1] + ":" + parts[2] + ":" + parts[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
return ipv6Backup;
|
||||
} catch (Exception e) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("[getLocalIpPrefix] - Failed to get IP prefix: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getCurrentNetworkKey() {
|
||||
return mCurrentNetworkKey;
|
||||
}
|
||||
|
||||
public void addListener(OnNetworkChange change) {
|
||||
this.mListeners.add(change);
|
||||
}
|
||||
|
||||
public interface OnNetworkChange {
|
||||
void onNetworkChange(String networkType);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mContext = null;
|
||||
mLastConnectedNetwork = NONE_NETWORK;
|
||||
mCurrentNetworkKey = NONE_NETWORK;
|
||||
mListeners.clear();
|
||||
}
|
||||
|
||||
private static boolean hasNetInfoPermission(Context context) {
|
||||
try {
|
||||
return checkSelfPermission(context, Manifest.permission.ACCESS_NETWORK_STATE)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
} catch (Throwable e) {
|
||||
HttpDnsLog.w("check network info permission fail", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int checkSelfPermission(Context context, String permission) {
|
||||
return context.checkPermission(permission, Process.myPid(), Process.myUid());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.alibaba.sdk.android.httpdns.observable;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.config.SpCacheItem;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class ObservableConfig implements SpCacheItem {
|
||||
//默认关闭
|
||||
public boolean enable = false;
|
||||
public double sampleRatio = 1.0;
|
||||
public String endpoint = "";
|
||||
public int batchReportMaxSize = 50;
|
||||
public int batchReportIntervalTime = 60;
|
||||
public int maxReportsPerMinute = 4;
|
||||
public JSONObject raw = null;
|
||||
|
||||
public boolean updateConfig(ObservableConfig config) {
|
||||
if (config == null) {
|
||||
//调度接口没有下发配置,关闭可观测
|
||||
if (enable) {
|
||||
enable = false;
|
||||
raw = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isSameConfig(config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
enable = config.enable;
|
||||
sampleRatio = config.sampleRatio;
|
||||
endpoint = config.endpoint;
|
||||
batchReportMaxSize = config.batchReportMaxSize;
|
||||
batchReportIntervalTime = config.batchReportIntervalTime;
|
||||
maxReportsPerMinute = config.maxReportsPerMinute;
|
||||
raw = config.raw;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isSameConfig(ObservableConfig config) {
|
||||
return enable == config.enable
|
||||
&& sampleRatio == config.sampleRatio
|
||||
&& TextUtils.equals(endpoint, config.endpoint)
|
||||
&& batchReportMaxSize == config.batchReportMaxSize
|
||||
&& batchReportIntervalTime == config.batchReportIntervalTime
|
||||
&& maxReportsPerMinute == config.maxReportsPerMinute;
|
||||
}
|
||||
|
||||
public static ObservableConfig fromJson(JSONObject json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ObservableConfig observableConfig = new ObservableConfig();
|
||||
if (json.has("enable")) {
|
||||
observableConfig.enable = json.optBoolean("enable");
|
||||
}
|
||||
if (json.has("sample_ratio")) {
|
||||
observableConfig.sampleRatio = json.optDouble("sample_ratio");
|
||||
}
|
||||
String endpoint = json.optString("endpoint");
|
||||
if (!TextUtils.isEmpty(endpoint)) {
|
||||
observableConfig.endpoint = endpoint;
|
||||
}
|
||||
observableConfig.batchReportMaxSize = json.optInt("batch_report_max_size", 50);
|
||||
if (observableConfig.batchReportMaxSize <= 0) {
|
||||
observableConfig.batchReportMaxSize = 50;
|
||||
}
|
||||
observableConfig.batchReportIntervalTime = json.optInt("batch_report_interval_time", 60);
|
||||
if (observableConfig.batchReportIntervalTime <= 0) {
|
||||
observableConfig.batchReportIntervalTime = 60;
|
||||
}
|
||||
observableConfig.maxReportsPerMinute = json.optInt("max_reports_per_minute", 4);
|
||||
if (observableConfig.maxReportsPerMinute <= 0) {
|
||||
observableConfig.maxReportsPerMinute = 4;
|
||||
}
|
||||
observableConfig.raw = json;
|
||||
|
||||
return observableConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFromCache(SharedPreferences sp) {
|
||||
String cachedObservableConfig = sp.getString(Constants.CONFIG_OBSERVABLE_CONFIG, null);
|
||||
if (!TextUtils.isEmpty(cachedObservableConfig)) {
|
||||
try {
|
||||
JSONObject json = new JSONObject(cachedObservableConfig);
|
||||
ObservableConfig cachedConfig = fromJson(json);
|
||||
enable = cachedConfig.enable;
|
||||
sampleRatio = cachedConfig.sampleRatio;
|
||||
endpoint = cachedConfig.endpoint;
|
||||
batchReportMaxSize = cachedConfig.batchReportMaxSize;
|
||||
batchReportIntervalTime = cachedConfig.batchReportIntervalTime;
|
||||
maxReportsPerMinute = cachedConfig.maxReportsPerMinute;
|
||||
raw = cachedConfig.raw;
|
||||
} catch (JSONException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToCache(SharedPreferences.Editor editor) {
|
||||
if (raw != null) {
|
||||
editor.putString(Constants.CONFIG_OBSERVABLE_CONFIG, raw.toString());
|
||||
} else {
|
||||
editor.remove(Constants.CONFIG_OBSERVABLE_CONFIG);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.alibaba.sdk.android.httpdns.observable;
|
||||
|
||||
public final class ObservableConstants {
|
||||
public static final int REQUEST_IP_TYPE_AUTO = 0x00;
|
||||
public static final int REQUEST_IP_TYPE_V4 = 0x01;
|
||||
public static final int REQUEST_IP_TYPE_V6 = 0x02;
|
||||
public static final int REQUEST_IP_TYPE_BOTH = 0x03;
|
||||
|
||||
public static final int RESOLVE_API_UNKNOWN = 0x00;
|
||||
public static final int RESOLVE_API_SYNC = 0x04;
|
||||
public static final int RESOLVE_API_ASYNC = 0x08;
|
||||
public static final int RESOLVE_API_SYN_NON_BLOCKING = 0x0C;
|
||||
|
||||
public static final int REMOTE_CACHE_NULL = 0x00;
|
||||
public static final int REMOTE_CACHE_EXPIRED = 0x10;
|
||||
|
||||
public static final int CACHE_NONE = 0x00;
|
||||
public static final int CACHE_NOT_EXPIRED = 0x10;
|
||||
public static final int CACHE_EXPIRED_NOT_USE = 0x20;
|
||||
public static final int CACHE_EXPIRED_USE = 0x30;
|
||||
|
||||
public static final int REQUEST_NOT_RETRY = 0x00;
|
||||
public static final int REQUEST_RETRY = 0x10;
|
||||
|
||||
public static final String UNKNOWN = "unknown";
|
||||
|
||||
public static final int HTTP_REQUEST = 0x00;
|
||||
public static final int HTTPS_REQUEST = 0x04;
|
||||
|
||||
public static final int NOT_SIGN_MODE_REQUEST = 0x00;
|
||||
public static final int SIGN_MODE_REQUEST = 0x08;
|
||||
|
||||
public static final int CLEAN_ALL_HOST_CACHE = 1;
|
||||
public static final int CLEAN_SPECIFY_HOST_CACHE = 2;
|
||||
|
||||
public static final int EMPTY_RESULT = 0x00;
|
||||
public static final int NOT_EMPTY_RESULT = 0x40;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.alibaba.sdk.android.httpdns.observable;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequestWrapper;
|
||||
import com.alibaba.sdk.android.httpdns.request.ResponseParser;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
public class ObservableHttpRequest<T> extends HttpRequestWrapper<T> {
|
||||
private String url;
|
||||
private ResponseParser<T> parser;
|
||||
private Map<String, String> mHeaders;
|
||||
private String mBody;
|
||||
|
||||
public ObservableHttpRequest(String url, ResponseParser<T> parser, Map<String, String> headers, String body) {
|
||||
super(null);
|
||||
this.url = url;
|
||||
this.parser = parser;
|
||||
mHeaders = headers;
|
||||
mBody = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T request() throws Throwable {
|
||||
HttpURLConnection conn = null;
|
||||
InputStream in = null;
|
||||
BufferedReader streamReader = null;
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("request url " + url);
|
||||
}
|
||||
|
||||
try {
|
||||
conn = (HttpURLConnection)new URL(url).openConnection();
|
||||
conn.setReadTimeout(10000);
|
||||
conn.setConnectTimeout(10000);
|
||||
|
||||
if (mHeaders != null) {
|
||||
for (Map.Entry<String, String> entry : mHeaders.entrySet()) {
|
||||
conn.setRequestProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(mBody)) {
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoOutput(true);
|
||||
|
||||
OutputStream outputStream = conn.getOutputStream();
|
||||
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8"));
|
||||
bw.write(mBody);
|
||||
bw.close();
|
||||
}
|
||||
|
||||
conn.connect();
|
||||
|
||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
||||
in = conn.getInputStream();
|
||||
streamReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
||||
String responseStr = readStringFrom(streamReader).toString();
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("request success " + responseStr);
|
||||
}
|
||||
|
||||
return parser.parse(null, responseStr);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d(e.getMessage());
|
||||
}
|
||||
} finally {
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
try {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
if (streamReader != null) {
|
||||
streamReader.close();
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
package com.alibaba.sdk.android.httpdns.observable;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Message;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsConfig;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.observable.event.GroupEvent;
|
||||
import com.alibaba.sdk.android.httpdns.observable.event.LocalDnsEvent;
|
||||
import com.alibaba.sdk.android.httpdns.observable.event.ObservableEvent;
|
||||
import com.alibaba.sdk.android.httpdns.observable.event.ReportingRateExceptionEvent;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequest;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequestTask;
|
||||
import com.alibaba.sdk.android.httpdns.request.RequestCallback;
|
||||
import com.alibaba.sdk.android.httpdns.request.ResponseParser;
|
||||
import com.alibaba.sdk.android.httpdns.request.RetryHttpRequest;
|
||||
import com.alibaba.sdk.android.httpdns.resolve.ResolveHostHelper;
|
||||
import com.alibaba.sdk.android.httpdns.track.SessionTrackMgr;
|
||||
import com.alibaba.sdk.android.httpdns.utils.ThreadUtil;
|
||||
|
||||
/**
|
||||
* utils data upload manager
|
||||
*/
|
||||
|
||||
public class ObservableManager {
|
||||
private static final int MESSAGE_REPORT = 101;
|
||||
private HttpDnsConfig mHttpDnsConfig;
|
||||
private String mSecret = null;
|
||||
private int mReportBatchIndex = 0;
|
||||
private List<ObservableEvent> mCacheEvents = new ArrayList<>();
|
||||
|
||||
private boolean mPositiveEnableObservable = true;
|
||||
private boolean mReportingRateException = false;
|
||||
private AtomicBoolean initMessage = new AtomicBoolean(false);
|
||||
private ArrayDeque<Long> mReportsTime = new ArrayDeque<>();
|
||||
private AtomicBoolean mReporting = new AtomicBoolean(false);
|
||||
|
||||
private final Handler mHandler;
|
||||
private static final ExecutorService sWorker = ThreadUtil.createObservableExecutorService();
|
||||
private static final ExecutorService mReportWorker = ThreadUtil.createObservableReportExecutorService();
|
||||
private static final char[] hexChar = {'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
public ObservableManager(HttpDnsConfig httpDnsConfig, String secret) {
|
||||
mHttpDnsConfig = httpDnsConfig;
|
||||
mSecret = secret;
|
||||
|
||||
if (mHttpDnsConfig.getObservableSampleBenchMarks() < 0) {
|
||||
mHttpDnsConfig.setObservableSampleBenchMarks(generateSampleBenchMarks());
|
||||
}
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("observable: " + mHttpDnsConfig.getObservableSampleBenchMarks());
|
||||
}
|
||||
|
||||
HandlerThread handlerThread = new HandlerThread("httpdns_observable");
|
||||
handlerThread.start();
|
||||
mHandler = new Handler(handlerThread.getLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (msg.what == MESSAGE_REPORT) {
|
||||
report();
|
||||
|
||||
if (isEnableObservable() || !mCacheEvents.isEmpty()) {
|
||||
mHandler.sendEmptyMessageDelayed(MESSAGE_REPORT, mHttpDnsConfig.getObservableConfig().batchReportIntervalTime * DateUtils.SECOND_IN_MILLIS);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void positiveEnableObservable(boolean enable) {
|
||||
mPositiveEnableObservable = enable;
|
||||
}
|
||||
|
||||
private boolean isEnableReport() {
|
||||
return mPositiveEnableObservable && !TextUtils.isEmpty(mHttpDnsConfig.getObservableConfig().endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否开启可观测,如果关闭,则打点和上报都关闭
|
||||
* @return
|
||||
*/
|
||||
private boolean isEnableObservable() {
|
||||
return mPositiveEnableObservable && mHttpDnsConfig.getObservableConfig().enable
|
||||
&& mHttpDnsConfig.getObservableSampleBenchMarks() < mHttpDnsConfig.getObservableConfig().sampleRatio
|
||||
&& !mReportingRateException;
|
||||
}
|
||||
|
||||
public void addObservableEvent(final ObservableEvent event) {
|
||||
if (!isEnableObservable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event instanceof LocalDnsEvent) {
|
||||
//需要local dns的event,在这里统一执行local dns解析
|
||||
try {
|
||||
sWorker.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
((LocalDnsEvent) event).localDns(event.getHostName());
|
||||
addObservableEventInner(event);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
//线程池异常,也要添加事件
|
||||
addObservableEventInner(event);
|
||||
}
|
||||
} else {
|
||||
addObservableEventInner(event);
|
||||
}
|
||||
}
|
||||
|
||||
private void addObservableEventInner(final ObservableEvent event) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (event instanceof GroupEvent) {
|
||||
ObservableEvent temp;
|
||||
GroupEvent targetGroupEvent = null;
|
||||
for (int i= 0; i != mCacheEvents.size(); ++i) {
|
||||
temp = mCacheEvents.get(i);
|
||||
if (temp instanceof GroupEvent) {
|
||||
if (((GroupEvent) temp).isSameGroup(event)) {
|
||||
targetGroupEvent = (GroupEvent) temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetGroupEvent != null) {
|
||||
targetGroupEvent.groupWith(event);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("group event: " + targetGroupEvent.toString());
|
||||
}
|
||||
} else {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("add group event: " + event.toString());
|
||||
}
|
||||
addEventInner(event);
|
||||
tryReport();
|
||||
}
|
||||
} else {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("add event: " + event.toString());
|
||||
}
|
||||
addEventInner(event);
|
||||
tryReport();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (initMessage.compareAndSet(false, true)) {
|
||||
mHandler.sendEmptyMessageDelayed(MESSAGE_REPORT, mHttpDnsConfig.getObservableConfig().batchReportIntervalTime * DateUtils.SECOND_IN_MILLIS);
|
||||
}
|
||||
}
|
||||
|
||||
private void addEventInner(ObservableEvent event) {
|
||||
mCacheEvents.add(event);
|
||||
|
||||
if (mCacheEvents.size() >= 400) {
|
||||
ReportingRateExceptionEvent reportingRateExceptionEvent = new ReportingRateExceptionEvent();
|
||||
reportingRateExceptionEvent.setStatusCode(200);
|
||||
|
||||
Map<Integer, Integer> eventNumMap = new HashMap<>();
|
||||
for (ObservableEvent event1 : mCacheEvents) {
|
||||
Integer num = eventNumMap.get(event1.getEventName());
|
||||
if (num == null) {
|
||||
num = 0;
|
||||
}
|
||||
|
||||
eventNumMap.put(event1.getEventName(), num + 1);
|
||||
}
|
||||
|
||||
int eventName = -1;
|
||||
int tmpNum = 0;
|
||||
for (Map.Entry<Integer, Integer> entry : eventNumMap.entrySet()) {
|
||||
if (entry.getValue() > tmpNum) {
|
||||
tmpNum = entry.getValue();
|
||||
eventName = entry.getKey();
|
||||
}
|
||||
}
|
||||
reportingRateExceptionEvent.setTag(eventName);
|
||||
|
||||
addObservableEvent(reportingRateExceptionEvent);
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.w("observable report rate exception.");
|
||||
}
|
||||
//关闭日志采集
|
||||
mReportingRateException = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void tryReport() {
|
||||
if (mCacheEvents.size() >= mHttpDnsConfig.getObservableConfig().batchReportMaxSize) {
|
||||
//达到上报数量
|
||||
mHandler.removeMessages(MESSAGE_REPORT);
|
||||
mHandler.sendEmptyMessage(MESSAGE_REPORT);
|
||||
}
|
||||
}
|
||||
|
||||
private void report() {
|
||||
if (TextUtils.isEmpty(mHttpDnsConfig.getObservableConfig().endpoint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCacheEvents.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isEnableReport()) {
|
||||
mCacheEvents.clear();
|
||||
++mReportBatchIndex;
|
||||
return;
|
||||
}
|
||||
|
||||
//上报流量控制判断
|
||||
long current = System.currentTimeMillis();
|
||||
if (reachingReportingLimit(current)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mReporting.compareAndSet(false, true)) {
|
||||
String body = buildReportBody();
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("observable: " + body);
|
||||
}
|
||||
|
||||
int num = Math.min(mHttpDnsConfig.getObservableConfig().batchReportMaxSize, mCacheEvents.size());
|
||||
for (int i = 0; i != num; ++i) {
|
||||
mCacheEvents.remove(0);
|
||||
}
|
||||
|
||||
Map<String, String> headers = getHeader(mReportBatchIndex++);
|
||||
|
||||
try {
|
||||
String url = getUrl(body);
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//添加上报时间点,用于上报流量控制
|
||||
mReportsTime.addLast(current);
|
||||
|
||||
HttpRequest<String> request = new ObservableHttpRequest<>(url, new ResponseParser<String>() {
|
||||
@Override
|
||||
public String parse(String serverIp, String response) throws Throwable {
|
||||
return response;
|
||||
}
|
||||
}, headers, body);
|
||||
// 重试2次
|
||||
request = new RetryHttpRequest<>(request, 2);
|
||||
|
||||
mReportWorker.execute(new HttpRequestTask<>(request, new RequestCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(String response) {
|
||||
mReporting.set(false);
|
||||
mHandler.removeMessages(MESSAGE_REPORT);
|
||||
mHandler.sendEmptyMessage(MESSAGE_REPORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFail(Throwable throwable) {
|
||||
mReporting.set(false);
|
||||
}
|
||||
}));
|
||||
} catch (Throwable e) {
|
||||
mReporting.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String buildReportBody() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int num = Math.min(mHttpDnsConfig.getObservableConfig().batchReportMaxSize, mCacheEvents.size());
|
||||
for (int i = 0; i != num; ++i) {
|
||||
sb.append(mCacheEvents.get(i).toString());
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String getUrl(String content) throws UnsupportedEncodingException, NoSuchAlgorithmException {
|
||||
ObservableConfig config = mHttpDnsConfig.getObservableConfig();
|
||||
String url = config.endpoint;
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(url) && (!url.startsWith("http://") && !url.startsWith("https://"))) {
|
||||
url = "https://" + url;
|
||||
}
|
||||
if (!url.endsWith("/")) {
|
||||
url += "/";
|
||||
}
|
||||
|
||||
url += mHttpDnsConfig.getAccountId() + "/metrics";
|
||||
|
||||
long time = System.currentTimeMillis();
|
||||
url += "?t=" + time;
|
||||
|
||||
url += "&ssk=" + (TextUtils.isEmpty(mSecret) ? 0 : 1);
|
||||
//tags
|
||||
url += ResolveHostHelper.getTags(mHttpDnsConfig);
|
||||
|
||||
String sign = md5(md5(content) + "-" + (TextUtils.isEmpty(mSecret) ? mHttpDnsConfig.getAccountId() : mSecret) + "-" + time);
|
||||
url += "&s=" + sign;
|
||||
return url;
|
||||
}
|
||||
|
||||
private Map<String, String> getHeader(int reportBatchIndex) {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("User-Agent", mHttpDnsConfig.getUA());
|
||||
headers.put("X-HTTPDNS-REQUEST-ID", SessionTrackMgr.getInstance().getSessionId() + "-" + reportBatchIndex);
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
private String md5(String content) throws NoSuchAlgorithmException, UnsupportedEncodingException {
|
||||
byte[] hash;
|
||||
hash = MessageDigest.getInstance("MD5").digest(content.getBytes("UTF-8"));
|
||||
|
||||
StringBuilder sb = new StringBuilder(hash.length * 2);
|
||||
for (byte aByte : hash) {
|
||||
sb.append(hexChar[(aByte & 0xf0) >>> 4]);
|
||||
sb.append(hexChar[aByte & 0x0f]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private float generateSampleBenchMarks() {
|
||||
Random random = new Random(System.currentTimeMillis());
|
||||
return random.nextFloat();
|
||||
}
|
||||
|
||||
private boolean reachingReportingLimit(long current) {
|
||||
int limit = mHttpDnsConfig.getObservableConfig().maxReportsPerMinute;
|
||||
|
||||
if (mReportsTime.size() < limit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//先移除超过每分钟上报次数的上报时间点
|
||||
while (mReportsTime.size() > limit) {
|
||||
mReportsTime.pollFirst();
|
||||
}
|
||||
|
||||
Long first = mReportsTime.peekFirst();
|
||||
if (first == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return current - first < DateUtils.MINUTE_IN_MILLIS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.alibaba.sdk.android.httpdns.observable.event;
|
||||
|
||||
public class BatchQueryHttpDnsApiEvent extends QueryHttpDnsApiEvent {
|
||||
|
||||
public BatchQueryHttpDnsApiEvent() {
|
||||
super();
|
||||
mEventName = 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHostName(String hostName) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHttpDnsIps(String[] ipv4s, String[] ipv6s) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocalDnsIps(String localDnsIps) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.alibaba.sdk.android.httpdns.observable.event;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableConstants;
|
||||
|
||||
public class CallSdkApiEvent extends ObservableEvent implements GroupEvent, LocalDnsEvent {
|
||||
|
||||
public CallSdkApiEvent(long startTime) {
|
||||
super();
|
||||
mTimestamp = startTime;
|
||||
mEventName = 5;
|
||||
mCount = 1;
|
||||
}
|
||||
|
||||
public void setRequestType(RequestIpType queryType) {
|
||||
if (queryType == RequestIpType.v4) {
|
||||
mTag |= ObservableConstants.REQUEST_IP_TYPE_V4;
|
||||
} else if (queryType == RequestIpType.v6) {
|
||||
mTag |= ObservableConstants.REQUEST_IP_TYPE_V6;
|
||||
} else if (queryType == RequestIpType.both) {
|
||||
mTag |= ObservableConstants.REQUEST_IP_TYPE_BOTH;
|
||||
}
|
||||
}
|
||||
|
||||
public void setInvokeApi(int api) {
|
||||
mTag |= api;
|
||||
}
|
||||
|
||||
public void setCacheScene(int scene) {
|
||||
mTag |= scene;
|
||||
}
|
||||
|
||||
public void setResultStatus(int status) {
|
||||
mTag |= status;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
mTimestamp = timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorCode(String errorCode) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
private void incrementCount() {
|
||||
++mCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameGroup(ObservableEvent event) {
|
||||
if (event == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return TextUtils.equals(getHostName(), event.getHostName())
|
||||
&& getTag() == event.getTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void groupWith(ObservableEvent event) {
|
||||
setTimestamp(event.getTimestamp());
|
||||
mServerIp = event.getServerIp();
|
||||
int totalCostTime = getCostTime() * getCount();
|
||||
incrementCount();
|
||||
setCostTime((totalCostTime + event.getCostTime()) / getCount());
|
||||
if (!TextUtils.isEmpty(event.getHttpDnsIps())) {
|
||||
mHttpDnsIps = event.getHttpDnsIps();
|
||||
}
|
||||
if (!TextUtils.isEmpty(event.getLocalDnsIps())) {
|
||||
mLocalDnsIps = event.getLocalDnsIps();
|
||||
mLocalDnsCost = event.getLocalDnsCost();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRequestIpType() {
|
||||
return mTag;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.alibaba.sdk.android.httpdns.observable.event;
|
||||
|
||||
public class CleanHostCacheEvent extends ObservableEvent {
|
||||
|
||||
public CleanHostCacheEvent() {
|
||||
super();
|
||||
mEventName = 6;
|
||||
mCount = 1;
|
||||
}
|
||||
|
||||
public void setTag(int cleanType) {
|
||||
mTag = cleanType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHostName(String hostName) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServerIp(String serverIp) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIpType(int type) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorCode(String errorCode) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHttpDnsIps(String[] ipv4s, String[] ipv6s) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocalDnsIps(String localDnsIps) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.alibaba.sdk.android.httpdns.observable.event;
|
||||
|
||||
/**
|
||||
* 需要聚合的事件
|
||||
*/
|
||||
public interface GroupEvent {
|
||||
boolean isSameGroup(ObservableEvent event);
|
||||
|
||||
void groupWith(ObservableEvent event);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.alibaba.sdk.android.httpdns.observable.event;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableConstants;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public interface LocalDnsEvent {
|
||||
|
||||
default void localDns(String host) {
|
||||
try {
|
||||
long localDnsStart = System.currentTimeMillis();
|
||||
InetAddress[] addresses = InetAddress.getAllByName(host);
|
||||
long localDnsCost = System.currentTimeMillis() - localDnsStart;
|
||||
if (addresses != null && addresses.length > 0) {
|
||||
List<String> ips = new ArrayList<>();
|
||||
List<String> ipv6s = new ArrayList<>();
|
||||
for (InetAddress address : addresses) {
|
||||
if (address instanceof Inet4Address) {
|
||||
ips.add(address.getHostAddress());
|
||||
} else if (address instanceof Inet6Address) {
|
||||
ipv6s.add(address.getHostAddress());
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int ipType = getRequestIpType();
|
||||
if ((ipType & ObservableConstants.REQUEST_IP_TYPE_V4) == ObservableConstants.REQUEST_IP_TYPE_V4) {
|
||||
for (int i = 0; i != ips.size(); ++i) {
|
||||
sb.append(ips.get(i));
|
||||
if (i != ips.size() - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((ipType & ObservableConstants.REQUEST_IP_TYPE_V6) ==ObservableConstants.REQUEST_IP_TYPE_V6) {
|
||||
if (!ipv6s.isEmpty() && sb.length() > 0) {
|
||||
sb.append(";");
|
||||
}
|
||||
for (int i = 0; i != ipv6s.size(); ++i) {
|
||||
sb.append(ipv6s.get(i));
|
||||
if (i != ipv6s.size() - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this instanceof ObservableEvent) {
|
||||
((ObservableEvent) this).setLocalDnsIps(sb.toString());
|
||||
((ObservableEvent) this).setLocalDnsCost(localDnsCost);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回值和tag中的请求类型保持一致
|
||||
* @return
|
||||
*/
|
||||
abstract int getRequestIpType();
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package com.alibaba.sdk.android.httpdns.observable.event;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsServiceImpl;
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableConstants;
|
||||
import com.alibaba.sdk.android.httpdns.utils.NetworkUtil;
|
||||
|
||||
public abstract class ObservableEvent {
|
||||
private static final String DIVIDER = "|";
|
||||
protected int mEventName;
|
||||
protected int mTag;
|
||||
protected String mHostName;
|
||||
protected long mTimestamp;
|
||||
protected String mServerIp;
|
||||
protected int mCostTime;
|
||||
private String mIpType;
|
||||
protected int mStatusCode;
|
||||
protected String mErrorCode;
|
||||
protected int mCount;
|
||||
private String mNetworkType;
|
||||
protected String mHttpDnsIps;
|
||||
protected String mLocalDnsIps;
|
||||
protected long mLocalDnsCost;
|
||||
|
||||
protected ObservableEvent() {
|
||||
mTimestamp = System.currentTimeMillis();
|
||||
mNetworkType = String.valueOf(NetworkUtil.getNetworkType(HttpDnsServiceImpl.sContext));
|
||||
}
|
||||
|
||||
public int getEventName() {
|
||||
return mEventName;
|
||||
}
|
||||
|
||||
public int getTag() {
|
||||
return mTag;
|
||||
}
|
||||
|
||||
public void setHostName(String hostName) {
|
||||
mHostName = hostName;
|
||||
}
|
||||
|
||||
public String getHostName() {
|
||||
return mHostName;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
public void setServerIp(String serverIp) {
|
||||
mServerIp = serverIp;
|
||||
}
|
||||
|
||||
public String getServerIp() {
|
||||
return mServerIp;
|
||||
}
|
||||
|
||||
public void setCostTime(int time) {
|
||||
mCostTime = time;
|
||||
}
|
||||
|
||||
public int getCostTime() {
|
||||
return mCostTime;
|
||||
}
|
||||
|
||||
public void setIpType(int type) {
|
||||
mIpType = String.valueOf(type);
|
||||
}
|
||||
|
||||
public int getIpType() {
|
||||
return Integer.parseInt(mIpType);
|
||||
}
|
||||
|
||||
public void setStatusCode(int statusCode) {
|
||||
mStatusCode = statusCode;
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
return mStatusCode;
|
||||
}
|
||||
|
||||
public void setErrorCode(String errorCode) {
|
||||
if (TextUtils.isEmpty(errorCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (errorCode.length() > 128) {
|
||||
mErrorCode = errorCode.substring(0, 128);
|
||||
} else {
|
||||
mErrorCode = errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
public String getErrorCode() {
|
||||
return mErrorCode;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
public void setHttpDnsIps(String[] ipv4s, String[] ipv6s) {
|
||||
int ipType = 0;
|
||||
if (ipv4s != null && ipv4s.length > 0) {
|
||||
ipType |= ObservableConstants.REQUEST_IP_TYPE_V4;
|
||||
}
|
||||
|
||||
if (ipv6s != null && ipv6s.length > 0) {
|
||||
ipType |= ObservableConstants.REQUEST_IP_TYPE_V6;
|
||||
}
|
||||
|
||||
setIpType(ipType);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (ipv4s != null) {
|
||||
for (int i = 0; i != ipv4s.length; ++i) {
|
||||
sb.append(ipv4s[i]);
|
||||
if (i != ipv4s.length - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ipv6s != null) {
|
||||
if (ipv6s.length != 0 && sb.length() > 0) {
|
||||
sb.append(";");
|
||||
}
|
||||
for (int i = 0; i != ipv6s.length; ++i) {
|
||||
sb.append(ipv6s[i]);
|
||||
if (i != ipv6s.length - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mHttpDnsIps = sb.toString();
|
||||
}
|
||||
|
||||
public String getHttpDnsIps() {
|
||||
return mHttpDnsIps;
|
||||
}
|
||||
|
||||
public void setLocalDnsIps(String localDnsIps) {
|
||||
mLocalDnsIps = localDnsIps;
|
||||
}
|
||||
|
||||
public String getLocalDnsIps() {
|
||||
return mLocalDnsIps;
|
||||
}
|
||||
|
||||
public void setLocalDnsCost(long localDnsCost) {
|
||||
mLocalDnsCost = localDnsCost;
|
||||
}
|
||||
|
||||
public long getLocalDnsCost() {
|
||||
return mLocalDnsCost;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return mEventName +
|
||||
DIVIDER + mTag +
|
||||
DIVIDER + (TextUtils.isEmpty(mHostName) ? "" : mHostName) +
|
||||
DIVIDER + mTimestamp +
|
||||
DIVIDER + (TextUtils.isEmpty(mServerIp) ? "" : mServerIp) +
|
||||
DIVIDER + mCostTime +
|
||||
DIVIDER + (TextUtils.isEmpty(mNetworkType) ? "" : mNetworkType) + //预留的网络类型
|
||||
DIVIDER + ((TextUtils.isEmpty(mIpType)) ? "" : mIpType) +
|
||||
DIVIDER + mStatusCode +
|
||||
DIVIDER + (TextUtils.isEmpty(mErrorCode) ? "" : mErrorCode) +
|
||||
DIVIDER + mCount +
|
||||
DIVIDER + (TextUtils.isEmpty(mHttpDnsIps) ? "" : mHttpDnsIps) +
|
||||
DIVIDER + (TextUtils.isEmpty(mLocalDnsIps) ? "" : mLocalDnsIps) +
|
||||
DIVIDER + mLocalDnsCost;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.alibaba.sdk.android.httpdns.observable.event;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableConstants;
|
||||
|
||||
public class QueryHttpDnsApiEvent extends ObservableEvent implements LocalDnsEvent {
|
||||
public QueryHttpDnsApiEvent() {
|
||||
super();
|
||||
mEventName = 3;
|
||||
mCount = 1;
|
||||
}
|
||||
|
||||
public void setTag(boolean isRetry, RequestIpType queryType, boolean http, boolean signMode) {
|
||||
mTag = isRetry ? ObservableConstants.REQUEST_RETRY : ObservableConstants.REQUEST_NOT_RETRY;
|
||||
if (queryType == RequestIpType.v4) {
|
||||
mTag |= ObservableConstants.REQUEST_IP_TYPE_V4;
|
||||
} else if (queryType == RequestIpType.v6) {
|
||||
mTag |= ObservableConstants.REQUEST_IP_TYPE_V6;
|
||||
} else if (queryType == RequestIpType.both) {
|
||||
mTag |= ObservableConstants.REQUEST_IP_TYPE_BOTH;
|
||||
}
|
||||
|
||||
if (!http) {
|
||||
mTag |= ObservableConstants.HTTPS_REQUEST;
|
||||
}
|
||||
|
||||
if (signMode) {
|
||||
mTag |= ObservableConstants.SIGN_MODE_REQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRequestIpType() {
|
||||
return mTag;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.alibaba.sdk.android.httpdns.observable.event;
|
||||
|
||||
public class ReportingRateExceptionEvent extends ObservableEvent {
|
||||
|
||||
public ReportingRateExceptionEvent() {
|
||||
super();
|
||||
mEventName = 8;
|
||||
mCount = 1;
|
||||
}
|
||||
|
||||
public void setTag(int eventName) {
|
||||
mTag = eventName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHostName(String hostName) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCostTime(int time) {
|
||||
mCostTime = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIpType(int type) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorCode(String errorCode) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHttpDnsIps(String[] ipv4s, String[] ipv6s) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocalDnsIps(String localDnsIps) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.alibaba.sdk.android.httpdns.observable.event;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
public class UpdateRegionServerIpsEvent extends ObservableEvent {
|
||||
|
||||
public UpdateRegionServerIpsEvent() {
|
||||
super();
|
||||
mEventName = 7;
|
||||
mCount = 1;
|
||||
}
|
||||
|
||||
public void setTag(int scenes) {
|
||||
if (scenes == Constants.UPDATE_REGION_SERVER_SCENES_INIT) {
|
||||
mTag = 3;
|
||||
} else if (scenes == Constants.UPDATE_REGION_SERVER_SCENES_REGION_CHANGE) {
|
||||
mTag = 2;
|
||||
} else if (scenes == Constants.UPDATE_REGION_SERVER_SCENES_SERVER_UNAVAILABLE) {
|
||||
mTag = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHostName(String hostName) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServerIp(String serverIp) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIpType(int type) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorCode(String errorCode) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHttpDnsIps(String[] ipv4s, String[] ipv6s) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocalDnsIps(String localDnsIps) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.alibaba.sdk.android.httpdns.ranking;
|
||||
|
||||
/**
|
||||
* IP优选配置项
|
||||
*/
|
||||
public class IPRankingBean {
|
||||
/**
|
||||
* 进行ip优选的域名
|
||||
*/
|
||||
String hostName;
|
||||
/**
|
||||
* 用于测试速度的端口
|
||||
*/
|
||||
int port;
|
||||
|
||||
public IPRankingBean(String hostName, int port) {
|
||||
this.hostName = hostName;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getHostName() {
|
||||
return hostName;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
IPRankingBean that = (IPRankingBean)o;
|
||||
return port == that.port && hostName != null && hostName.equals(that.hostName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 31 * hash + (hostName == null ? 0 : hostName.hashCode());
|
||||
hash = 31 * hash + port;
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.alibaba.sdk.android.httpdns.ranking;
|
||||
|
||||
/**
|
||||
* IP优选的结果回调
|
||||
*/
|
||||
public interface IPRankingCallback {
|
||||
void onResult(String host, String[] sortedIps);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.alibaba.sdk.android.httpdns.ranking;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsConfig;
|
||||
|
||||
/**
|
||||
* IP优选服务
|
||||
*/
|
||||
public class IPRankingService {
|
||||
|
||||
private final HttpDnsConfig mHttpDnsConfig;
|
||||
private List<IPRankingBean> mIPRankingBeanList;
|
||||
private IPRankingTask.SpeedTestSocketFactory mSocketFactory
|
||||
= new IPRankingTask.SpeedTestSocketFactory() {
|
||||
@Override
|
||||
public Socket create() {
|
||||
return new Socket();
|
||||
}
|
||||
};
|
||||
private final ConcurrentSkipListSet<String> mProbingHosts = new ConcurrentSkipListSet<>();
|
||||
|
||||
public IPRankingService(HttpDnsConfig config) {
|
||||
this.mHttpDnsConfig = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 进行ipv4优选
|
||||
*/
|
||||
public void probeIpv4(String host, String[] ips, final IPRankingCallback IPRankingCallback) {
|
||||
if (mHttpDnsConfig.isIPRankingDisabled()) {
|
||||
return;
|
||||
}
|
||||
IPRankingBean ipRankingBean = getIPRankingBean(host);
|
||||
if (ipRankingBean != null && ips != null && ips.length > 1) {
|
||||
if (mProbingHosts.contains(host)) {
|
||||
return;
|
||||
}
|
||||
mProbingHosts.add(host);
|
||||
try {
|
||||
mHttpDnsConfig.getWorker().execute(
|
||||
new IPRankingTask(mSocketFactory, host, ips, ipRankingBean, new IPRankingCallback() {
|
||||
@Override
|
||||
public void onResult(String host, String[] sortedIps) {
|
||||
mProbingHosts.remove(host);
|
||||
if (IPRankingCallback != null) {
|
||||
IPRankingCallback.onResult(host, sortedIps);
|
||||
}
|
||||
}
|
||||
}));
|
||||
} catch (Exception e) {
|
||||
mProbingHosts.remove(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setIPRankingList(List<IPRankingBean> ipRankingBeanList) {
|
||||
this.mIPRankingBeanList = ipRankingBeanList;
|
||||
}
|
||||
|
||||
public void setSocketFactory(IPRankingTask.SpeedTestSocketFactory socketFactory) {
|
||||
this.mSocketFactory = socketFactory;
|
||||
}
|
||||
|
||||
private IPRankingBean getIPRankingBean(String host) {
|
||||
if (mIPRankingBeanList != null && mIPRankingBeanList.size() > 0) {
|
||||
ArrayList<IPRankingBean> list = new ArrayList<>(mIPRankingBeanList);
|
||||
for (IPRankingBean item : list) {
|
||||
if (host.equals(item.getHostName())) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.alibaba.sdk.android.httpdns.ranking;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.utils.CommonUtil;
|
||||
|
||||
/**
|
||||
* ip优选实现
|
||||
*/
|
||||
public class IPRankingTask implements Runnable {
|
||||
|
||||
public interface SpeedTestSocketFactory {
|
||||
Socket create();
|
||||
}
|
||||
|
||||
private final SpeedTestSocketFactory mSocketFactory;
|
||||
private final String mHost;
|
||||
private final String[] mIps;
|
||||
private final IPRankingBean mIPRankingBean;
|
||||
private final IPRankingCallback mIPRankingCallback;
|
||||
|
||||
public IPRankingTask(SpeedTestSocketFactory socketFactory, String host, String[] ips,
|
||||
IPRankingBean ipRankingBean, IPRankingCallback IPRankingCallback) {
|
||||
this.mSocketFactory = socketFactory;
|
||||
this.mHost = host;
|
||||
this.mIps = ips;
|
||||
this.mIPRankingBean = ipRankingBean;
|
||||
this.mIPRankingCallback = IPRankingCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
int[] speeds = new int[mIps.length];
|
||||
for (int i = 0; i < mIps.length; i++) {
|
||||
speeds[i] = testConnectSpeed(mIps[i], mIPRankingBean.getPort());
|
||||
}
|
||||
String[] result = CommonUtil.sortIpsWithSpeeds(mIps, speeds);
|
||||
if (mIPRankingCallback != null) {
|
||||
mIPRankingCallback.onResult(mHost, result);
|
||||
}
|
||||
}
|
||||
|
||||
private int testConnectSpeed(String ip, int port) {
|
||||
long start = System.currentTimeMillis();
|
||||
long end = Long.MAX_VALUE;
|
||||
try (Socket socket = mSocketFactory.create()) {
|
||||
SocketAddress remoteAddress = new InetSocketAddress(ip, port);
|
||||
|
||||
socket.connect(remoteAddress, 5 * 1000);
|
||||
end = System.currentTimeMillis();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (end == Long.MAX_VALUE) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
return (int) (end - start);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
/**
|
||||
* 数据请求转异步
|
||||
*/
|
||||
public abstract class AsyncRequestTask<T> implements Runnable {
|
||||
|
||||
private final RequestCallback<T> mCallback;
|
||||
|
||||
public AsyncRequestTask(RequestCallback<T> callback) {
|
||||
this.mCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求数据,不需要直接调用,除非想要同步获取请求数据
|
||||
*/
|
||||
public abstract T request() throws Throwable;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
T t = request();
|
||||
if (mCallback != null) {
|
||||
mCallback.onSuccess(t);
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
if (mCallback != null) {
|
||||
mCallback.onFail(throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableConstants;
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableManager;
|
||||
import com.alibaba.sdk.android.httpdns.observable.event.BatchQueryHttpDnsApiEvent;
|
||||
import com.alibaba.sdk.android.httpdns.resolve.ResolveHostResponse;
|
||||
|
||||
public class BatchResolveHttpRequestStatusWatcher implements HttpRequestWatcher.Watcher {
|
||||
|
||||
ObservableManager observableManager;
|
||||
|
||||
private BatchQueryHttpDnsApiEvent mBatchQueryHttpDnsApiEvent;
|
||||
|
||||
public BatchResolveHttpRequestStatusWatcher(ObservableManager observableManager) {
|
||||
this.observableManager = observableManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(HttpRequestConfig config) {
|
||||
mBatchQueryHttpDnsApiEvent = new BatchQueryHttpDnsApiEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(HttpRequestConfig config, Object data) {
|
||||
if (mBatchQueryHttpDnsApiEvent != null && observableManager != null) {
|
||||
mBatchQueryHttpDnsApiEvent.setTag(config.isRetry(), config.getResolvingIpType(),
|
||||
HttpRequestConfig.HTTP_SCHEMA.equals(config.getSchema()), config.isSignMode());
|
||||
mBatchQueryHttpDnsApiEvent.setServerIp(config.getIp());
|
||||
mBatchQueryHttpDnsApiEvent.setCostTime((int) (System.currentTimeMillis() - mBatchQueryHttpDnsApiEvent.getTimestamp()));
|
||||
|
||||
int resultIpType = 0x00;
|
||||
if (data instanceof ResolveHostResponse) {
|
||||
for (ResolveHostResponse.HostItem item : ((ResolveHostResponse) data).getItems()) {
|
||||
if (item.getIpType() == RequestIpType.v4) {
|
||||
resultIpType |= ObservableConstants.REQUEST_IP_TYPE_V4;
|
||||
} else if (item.getIpType() == RequestIpType.v6) {
|
||||
resultIpType |= ObservableConstants.REQUEST_IP_TYPE_V6;
|
||||
}
|
||||
}
|
||||
}
|
||||
mBatchQueryHttpDnsApiEvent.setIpType(resultIpType);
|
||||
mBatchQueryHttpDnsApiEvent.setStatusCode(resultIpType == 0x00 ? 204 : 200);
|
||||
|
||||
observableManager.addObservableEvent(mBatchQueryHttpDnsApiEvent);
|
||||
mBatchQueryHttpDnsApiEvent = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFail(HttpRequestConfig config, Throwable throwable) {
|
||||
if (mBatchQueryHttpDnsApiEvent != null && observableManager != null) {
|
||||
mBatchQueryHttpDnsApiEvent.setTag(config.isRetry(), config.getResolvingIpType(),
|
||||
HttpRequestConfig.HTTP_SCHEMA.equals(config.getSchema()), config.isSignMode());
|
||||
mBatchQueryHttpDnsApiEvent.setServerIp(config.getIp());
|
||||
mBatchQueryHttpDnsApiEvent.setCostTime((int) (System.currentTimeMillis() - mBatchQueryHttpDnsApiEvent.getTimestamp()));
|
||||
mBatchQueryHttpDnsApiEvent.setIpType(0x00);
|
||||
if (throwable instanceof HttpException) {
|
||||
mBatchQueryHttpDnsApiEvent.setStatusCode(((HttpException) throwable).getCode());
|
||||
String errorCode = throwable.getMessage();
|
||||
mBatchQueryHttpDnsApiEvent.setErrorCode(TextUtils.isEmpty(errorCode) ? "Unknown" : errorCode);
|
||||
} else {
|
||||
mBatchQueryHttpDnsApiEvent.setStatusCode(-1);
|
||||
mBatchQueryHttpDnsApiEvent.setErrorCode(throwable.getClass().getSimpleName() + ":" + throwable.getMessage());
|
||||
}
|
||||
|
||||
observableManager.addObservableEvent(mBatchQueryHttpDnsApiEvent);
|
||||
mBatchQueryHttpDnsApiEvent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* 网络请求失败的异常
|
||||
*/
|
||||
public class HttpException extends Exception {
|
||||
public static final int ERROR_CODE_403 = 403;
|
||||
public static final int ERROR_CODE_400 = 400;
|
||||
public static final int ERROR_CODE_500 = 500;
|
||||
|
||||
/**
|
||||
* 用户服务level不匹配
|
||||
* (用户有不同的level,高level有一些专用的服务节点,如果一个低level的用户,向一个高level专用的服务节点请求,就会返回此错误)
|
||||
* <p>
|
||||
* 需要重试 切换服务IP
|
||||
*/
|
||||
public static final String ERROR_MSG_SERVICE_LEVEL_DENY = "ServiceLevelDeny";
|
||||
|
||||
/**
|
||||
* 未用签名访问
|
||||
* 不重试 不切换
|
||||
* 生成空解析 缓存1小时
|
||||
*/
|
||||
public static final String ERROR_MSG_UNSIGNED = "UnsignedInterfaceDisabled";
|
||||
|
||||
/**
|
||||
* 签名过期
|
||||
* 不重试 不切换 (sdk逻辑保证不应该过期)
|
||||
*/
|
||||
public static final String ERROR_MSG_SIGNATURE_EXPIRED = "SignatureExpired";
|
||||
|
||||
/**
|
||||
* 签名验证失败
|
||||
* 不重试 不切换
|
||||
*/
|
||||
public static final String ERROR_MSG_INVALID_SIGNATURE = "InvalidSignature";
|
||||
|
||||
/**
|
||||
* 账户服务level缺失
|
||||
* 不重试 不切换
|
||||
* 生成空解析 缓存1小时
|
||||
*/
|
||||
public static final String ERROR_MSG_INVALID_ACCOUNT = "InvalidAccount";
|
||||
|
||||
/**
|
||||
* 账户不存在或者禁用
|
||||
* 不重试 不切换
|
||||
* 生成空解析 缓存1小时
|
||||
*/
|
||||
public static final String ERROR_MSG_ACCOUNT_NOT_EXISTS = "AccountNotExists";
|
||||
|
||||
/**
|
||||
* 签名有效时间过长
|
||||
* 不重试 不切换
|
||||
*/
|
||||
public static final String ERROR_MSG_INVALID_DURATION = "InvalidDuration";
|
||||
|
||||
/**
|
||||
* 无效域名
|
||||
* 不重试 不切换
|
||||
*/
|
||||
public static final String ERROR_MSG_INVALID_HOST = "InvalidHost";
|
||||
|
||||
public static final String ERROR_MSG_TAG = "code";
|
||||
private final int mCode;
|
||||
|
||||
private HttpException(int code, String message) {
|
||||
super(message);
|
||||
this.mCode = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Http status code
|
||||
*/
|
||||
public int getCode() {
|
||||
return mCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建异常
|
||||
*
|
||||
* @param code
|
||||
* @param message 服务器返回的特定格式的数据
|
||||
* @return
|
||||
*/
|
||||
public static HttpException create(int code, String message) {
|
||||
return new HttpException(code, tryTranslateMessage(message));
|
||||
}
|
||||
|
||||
private static String tryTranslateMessage(String message) {
|
||||
JSONObject object;
|
||||
try {
|
||||
object = new JSONObject(message);
|
||||
return object.getString(ERROR_MSG_TAG);
|
||||
} catch (JSONException ignored) {
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {return true;}
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
HttpException that = (HttpException)o;
|
||||
return mCode == that.mCode && getMessage().equals(that.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(new Object[] {mCode, getMessage()});
|
||||
}
|
||||
|
||||
/**
|
||||
* 是不是要切换服务IP
|
||||
*/
|
||||
public boolean shouldShiftServer() {
|
||||
String msg = getMessage();
|
||||
return !ERROR_MSG_UNSIGNED.equals(msg)
|
||||
&& !ERROR_MSG_SIGNATURE_EXPIRED.equals(msg)
|
||||
&& !ERROR_MSG_INVALID_SIGNATURE.equals(msg)
|
||||
&& !ERROR_MSG_INVALID_ACCOUNT.equals(msg)
|
||||
&& !ERROR_MSG_ACCOUNT_NOT_EXISTS.equals(msg)
|
||||
&& !ERROR_MSG_INVALID_DURATION.equals(msg)
|
||||
&& !ERROR_MSG_INVALID_HOST.equals(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个错误是否建议重试
|
||||
*/
|
||||
public boolean shouldRetry() {
|
||||
String msg = getMessage();
|
||||
return !ERROR_MSG_UNSIGNED.equals(msg)
|
||||
&& !ERROR_MSG_SIGNATURE_EXPIRED.equals(msg)
|
||||
&& !ERROR_MSG_INVALID_SIGNATURE.equals(msg)
|
||||
&& !ERROR_MSG_INVALID_ACCOUNT.equals(msg)
|
||||
&& !ERROR_MSG_ACCOUNT_NOT_EXISTS.equals(msg)
|
||||
&& !ERROR_MSG_INVALID_DURATION.equals(msg)
|
||||
&& !ERROR_MSG_INVALID_HOST.equals(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个错误是否要创建空缓存
|
||||
*/
|
||||
public boolean shouldCreateEmptyCache() {
|
||||
String msg = getMessage();
|
||||
return ERROR_MSG_UNSIGNED.equals(msg)
|
||||
|| ERROR_MSG_INVALID_ACCOUNT.equals(msg)
|
||||
|| ERROR_MSG_ACCOUNT_NOT_EXISTS.equals(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
|
||||
/**
|
||||
* 网络请求
|
||||
*/
|
||||
public class HttpRequest<T> {
|
||||
private HttpRequestConfig requestConfig;
|
||||
private ResponseParser<T> translator;
|
||||
|
||||
protected HttpRequest() {
|
||||
}
|
||||
|
||||
public HttpRequest(HttpRequestConfig requestConfig, ResponseParser<T> translator) {
|
||||
this.requestConfig = requestConfig;
|
||||
this.translator = translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本次请求的配置
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public HttpRequestConfig getRequestConfig() {
|
||||
return requestConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起请求
|
||||
*
|
||||
* @return 返回的数据
|
||||
* @throws Throwable 发生的错误
|
||||
*/
|
||||
public T request() throws Throwable {
|
||||
HttpURLConnection conn = null;
|
||||
InputStream in = null;
|
||||
BufferedReader streamReader = null;
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
String serverIp = requestConfig.getIp();
|
||||
String url = requestConfig.url();
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("request url " + url);
|
||||
}
|
||||
try {
|
||||
conn = (HttpURLConnection)new URL(url).openConnection();
|
||||
conn.setReadTimeout(requestConfig.getTimeout());
|
||||
conn.setConnectTimeout(requestConfig.getTimeout());
|
||||
//设置通用UA
|
||||
conn.setRequestProperty("User-Agent", requestConfig.getUA());
|
||||
if (conn instanceof HttpsURLConnection) {
|
||||
((HttpsURLConnection)conn).setHostnameVerifier(new HostnameVerifier() {
|
||||
@Override
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return HttpsURLConnection.getDefaultHostnameVerifier().verify(
|
||||
HttpRequestConfig.HTTPS_CERTIFICATE_HOSTNAME, session);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
||||
in = conn.getErrorStream();
|
||||
if (in != null) {
|
||||
streamReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
||||
String errStr = readStringFrom(streamReader).toString();
|
||||
throw HttpException.create(conn.getResponseCode(), errStr);
|
||||
} else {
|
||||
throw HttpException.create(conn.getResponseCode(), "");
|
||||
}
|
||||
} else {
|
||||
in = conn.getInputStream();
|
||||
streamReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
||||
String responseStr = readStringFrom(streamReader).toString();
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("request success " + responseStr);
|
||||
}
|
||||
return translator.parse(serverIp, responseStr);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
HttpDnsLog.w("request " + url + " fail, cost " + cost, e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
try {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
if (streamReader != null) {
|
||||
streamReader.close();
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* stream to string
|
||||
*/
|
||||
public static StringBuilder readStringFrom(BufferedReader streamReader) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = streamReader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.impl.AESEncryptService;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
/**
|
||||
* 网络请求的配置
|
||||
*/
|
||||
public class HttpRequestConfig {
|
||||
public static final String HTTP_SCHEMA = "http://";
|
||||
public static final String HTTPS_SCHEMA = "https://";
|
||||
public static final String HTTPS_CERTIFICATE_HOSTNAME = "203.107.1.1";
|
||||
/**
|
||||
* 请求协议
|
||||
*/
|
||||
private String mSchema = HTTP_SCHEMA;
|
||||
/**
|
||||
* 服务器ip
|
||||
*/
|
||||
private String mIp;
|
||||
/**
|
||||
* 服务器端口
|
||||
*/
|
||||
private int mPort;
|
||||
/**
|
||||
* 请求路径,包含query参数
|
||||
*/
|
||||
private String mPath;
|
||||
/**
|
||||
* 请求超时时间
|
||||
*/
|
||||
private int mTimeout = Constants.DEFAULT_TIMEOUT;
|
||||
|
||||
/**
|
||||
* 服务器IP类型 只会是 v4 或者 v6
|
||||
*/
|
||||
private RequestIpType mIpType = RequestIpType.v4;
|
||||
|
||||
private String mUA = "";
|
||||
|
||||
//待解析的域名
|
||||
private String mResolvingHost;
|
||||
//待解析的域名的ip类型
|
||||
private RequestIpType mResolvingIpType;
|
||||
//是不是重试
|
||||
private boolean isRetry = false;
|
||||
|
||||
private boolean isSignMode = false;
|
||||
private AESEncryptService mAESEncryptService;
|
||||
|
||||
public HttpRequestConfig(String ip, int port, String path) {
|
||||
this.mIp = ip;
|
||||
this.mPort = port;
|
||||
this.mPath = path;
|
||||
}
|
||||
|
||||
public HttpRequestConfig(String schema, String ip, int port, String path, int timeout, RequestIpType ipType) {
|
||||
this.mSchema = schema;
|
||||
this.mIp = ip;
|
||||
this.mPort = port;
|
||||
this.mPath = path;
|
||||
this.mTimeout = timeout;
|
||||
this.mIpType = ipType;
|
||||
}
|
||||
|
||||
public HttpRequestConfig(String schema, String ip, int port, String path, int timeout, RequestIpType ipType, boolean signMode) {
|
||||
this.mSchema = schema;
|
||||
this.mIp = ip;
|
||||
this.mPort = port;
|
||||
this.mPath = path;
|
||||
this.mTimeout = timeout;
|
||||
this.mIpType = ipType;
|
||||
isSignMode = signMode;
|
||||
}
|
||||
|
||||
public void setSchema(String schema) {
|
||||
this.mSchema = schema;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.mIp = ip;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.mPort = port;
|
||||
}
|
||||
|
||||
public String getSchema() {
|
||||
return this.mSchema;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return mIp;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return mPort;
|
||||
}
|
||||
|
||||
public void setTimeout(int timeout) {
|
||||
this.mTimeout = timeout;
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
return mTimeout;
|
||||
}
|
||||
|
||||
public RequestIpType getIpType() {
|
||||
return mIpType;
|
||||
}
|
||||
|
||||
public String url() {
|
||||
if (mIpType == RequestIpType.v6) {
|
||||
//域名兜底,这里需要对域名做特殊处理
|
||||
if (!TextUtils.isEmpty(mIp) && mIp.contains(".")) {
|
||||
return mSchema + mIp + ":" + mPort + mPath;
|
||||
}
|
||||
return mSchema + "[" + mIp + "]" + ":" + mPort + mPath;
|
||||
} else {
|
||||
return mSchema + mIp + ":" + mPort + mPath;
|
||||
}
|
||||
}
|
||||
|
||||
public void setUA(String ua) {
|
||||
mUA = ua;
|
||||
}
|
||||
|
||||
public String getUA() {
|
||||
return mUA;
|
||||
}
|
||||
|
||||
public void setResolvingHost(String host) {
|
||||
mResolvingHost = host;
|
||||
}
|
||||
|
||||
public String getResolvingHost() {
|
||||
return mResolvingHost;
|
||||
}
|
||||
|
||||
public void setResolvingIpType(RequestIpType type) {
|
||||
mResolvingIpType = type;
|
||||
}
|
||||
|
||||
public RequestIpType getResolvingIpType() {
|
||||
return mResolvingIpType;
|
||||
}
|
||||
|
||||
public void setRetry() {
|
||||
isRetry = true;
|
||||
}
|
||||
public boolean isRetry() {
|
||||
return isRetry;
|
||||
}
|
||||
|
||||
public boolean isSignMode() {
|
||||
return isSignMode;
|
||||
}
|
||||
|
||||
public AESEncryptService getAESEncryptService() {
|
||||
return mAESEncryptService;
|
||||
}
|
||||
|
||||
public void setAESEncryptService(AESEncryptService mAESEncryptService) {
|
||||
this.mAESEncryptService = mAESEncryptService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableManager;
|
||||
|
||||
public class HttpRequestFailWatcher implements HttpRequestWatcher.Watcher {
|
||||
|
||||
public HttpRequestFailWatcher(ObservableManager reportManager) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(HttpRequestConfig config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(HttpRequestConfig config, Object data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFail(HttpRequestConfig config, Throwable throwable) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
/**
|
||||
* 网络请求的异步任务
|
||||
*/
|
||||
public class HttpRequestTask<T> extends AsyncRequestTask<T> {
|
||||
|
||||
private final HttpRequest<T> mHttpRequest;
|
||||
|
||||
public HttpRequestTask(HttpRequest<T> httpRequest, RequestCallback<T> callback) {
|
||||
super(callback);
|
||||
this.mHttpRequest = httpRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T request() throws Throwable {
|
||||
return mHttpRequest.request();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
/**
|
||||
* 监听网络请求,用于附加业务逻辑
|
||||
*/
|
||||
public class HttpRequestWatcher<T> extends HttpRequestWrapper<T> {
|
||||
|
||||
private final Watcher mWatcher;
|
||||
|
||||
public HttpRequestWatcher(HttpRequest<T> request, Watcher watcher) {
|
||||
super(request);
|
||||
this.mWatcher = watcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T request() throws Throwable {
|
||||
try {
|
||||
if (mWatcher != null) {
|
||||
try {
|
||||
mWatcher.onStart(getRequestConfig());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
T t = super.request();
|
||||
if (mWatcher != null) {
|
||||
try {
|
||||
mWatcher.onSuccess(getRequestConfig(), t);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return t;
|
||||
} catch (Throwable throwable) {
|
||||
if (mWatcher != null) {
|
||||
try {
|
||||
mWatcher.onFail(getRequestConfig(), throwable);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
throw throwable;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Watcher {
|
||||
void onStart(HttpRequestConfig config);
|
||||
|
||||
void onSuccess(HttpRequestConfig config, Object data);
|
||||
|
||||
void onFail(HttpRequestConfig config, Throwable throwable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
/**
|
||||
* 网络请求的包装类
|
||||
*/
|
||||
public class HttpRequestWrapper<T> extends HttpRequest<T> {
|
||||
|
||||
private final HttpRequest<T> mHttpRequest;
|
||||
|
||||
public HttpRequestWrapper(HttpRequest<T> request) {
|
||||
this.mHttpRequest = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequestConfig getRequestConfig() {
|
||||
return mHttpRequest.getRequestConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T request() throws Throwable {
|
||||
return mHttpRequest.request();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
/**
|
||||
* http请求结果回调
|
||||
*/
|
||||
public interface RequestCallback<T> {
|
||||
void onSuccess(T response);
|
||||
|
||||
void onFail(Throwable throwable);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
/**
|
||||
* http响应解析
|
||||
*/
|
||||
public interface ResponseParser<T> {
|
||||
T parse(String serverIp, String response) throws Throwable;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
/**
|
||||
* 失败时 重试请求
|
||||
*/
|
||||
public class RetryHttpRequest<T> extends HttpRequestWrapper<T> {
|
||||
|
||||
private int retryCount;
|
||||
|
||||
public RetryHttpRequest(HttpRequest<T> request, int retryCount) {
|
||||
super(request);
|
||||
this.retryCount = retryCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T request() throws Throwable {
|
||||
while (true) {
|
||||
try {
|
||||
return super.request();
|
||||
} catch (Throwable throwable) {
|
||||
if (shouldRetry(throwable)) {
|
||||
if (retryCount > 0) {
|
||||
//可观测需要
|
||||
getRequestConfig().setRetry();
|
||||
retryCount--;
|
||||
} else {
|
||||
throw throwable;
|
||||
}
|
||||
} else {
|
||||
throw throwable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldRetry(Throwable throwable) {
|
||||
if (throwable instanceof HttpException) {
|
||||
return ((HttpException)throwable).shouldRetry();
|
||||
} else {
|
||||
// 其它异常都可以重试
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableManager;
|
||||
import com.alibaba.sdk.android.httpdns.observable.event.QueryHttpDnsApiEvent;
|
||||
import com.alibaba.sdk.android.httpdns.resolve.ResolveHostResponse;
|
||||
|
||||
public class SingleResolveHttpRequestStatusWatcher implements HttpRequestWatcher.Watcher {
|
||||
private final ObservableManager observableManager;
|
||||
|
||||
private QueryHttpDnsApiEvent mQueryHttpDnsApiEvent = null;
|
||||
|
||||
public SingleResolveHttpRequestStatusWatcher(ObservableManager observableManager) {
|
||||
this.observableManager = observableManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(HttpRequestConfig config) {
|
||||
mQueryHttpDnsApiEvent = new QueryHttpDnsApiEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(HttpRequestConfig config, Object data) {
|
||||
if (mQueryHttpDnsApiEvent != null && observableManager != null) {
|
||||
mQueryHttpDnsApiEvent.setTag(config.isRetry(), config.getResolvingIpType(),
|
||||
HttpRequestConfig.HTTP_SCHEMA.equals(config.getSchema()), config.isSignMode());
|
||||
mQueryHttpDnsApiEvent.setHostName(config.getResolvingHost());
|
||||
mQueryHttpDnsApiEvent.setServerIp(config.getIp());
|
||||
mQueryHttpDnsApiEvent.setCostTime((int) (System.currentTimeMillis() - mQueryHttpDnsApiEvent.getTimestamp()));
|
||||
|
||||
int resultIpType = 0x00;
|
||||
if (data instanceof ResolveHostResponse) {
|
||||
ResolveHostResponse response = (ResolveHostResponse) data;
|
||||
String[] ips = null, ipv6s = null;
|
||||
for (ResolveHostResponse.HostItem item : response.getItems()) {
|
||||
if (item.getIpType() == RequestIpType.v4) {
|
||||
resultIpType |= 0x01;
|
||||
ips = item.getIps();
|
||||
} else if (item.getIpType() == RequestIpType.v6) {
|
||||
resultIpType |= 0x02;
|
||||
ipv6s = item.getIps();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
mQueryHttpDnsApiEvent.setHttpDnsIps(ips, ipv6s);
|
||||
}
|
||||
mQueryHttpDnsApiEvent.setIpType(resultIpType);
|
||||
mQueryHttpDnsApiEvent.setStatusCode(resultIpType == 0x00 ? 204 : 200);
|
||||
|
||||
observableManager.addObservableEvent(mQueryHttpDnsApiEvent);
|
||||
mQueryHttpDnsApiEvent = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFail(HttpRequestConfig config, Throwable throwable) {
|
||||
if (mQueryHttpDnsApiEvent != null && observableManager != null) {
|
||||
mQueryHttpDnsApiEvent.setTag(config.isRetry(), config.getResolvingIpType(),
|
||||
HttpRequestConfig.HTTP_SCHEMA.equals(config.getSchema()), config.isSignMode());
|
||||
mQueryHttpDnsApiEvent.setHostName(config.getResolvingHost());
|
||||
mQueryHttpDnsApiEvent.setServerIp(config.getIp());
|
||||
mQueryHttpDnsApiEvent.setCostTime((int) (System.currentTimeMillis() - mQueryHttpDnsApiEvent.getTimestamp()));
|
||||
mQueryHttpDnsApiEvent.setIpType(0x00);
|
||||
|
||||
if (throwable instanceof HttpException) {
|
||||
mQueryHttpDnsApiEvent.setStatusCode(((HttpException) throwable).getCode());
|
||||
String errorCode = throwable.getMessage();
|
||||
mQueryHttpDnsApiEvent.setErrorCode(TextUtils.isEmpty(errorCode) ? "Unknown" : errorCode);
|
||||
} else {
|
||||
mQueryHttpDnsApiEvent.setStatusCode(-1);
|
||||
mQueryHttpDnsApiEvent.setErrorCode(throwable.getClass().getSimpleName() + ":" + throwable.getMessage());
|
||||
}
|
||||
|
||||
observableManager.addObservableEvent(mQueryHttpDnsApiEvent);
|
||||
mQueryHttpDnsApiEvent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.alibaba.sdk.android.httpdns.request;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableManager;
|
||||
import com.alibaba.sdk.android.httpdns.observable.event.UpdateRegionServerIpsEvent;
|
||||
|
||||
public class UpdateRegionServerHttpRequestStatusWatcher implements HttpRequestWatcher.Watcher {
|
||||
private final ObservableManager observableManager;
|
||||
private int mScenes;
|
||||
private UpdateRegionServerIpsEvent mUpdateRegionServerIpsEvent;
|
||||
|
||||
public UpdateRegionServerHttpRequestStatusWatcher(int scenes, ObservableManager observableManager) {
|
||||
this.observableManager = observableManager;
|
||||
mScenes = scenes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(HttpRequestConfig config) {
|
||||
mUpdateRegionServerIpsEvent = new UpdateRegionServerIpsEvent();
|
||||
mUpdateRegionServerIpsEvent.setTag(mScenes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(HttpRequestConfig config, Object data) {
|
||||
if (mUpdateRegionServerIpsEvent != null && observableManager != null) {
|
||||
mUpdateRegionServerIpsEvent.setCostTime((int) (System.currentTimeMillis() - mUpdateRegionServerIpsEvent.getTimestamp()));
|
||||
mUpdateRegionServerIpsEvent.setStatusCode(200);
|
||||
|
||||
observableManager.addObservableEvent(mUpdateRegionServerIpsEvent);
|
||||
mUpdateRegionServerIpsEvent = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFail(HttpRequestConfig config, Throwable throwable) {
|
||||
if (mUpdateRegionServerIpsEvent != null && observableManager != null) {
|
||||
mUpdateRegionServerIpsEvent.setCostTime((int) (System.currentTimeMillis() - mUpdateRegionServerIpsEvent.getTimestamp()));
|
||||
if (throwable instanceof HttpException) {
|
||||
mUpdateRegionServerIpsEvent.setStatusCode(((HttpException) throwable).getCode());
|
||||
String errorCode = throwable.getMessage();
|
||||
mUpdateRegionServerIpsEvent.setErrorCode(TextUtils.isEmpty(errorCode) ? "Unknown" : errorCode);
|
||||
} else {
|
||||
mUpdateRegionServerIpsEvent.setStatusCode(-1);
|
||||
mUpdateRegionServerIpsEvent.setErrorCode(throwable.getClass().getSimpleName() + ":" + throwable.getMessage());
|
||||
}
|
||||
|
||||
observableManager.addObservableEvent(mUpdateRegionServerIpsEvent);
|
||||
mUpdateRegionServerIpsEvent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HTTPDNSResultWrapper;
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.impl.HostResolveLocker;
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsConfig;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.net.NetworkStateManager;
|
||||
import com.alibaba.sdk.android.httpdns.ranking.IPRankingCallback;
|
||||
import com.alibaba.sdk.android.httpdns.ranking.IPRankingService;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpException;
|
||||
import com.alibaba.sdk.android.httpdns.request.RequestCallback;
|
||||
import com.alibaba.sdk.android.httpdns.utils.CommonUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 批量解析域名
|
||||
*
|
||||
* @author zonglin.nzl
|
||||
* @date 2020/12/11
|
||||
*/
|
||||
public class BatchResolveHostService {
|
||||
private final HttpDnsConfig mHttpDnsConfig;
|
||||
private final ResolveHostResultRepo mResultRepo;
|
||||
private final ResolveHostRequestHandler mRequestHandler;
|
||||
private final IPRankingService mIpIPRankingService;
|
||||
private final HostFilter mHostFilter;
|
||||
private final HostResolveLocker mAsyncLocker;
|
||||
|
||||
public BatchResolveHostService(HttpDnsConfig config, ResolveHostResultRepo repo,
|
||||
ResolveHostRequestHandler requestHandler,
|
||||
IPRankingService ipIPRankingService, HostFilter filter,
|
||||
HostResolveLocker locker) {
|
||||
this.mHttpDnsConfig = config;
|
||||
this.mResultRepo = repo;
|
||||
this.mRequestHandler = requestHandler;
|
||||
this.mIpIPRankingService = ipIPRankingService;
|
||||
this.mHostFilter = filter;
|
||||
this.mAsyncLocker = locker;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量解析域名
|
||||
*
|
||||
* @param hostList 待解析域名列表
|
||||
* @param type 解析类型
|
||||
*/
|
||||
public void batchResolveHostAsync(final List<String> hostList, final RequestIpType type) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("batch resolve host " + hostList.toString() + " " + type);
|
||||
}
|
||||
|
||||
ArrayList<String> hostsRequestV4 = new ArrayList<>();
|
||||
ArrayList<String> hostsRequestV6 = new ArrayList<>();
|
||||
ArrayList<String> hostsRequestBoth = new ArrayList<>();
|
||||
|
||||
for (String host : hostList) {
|
||||
if (!CommonUtil.isAHost(host)
|
||||
|| CommonUtil.isAnIP(host)
|
||||
|| this.mHostFilter.isFiltered(host)) {
|
||||
// 过滤掉不需要的域名
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("resolve ignore host as not invalid " + host);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 过滤掉有缓存的域名
|
||||
String networkKey = NetworkStateManager.getInstance().getCurrentNetworkKey();
|
||||
if (type == RequestIpType.v4) {
|
||||
HTTPDNSResultWrapper result = mResultRepo.getIps(host, type, networkKey);
|
||||
if (result == null || result.isExpired()) {
|
||||
// 需要解析
|
||||
hostsRequestV4.add(host);
|
||||
}
|
||||
} else if (type == RequestIpType.v6) {
|
||||
HTTPDNSResultWrapper result = mResultRepo.getIps(host, type, networkKey);
|
||||
if (result == null || result.isExpired()) {
|
||||
// 需要解析
|
||||
hostsRequestV6.add(host);
|
||||
}
|
||||
} else {
|
||||
HTTPDNSResultWrapper resultV4 = mResultRepo.getIps(host, RequestIpType.v4, networkKey);
|
||||
HTTPDNSResultWrapper resultV6 = mResultRepo.getIps(host, RequestIpType.v6, networkKey);
|
||||
if ((resultV4 == null || resultV4.isExpired()) && (resultV6 == null || resultV6.isExpired())) {
|
||||
// 都需要解析
|
||||
hostsRequestBoth.add(host);
|
||||
} else if (resultV4 == null || resultV4.isExpired()) {
|
||||
hostsRequestV4.add(host);
|
||||
} else if (resultV6 == null || resultV6.isExpired()) {
|
||||
hostsRequestV6.add(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
batchResolveHost(hostsRequestV4, RequestIpType.v4);
|
||||
batchResolveHost(hostsRequestV6, RequestIpType.v6);
|
||||
batchResolveHost(hostsRequestBoth, RequestIpType.both);
|
||||
}
|
||||
|
||||
private void batchResolveHost(ArrayList<String> hostList, final RequestIpType type) {
|
||||
if (hostList == null || hostList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
ArrayList<String> allHosts = new ArrayList<>(hostList);
|
||||
// 预解析每次最多5个域名
|
||||
final int maxCountPerRequest = 5;
|
||||
int requestCount = (hostList.size() + maxCountPerRequest - 1) / maxCountPerRequest;
|
||||
for (int i = 0; i < requestCount; i++) {
|
||||
final ArrayList<String> targetHost = new ArrayList<>();
|
||||
while (targetHost.size() < maxCountPerRequest && allHosts.size() > 0) {
|
||||
String host = allHosts.remove(0);
|
||||
if (mAsyncLocker.beginResolve(host, type, null)) {
|
||||
targetHost.add(host);
|
||||
} else {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("resolve ignore host as already interpret " + host);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targetHost.size() <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i("resolve host " + targetHost.toString() + " " + type);
|
||||
}
|
||||
final String region = mHttpDnsConfig.getRegion();
|
||||
mRequestHandler.requestResolveHost(targetHost, type,
|
||||
new RequestCallback<ResolveHostResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final ResolveHostResponse resolveHostResponse) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("resolve hosts for " + targetHost.toString() + " " + type
|
||||
+ " return " + resolveHostResponse.toString());
|
||||
}
|
||||
String networkKey = NetworkStateManager.getInstance().getCurrentNetworkKey();
|
||||
mResultRepo.save(region, resolveHostResponse, networkKey);
|
||||
if (type == RequestIpType.v4 || type == RequestIpType.both) {
|
||||
for (final ResolveHostResponse.HostItem item
|
||||
: resolveHostResponse.getItems()) {
|
||||
if (item.getIpType() == RequestIpType.v4) {
|
||||
mIpIPRankingService.probeIpv4(item.getHost(), item.getIps(),
|
||||
new IPRankingCallback() {
|
||||
@Override
|
||||
public void onResult(String host, String[] sortedIps) {
|
||||
String networkKey = NetworkStateManager.getInstance().getCurrentNetworkKey();
|
||||
mResultRepo.update(item.getHost(),
|
||||
item.getIpType(),
|
||||
networkKey, sortedIps);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String host : targetHost) {
|
||||
mAsyncLocker.endResolve(host, type, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFail(Throwable throwable) {
|
||||
HttpDnsLog.w("resolve hosts for " + targetHost.toString() + " fail",
|
||||
throwable);
|
||||
|
||||
if (throwable instanceof Exception) {
|
||||
String query = "4";
|
||||
if (type == RequestIpType.v6) {
|
||||
query = "6";
|
||||
}else if (type == RequestIpType.both) {
|
||||
query = "4,6";
|
||||
}
|
||||
String errorMsg = (throwable instanceof HttpException) ? throwable.getMessage() : throwable.toString();
|
||||
HttpDnsLog.w("RESOLVE FAIL, HOST:" + targetHost + ", QUERY:" + query
|
||||
+ ", Msg:" + errorMsg);
|
||||
}
|
||||
if (throwable instanceof HttpException
|
||||
&& ((HttpException)throwable).shouldCreateEmptyCache()) {
|
||||
ResolveHostResponse emptyResponse = ResolveHostResponse.createEmpty(
|
||||
targetHost, type, 60 * 60);
|
||||
String networkKey = NetworkStateManager.getInstance().getCurrentNetworkKey();
|
||||
mResultRepo.save(region, emptyResponse, networkKey);
|
||||
}
|
||||
for (String host : targetHost) {
|
||||
mAsyncLocker.endResolve(host, type, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsConfig;
|
||||
import com.alibaba.sdk.android.httpdns.serverip.RegionServerScheduleService;
|
||||
|
||||
/**
|
||||
* 域名解析策略控制
|
||||
*/
|
||||
public class CategoryController implements StatusControl {
|
||||
|
||||
private Status mStatus = Status.NORMAL;
|
||||
private final NormalResolveCategory mNormal;
|
||||
private final SniffResolveCategory mSniff;
|
||||
|
||||
public CategoryController(HttpDnsConfig config, RegionServerScheduleService scheduleService) {
|
||||
mNormal = new NormalResolveCategory(config, scheduleService, this);
|
||||
mSniff = new SniffResolveCategory(config, scheduleService, this);
|
||||
}
|
||||
|
||||
public ResolveHostCategory getCategory() {
|
||||
if (mStatus == Status.DISABLE) {
|
||||
return mSniff;
|
||||
}
|
||||
return mNormal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void turnDown() {
|
||||
switch (mStatus) {
|
||||
case NORMAL:
|
||||
mStatus = Status.PRE_DISABLE;
|
||||
break;
|
||||
case PRE_DISABLE:
|
||||
mStatus = Status.DISABLE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void turnUp() {
|
||||
mStatus = Status.NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置策略
|
||||
*/
|
||||
public void reset() {
|
||||
mStatus = Status.NORMAL;
|
||||
mSniff.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置嗅探模式请求间隔
|
||||
*
|
||||
*/
|
||||
public void setSniffTimeInterval(int timeInterval) {
|
||||
mSniff.setInterval(timeInterval);
|
||||
}
|
||||
|
||||
/**
|
||||
* 策略状态,只有disable状态会使用嗅探模式
|
||||
*/
|
||||
enum Status {
|
||||
NORMAL,
|
||||
PRE_DISABLE,
|
||||
DISABLE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.DegradationFilter;
|
||||
import com.alibaba.sdk.android.httpdns.NotUseHttpDnsFilter;
|
||||
|
||||
public class HostFilter {
|
||||
DegradationFilter mFilter;
|
||||
NotUseHttpDnsFilter mNotUseHttpDnsFilter;
|
||||
|
||||
public boolean isFiltered(String host) {
|
||||
return (mNotUseHttpDnsFilter != null && mNotUseHttpDnsFilter.notUseHttpDns(host)) || (mFilter != null && mFilter.shouldDegradeHttpDNS(host));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setFilter(DegradationFilter filter) {
|
||||
this.mFilter = filter;
|
||||
}
|
||||
|
||||
public void setFilter(NotUseHttpDnsFilter filter) {
|
||||
mNotUseHttpDnsFilter = filter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsConfig;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequest;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequestConfig;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequestTask;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequestWatcher;
|
||||
import com.alibaba.sdk.android.httpdns.request.RequestCallback;
|
||||
import com.alibaba.sdk.android.httpdns.request.RetryHttpRequest;
|
||||
import com.alibaba.sdk.android.httpdns.request.SingleResolveHttpRequestStatusWatcher;
|
||||
import com.alibaba.sdk.android.httpdns.serverip.RegionServerScheduleService;
|
||||
|
||||
/**
|
||||
* 域名解析的一般策略
|
||||
*/
|
||||
public class NormalResolveCategory implements ResolveHostCategory {
|
||||
private final HttpDnsConfig mHttpDnsConfig;
|
||||
private final StatusControl mStatusControl;
|
||||
private final RegionServerScheduleService mScheduleService;
|
||||
|
||||
public NormalResolveCategory(HttpDnsConfig config, RegionServerScheduleService scheduleService, StatusControl statusControl) {
|
||||
this.mScheduleService = scheduleService;
|
||||
this.mStatusControl = statusControl;
|
||||
mHttpDnsConfig = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolve(HttpDnsConfig config, HttpRequestConfig requestConfig,
|
||||
RequestCallback<ResolveHostResponse> callback) {
|
||||
HttpRequest<ResolveHostResponse> request = new HttpRequest<>(requestConfig,
|
||||
new ResolveHostResponseParser(requestConfig.getAESEncryptService()));
|
||||
request = new HttpRequestWatcher<>(request, new SingleResolveHttpRequestStatusWatcher(
|
||||
mHttpDnsConfig.getObservableManager()));
|
||||
// 切换服务IP,更新服务IP
|
||||
request = new HttpRequestWatcher<>(request, new ShiftServerWatcher(config,
|
||||
mScheduleService,
|
||||
mStatusControl));
|
||||
// 重试一次
|
||||
request = new RetryHttpRequest<>(request, 1);
|
||||
try {
|
||||
config.getResolveWorker().execute(new HttpRequestTask<>(request, callback));
|
||||
} catch (Throwable e) {
|
||||
callback.onFail(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HTTPDNSResultWrapper;
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.cache.HostRecord;
|
||||
|
||||
public class ResolveHostCache {
|
||||
|
||||
/**
|
||||
* v4的解析记录
|
||||
*/
|
||||
private final ConcurrentHashMap<String, HostRecord> mV4Records = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* v6的解析记录
|
||||
*/
|
||||
private final ConcurrentHashMap<String, HostRecord> mV6Records = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* v4的返回结果
|
||||
*/
|
||||
private final ConcurrentHashMap<String, HTTPDNSResultWrapper> mV4HttpDnsResults
|
||||
= new ConcurrentHashMap<>();
|
||||
/**
|
||||
* v6的返回结果
|
||||
*/
|
||||
private final ConcurrentHashMap<String, HTTPDNSResultWrapper> mV6HttpDnsResults
|
||||
= new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 同时解析4 6的结果
|
||||
*/
|
||||
private final ConcurrentHashMap<String, HTTPDNSResultWrapper> mBothHttpDnsResults =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
public HTTPDNSResultWrapper getResult(String host, RequestIpType type) {
|
||||
HTTPDNSResultWrapper result = obtainHttpResult(host, type);
|
||||
result = buildHttpResult(host, type, result);
|
||||
cacheResult(host, type, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private HTTPDNSResultWrapper obtainHttpResult(String host, RequestIpType type) {
|
||||
HTTPDNSResultWrapper result = null;
|
||||
switch (type) {
|
||||
case v6:
|
||||
result = mV6HttpDnsResults.get(host);
|
||||
break;
|
||||
case v4:
|
||||
result = mV4HttpDnsResults.get(host);
|
||||
break;
|
||||
case both:
|
||||
result = mBothHttpDnsResults.get(host);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void cacheResult(String host, RequestIpType type, HTTPDNSResultWrapper result) {
|
||||
if (result != null) {
|
||||
switch (type) {
|
||||
case v6:
|
||||
mV6HttpDnsResults.put(host, result);
|
||||
break;
|
||||
case v4:
|
||||
mV4HttpDnsResults.put(host, result);
|
||||
break;
|
||||
case both:
|
||||
mBothHttpDnsResults.put(host, result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HTTPDNSResultWrapper buildHttpResult(String host, RequestIpType type, HTTPDNSResultWrapper result) {
|
||||
HostRecord record;
|
||||
switch (type) {
|
||||
case v6:
|
||||
record = mV6Records.get(host);
|
||||
if (record != null) {
|
||||
if (result == null) {
|
||||
result = new HTTPDNSResultWrapper(host);
|
||||
}
|
||||
result.update(record);
|
||||
}
|
||||
break;
|
||||
case v4:
|
||||
record = mV4Records.get(host);
|
||||
if (record != null) {
|
||||
if (result == null) {
|
||||
result = new HTTPDNSResultWrapper(host);
|
||||
}
|
||||
result.update(record);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
record = mV4Records.get(host);
|
||||
HostRecord recordv6 = mV6Records.get(host);
|
||||
if (record == null || recordv6 == null) {
|
||||
return result;
|
||||
}
|
||||
if (result == null) {
|
||||
result = new HTTPDNSResultWrapper(host);
|
||||
}
|
||||
ArrayList<HostRecord> records = new ArrayList<>();
|
||||
records.add(record);
|
||||
records.add(recordv6);
|
||||
result.update(records);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public HostRecord update(String region, String host, RequestIpType type, String extra,
|
||||
String cacheKey, String[] ips, int ttl, String serverIp, String noIpCode) {
|
||||
HostRecord record = null;
|
||||
switch (type) {
|
||||
case v4:
|
||||
record = mV4Records.get(host);
|
||||
if (record == null) {
|
||||
record = HostRecord.create(region, host, type, extra, cacheKey, ips, ttl, serverIp, noIpCode);
|
||||
mV4Records.put(host, record);
|
||||
} else {
|
||||
record.setRegion(region);
|
||||
record.setQueryTime(System.currentTimeMillis());
|
||||
record.setIps(ips);
|
||||
record.setTtl(ttl);
|
||||
record.setExtra(extra);
|
||||
record.setFromDB(false);
|
||||
record.setServerIp(serverIp);
|
||||
record.setNoIpCode(noIpCode);
|
||||
}
|
||||
break;
|
||||
case v6:
|
||||
record = mV6Records.get(host);
|
||||
if (record == null) {
|
||||
record = HostRecord.create(region, host, type, extra, cacheKey, ips, ttl, serverIp, noIpCode);
|
||||
mV6Records.put(host, record);
|
||||
} else {
|
||||
record.setRegion(region);
|
||||
record.setQueryTime(System.currentTimeMillis());
|
||||
record.setIps(ips);
|
||||
record.setTtl(ttl);
|
||||
record.setExtra(extra);
|
||||
record.setFromDB(false);
|
||||
record.setServerIp(serverIp);
|
||||
record.setNoIpCode(noIpCode);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("type should be v4 or b6");
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
public void put(HostRecord record) {
|
||||
if (record.getType() == RequestIpType.v4.ordinal()) {
|
||||
mV4Records.put(record.getHost(), record);
|
||||
} else if (record.getType() == RequestIpType.v6.ordinal()) {
|
||||
mV6Records.put(record.getHost(), record);
|
||||
}
|
||||
}
|
||||
|
||||
public HostRecord updateIps(String host, RequestIpType type, String[] ips) {
|
||||
HostRecord record = null;
|
||||
switch (type) {
|
||||
case v4:
|
||||
record = mV4Records.get(host);
|
||||
if (record == null) {
|
||||
return null;
|
||||
}
|
||||
record.setIps(ips);
|
||||
break;
|
||||
case v6:
|
||||
record = mV6Records.get(host);
|
||||
if (record == null) {
|
||||
return null;
|
||||
}
|
||||
record.setIps(ips);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("type should be v4 or b6");
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
public List<HostRecord> clear() {
|
||||
ArrayList<HostRecord> list = new ArrayList<>();
|
||||
list.addAll(mV4Records.values());
|
||||
list.addAll(mV6Records.values());
|
||||
mV4Records.clear();
|
||||
mV6Records.clear();
|
||||
mV4HttpDnsResults.clear();
|
||||
mV6HttpDnsResults.clear();
|
||||
mBothHttpDnsResults.clear();
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<HostRecord> clear(List<String> hosts) {
|
||||
ArrayList<HostRecord> records = new ArrayList<>();
|
||||
for (String host : hosts) {
|
||||
HostRecord tmp = mV4Records.remove(host);
|
||||
if (tmp != null) {
|
||||
records.add(tmp);
|
||||
}
|
||||
}
|
||||
for (String host : hosts) {
|
||||
HostRecord tmp = mV6Records.remove(host);
|
||||
if (tmp != null) {
|
||||
records.add(tmp);
|
||||
}
|
||||
}
|
||||
for (String host : hosts) {
|
||||
mV4HttpDnsResults.remove(host);
|
||||
mV6HttpDnsResults.remove(host);
|
||||
mBothHttpDnsResults.remove(host);
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
public HashMap<String, RequestIpType> getAllHostNotEmptyResult() {
|
||||
HashMap<String, RequestIpType> all = new HashMap<>();
|
||||
for (HostRecord record : mV4Records.values()) {
|
||||
if (record.getIps() != null && record.getIps().length > 0) {
|
||||
all.put(record.getHost(), RequestIpType.v4);
|
||||
}
|
||||
}
|
||||
for (HostRecord record : mV6Records.values()) {
|
||||
if (record.getIps() != null && record.getIps().length > 0) {
|
||||
RequestIpType type = all.get(record.getHost());
|
||||
if (type == null) {
|
||||
all.put(record.getHost(), RequestIpType.v6);
|
||||
} else {
|
||||
all.put(record.getHost(), RequestIpType.both);
|
||||
}
|
||||
}
|
||||
}
|
||||
return all;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.cache.HostRecord;
|
||||
import com.alibaba.sdk.android.httpdns.net.NetworkStateManager;
|
||||
|
||||
/**
|
||||
* 缓存组管理器,支持网络隔离缓存
|
||||
*/
|
||||
public class ResolveHostCacheGroup {
|
||||
|
||||
/**
|
||||
* 所有缓存统一管理,包括网络隔离缓存和SDNS缓存
|
||||
*/
|
||||
private final HashMap<String, ResolveHostCache> mCaches = new HashMap<>();
|
||||
private final Object lock = new Object();
|
||||
private static final String NETWORK_KEY_PREFIX = "emasInner_";
|
||||
|
||||
public ResolveHostCache getCache(String cacheKey) {
|
||||
if (cacheKey == null || cacheKey.isEmpty()) {
|
||||
// 普通解析使用当前网络标识
|
||||
cacheKey = NetworkStateManager.getInstance().getCurrentNetworkKey();
|
||||
}
|
||||
|
||||
// 统一的缓存获取逻辑
|
||||
ResolveHostCache cache = mCaches.get(cacheKey);
|
||||
if (cache == null) {
|
||||
synchronized (lock) {
|
||||
cache = mCaches.get(cacheKey);
|
||||
if (cache == null) {
|
||||
cache = new ResolveHostCache();
|
||||
mCaches.put(cacheKey, cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有网络缓存中的域名(排除SDNS缓存)
|
||||
*/
|
||||
public HashMap<String, RequestIpType> getAllHostFromNetworkCaches() {
|
||||
HashMap<String, RequestIpType> allHosts = new HashMap<>();
|
||||
|
||||
synchronized (lock) {
|
||||
for (Map.Entry<String, ResolveHostCache> entry : mCaches.entrySet()) {
|
||||
String cacheKey = entry.getKey();
|
||||
|
||||
if (!cacheKey.startsWith(NETWORK_KEY_PREFIX)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResolveHostCache cache = entry.getValue();
|
||||
HashMap<String, RequestIpType> hosts = cache.getAllHostNotEmptyResult();
|
||||
|
||||
for (Map.Entry<String, RequestIpType> hostEntry : hosts.entrySet()) {
|
||||
String host = hostEntry.getKey();
|
||||
RequestIpType type = hostEntry.getValue();
|
||||
|
||||
if (!allHosts.containsKey(host)) {
|
||||
allHosts.put(host, type);
|
||||
continue;
|
||||
}
|
||||
|
||||
RequestIpType mergedType = mergeIpTypes(allHosts.get(host), type);
|
||||
allHosts.put(host, mergedType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return allHosts;
|
||||
}
|
||||
|
||||
private RequestIpType mergeIpTypes(RequestIpType type1, RequestIpType type2) {
|
||||
if (type1 == RequestIpType.both || type2 == RequestIpType.both) {
|
||||
return RequestIpType.both;
|
||||
}
|
||||
|
||||
if ((type1 == RequestIpType.v4 && type2 == RequestIpType.v6) ||
|
||||
(type1 == RequestIpType.v6 && type2 == RequestIpType.v4)) {
|
||||
return RequestIpType.both;
|
||||
}
|
||||
|
||||
return type1;
|
||||
}
|
||||
|
||||
public List<HostRecord> clearAll() {
|
||||
ArrayList<HostRecord> records = new ArrayList<>();
|
||||
if (mCaches.size() > 0) {
|
||||
synchronized (lock) {
|
||||
for (ResolveHostCache cache : mCaches.values()) {
|
||||
records.addAll(cache.clear());
|
||||
}
|
||||
}
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
public List<HostRecord> clearAll(List<String> hosts) {
|
||||
ArrayList<HostRecord> records = new ArrayList<>();
|
||||
if (mCaches.size() > 0) {
|
||||
synchronized (lock) {
|
||||
for (ResolveHostCache cache : mCaches.values()) {
|
||||
records.addAll(cache.clear(hosts));
|
||||
}
|
||||
}
|
||||
}
|
||||
return records;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsConfig;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequestConfig;
|
||||
import com.alibaba.sdk.android.httpdns.request.RequestCallback;
|
||||
|
||||
/**
|
||||
* 域名解析策略接口
|
||||
*/
|
||||
public interface ResolveHostCategory {
|
||||
/**
|
||||
* 解析域名
|
||||
* @param config {@link HttpDnsConfig}
|
||||
* @param requestConfig {@link HttpRequestConfig}
|
||||
* @param callback {@link RequestCallback<ResolveHostResponse>}
|
||||
*/
|
||||
void resolve(HttpDnsConfig config, HttpRequestConfig requestConfig,
|
||||
RequestCallback<ResolveHostResponse> callback);
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.BuildConfig;
|
||||
import com.alibaba.sdk.android.httpdns.NetType;
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.impl.AESEncryptService;
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsConfig;
|
||||
import com.alibaba.sdk.android.httpdns.impl.SignService;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequestConfig;
|
||||
import com.alibaba.sdk.android.httpdns.track.SessionTrackMgr;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class ResolveHostHelper {
|
||||
public static HttpRequestConfig getConfig(HttpDnsConfig config, String host,
|
||||
RequestIpType type,
|
||||
Map<String, String> extras, String cacheKey,
|
||||
Map<String, String> globalParams,
|
||||
SignService signService,
|
||||
AESEncryptService encryptService) {
|
||||
HashMap<String, String> extraArgs = null;
|
||||
if (cacheKey != null) {
|
||||
extraArgs = new HashMap<>();
|
||||
if (globalParams != null) {
|
||||
extraArgs.putAll(globalParams);
|
||||
}
|
||||
if (extras != null) {
|
||||
extraArgs.putAll(extras);
|
||||
}
|
||||
}
|
||||
|
||||
String path = getPath(config, host, type, extraArgs, signService, encryptService);
|
||||
HttpRequestConfig requestConfig = getHttpRequestConfig(config, path, signService.isSignMode());
|
||||
requestConfig.setUA(config.getUA());
|
||||
requestConfig.setAESEncryptService(encryptService);
|
||||
return requestConfig;
|
||||
}
|
||||
|
||||
public static String getPath(HttpDnsConfig config, String host, RequestIpType type,
|
||||
Map<String, String> extras,
|
||||
SignService signService,
|
||||
AESEncryptService encryptService) {
|
||||
//参数加密
|
||||
String enc = "";
|
||||
String query = getQuery(type);
|
||||
String version = "1.0";
|
||||
String tags = config.getBizTags();
|
||||
AESEncryptService.EncryptionMode mode = AESEncryptService.EncryptionMode.PLAIN;
|
||||
if (encryptService.isEncryptionMode()) {
|
||||
String encryptJson = buildEncryptionStr(host, query, extras, tags);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("encryptJson:" + encryptJson);
|
||||
}
|
||||
mode = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? AESEncryptService.EncryptionMode.AES_GCM : AESEncryptService.EncryptionMode.AES_CBC;
|
||||
enc = encryptService.encrypt(encryptJson, mode);
|
||||
}
|
||||
|
||||
String expireTime = signService.getExpireTime();
|
||||
|
||||
String queryStr = buildQueryStr(config.getAccountId(), mode.getMode(), host,
|
||||
query, extras, enc, expireTime, version, tags);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("query parameter:" + queryStr);
|
||||
}
|
||||
|
||||
//加签
|
||||
if (signService.isSignMode()) {
|
||||
Map<String, String> signParamMap = new HashMap<>();
|
||||
if (encryptService.isEncryptionMode()) {
|
||||
signParamMap.put("enc", enc);
|
||||
signParamMap.put("exp", expireTime);
|
||||
signParamMap.put("id", config.getAccountId());
|
||||
signParamMap.put("m", mode.getMode());
|
||||
signParamMap.put("v", version);
|
||||
}else {
|
||||
signParamMap.put("dn", host);
|
||||
signParamMap.put("exp", expireTime);
|
||||
signParamMap.put("id", config.getAccountId());
|
||||
signParamMap.put("m", mode.getMode());
|
||||
if (!TextUtils.isEmpty(query)) {
|
||||
signParamMap.put("q", query);
|
||||
}
|
||||
if (extras != null) {
|
||||
for (Map.Entry<String, String> entry : extras.entrySet()) {
|
||||
signParamMap.put("sdns-" + entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
if (!TextUtils.isEmpty(tags)) {
|
||||
signParamMap.put("tags", tags);
|
||||
}
|
||||
signParamMap.put("v", version);
|
||||
}
|
||||
|
||||
String sign = signService.sign(signParamMap);
|
||||
if (TextUtils.isEmpty(sign)) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("param sign fail");
|
||||
}
|
||||
}else {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("sign:" + sign);
|
||||
}
|
||||
queryStr += "&s=" + sign;
|
||||
}
|
||||
}
|
||||
String path = "/v2/d?" + queryStr + "&sdk=android_" + BuildConfig.VERSION_NAME + getSid();
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("path:" + path);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private static String buildQueryStr(String accountId, String mode, String host,
|
||||
String query, Map<String, String> extras, String enc,
|
||||
String expireTime, String version, String tags) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("id=").append(accountId);
|
||||
stringBuilder.append("&m=").append(mode);
|
||||
if (TextUtils.isEmpty(enc)) {
|
||||
stringBuilder.append("&dn=").append(host);
|
||||
if (!TextUtils.isEmpty(query)) {
|
||||
stringBuilder.append("&q=").append(query);
|
||||
}
|
||||
|
||||
String extra = getExtra(extras);
|
||||
if (!TextUtils.isEmpty(extra)) {
|
||||
stringBuilder.append(extra);
|
||||
}
|
||||
if (!TextUtils.isEmpty(tags)) {
|
||||
stringBuilder.append("&tags=").append(tags);
|
||||
}
|
||||
}else {
|
||||
stringBuilder.append("&enc=").append(enc);
|
||||
}
|
||||
stringBuilder.append("&v=").append(version);
|
||||
stringBuilder.append("&exp=").append(expireTime);
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
private static String buildEncryptionStr(String host, String query, Map<String, String> extras, String tags) {
|
||||
JSONObject json = new JSONObject();
|
||||
try {
|
||||
json.put("dn", host);
|
||||
if (!TextUtils.isEmpty(query)) {
|
||||
json.put("q", query);
|
||||
}
|
||||
if (!TextUtils.isEmpty(getExtra(extras))) {
|
||||
for (Map.Entry<String, String> entry : extras.entrySet()) {
|
||||
if (!checkKey(entry.getKey()) || !checkValue(entry.getValue())) {
|
||||
continue;
|
||||
}
|
||||
json.put("sdns-" + entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
if (!TextUtils.isEmpty(tags)) {
|
||||
json.put("tags", tags);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.e("encrypt param transfer to json fail.", e);
|
||||
}
|
||||
|
||||
}
|
||||
return json.toString();
|
||||
}
|
||||
|
||||
private static String getQuery(RequestIpType type) {
|
||||
String query = "";
|
||||
switch (type) {
|
||||
case v6:
|
||||
query = "6";
|
||||
break;
|
||||
case both:
|
||||
query = "4,6";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
private static String getExtra(Map<String, String> extras) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean isKey = true;
|
||||
boolean isValue = true;
|
||||
if (extras != null) {
|
||||
for (Map.Entry<String, String> entry : extras.entrySet()) {
|
||||
sb.append("&sdns-");
|
||||
sb.append(entry.getKey());
|
||||
sb.append("=");
|
||||
sb.append(entry.getValue());
|
||||
if (!checkKey(entry.getKey())) {
|
||||
isKey = false;
|
||||
HttpDnsLog.e("设置自定义参数失败,自定义key不合法:" + entry.getKey());
|
||||
break;
|
||||
}
|
||||
if (!checkValue(entry.getValue())) {
|
||||
isValue = false;
|
||||
HttpDnsLog.e("设置自定义参数失败,自定义value不合法:" + entry.getValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
if (isKey && isValue) {
|
||||
String extra = sb.toString();
|
||||
if (extra.getBytes(StandardCharsets.UTF_8).length <= 1000) {
|
||||
return extra;
|
||||
} else {
|
||||
HttpDnsLog.e("设置自定义参数失败,自定义参数过长");
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkKey(String s) {
|
||||
return s.matches("[a-zA-Z0-9\\-_]+");
|
||||
}
|
||||
|
||||
private static boolean checkValue(String s) {
|
||||
return s.matches("[a-zA-Z0-9\\-_=]+");
|
||||
}
|
||||
|
||||
public static HttpRequestConfig getConfig(HttpDnsConfig config, ArrayList<String> hostList,
|
||||
RequestIpType type, SignService signService,
|
||||
AESEncryptService encryptService) {
|
||||
//拼接host
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (int i = 0; i < hostList.size(); i++) {
|
||||
if (i != 0) {
|
||||
stringBuilder.append(",");
|
||||
}
|
||||
stringBuilder.append(hostList.get(i));
|
||||
}
|
||||
String host = stringBuilder.toString();
|
||||
String path = getPath(config, host, type, null, signService, encryptService);
|
||||
HttpRequestConfig requestConfig = getHttpRequestConfig(config, path, signService.isSignMode());
|
||||
requestConfig.setUA(config.getUA());
|
||||
requestConfig.setAESEncryptService(encryptService);
|
||||
return requestConfig;
|
||||
}
|
||||
|
||||
private static HttpRequestConfig getHttpRequestConfig(HttpDnsConfig config, String path, Boolean isSignMode) {
|
||||
if (config.getNetworkDetector() != null && config.getNetworkDetector().getNetType(
|
||||
config.getContext()) == NetType.v6) {
|
||||
return new HttpRequestConfig(config.getSchema(),
|
||||
config.getCurrentServer().getServerIpForV6(),
|
||||
config.getCurrentServer().getPortForV6(), path, config.getTimeout(),
|
||||
RequestIpType.v6, isSignMode);
|
||||
} else {
|
||||
return new HttpRequestConfig(config.getSchema(),
|
||||
config.getCurrentServer().getServerIp(), config.getCurrentServer().getPort(), path,
|
||||
config.getTimeout(), RequestIpType.v4, isSignMode);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static String getTags(HttpDnsConfig config) {
|
||||
if (TextUtils.isEmpty(config.getBizTags())) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return "&tags=" + config.getBizTags();
|
||||
}
|
||||
|
||||
public static String getSid() {
|
||||
String sessionId = SessionTrackMgr.getInstance().getSessionId();
|
||||
if (sessionId == null) {
|
||||
return "";
|
||||
} else {
|
||||
return "&sid=" + sessionId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.impl.AESEncryptService;
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsConfig;
|
||||
import com.alibaba.sdk.android.httpdns.impl.SignService;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.request.BatchResolveHttpRequestStatusWatcher;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequest;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequestConfig;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequestTask;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequestWatcher;
|
||||
import com.alibaba.sdk.android.httpdns.request.RequestCallback;
|
||||
import com.alibaba.sdk.android.httpdns.request.RetryHttpRequest;
|
||||
import com.alibaba.sdk.android.httpdns.serverip.RegionServerScheduleService;
|
||||
|
||||
/**
|
||||
* 发起域名解析请求
|
||||
*/
|
||||
public class ResolveHostRequestHandler {
|
||||
|
||||
private final HttpDnsConfig mHttpDnsConfig;
|
||||
private final RegionServerScheduleService mScheduleService;
|
||||
private final CategoryController mCategoryController;
|
||||
private final HashMap<String, String> mGlobalParams;
|
||||
private final SignService mSignService;
|
||||
private final AESEncryptService mAESEncryptService;
|
||||
|
||||
public ResolveHostRequestHandler(HttpDnsConfig config, RegionServerScheduleService scheduleService,
|
||||
SignService signService, AESEncryptService aesEncryptService) {
|
||||
this.mHttpDnsConfig = config;
|
||||
this.mScheduleService = scheduleService;
|
||||
this.mCategoryController = new CategoryController(config, scheduleService);
|
||||
this.mGlobalParams = new HashMap<>();
|
||||
this.mSignService = signService;
|
||||
this.mAESEncryptService = aesEncryptService;
|
||||
}
|
||||
|
||||
public void requestResolveHost(final String host, final RequestIpType type,
|
||||
Map<String, String> extras, final String cacheKey,
|
||||
RequestCallback<ResolveHostResponse> callback) {
|
||||
HttpRequestConfig requestConfig = ResolveHostHelper.getConfig(mHttpDnsConfig, host, type,
|
||||
extras, cacheKey, mGlobalParams, mSignService, mAESEncryptService);
|
||||
//补充可观测数据
|
||||
requestConfig.setResolvingHost(host);
|
||||
requestConfig.setResolvingIpType(type);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("start resolve ip request for " + host + " " + type);
|
||||
}
|
||||
mCategoryController.getCategory().resolve(mHttpDnsConfig, requestConfig, callback);
|
||||
}
|
||||
|
||||
public void requestResolveHost(final ArrayList<String> hostList, final RequestIpType type,
|
||||
RequestCallback<ResolveHostResponse> callback) {
|
||||
HttpRequestConfig requestConfig = ResolveHostHelper.getConfig(mHttpDnsConfig, hostList,
|
||||
type, mSignService, mAESEncryptService);
|
||||
requestConfig.setResolvingIpType(type);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("start resolve hosts async for " + hostList.toString() + " " + type);
|
||||
}
|
||||
|
||||
HttpRequest<ResolveHostResponse> request = new HttpRequest<>(
|
||||
requestConfig, new ResolveHostResponseParser(mAESEncryptService));
|
||||
request = new HttpRequestWatcher<>(request,
|
||||
new BatchResolveHttpRequestStatusWatcher(mHttpDnsConfig.getObservableManager()));
|
||||
// 切换服务IP,更新服务IP
|
||||
request = new HttpRequestWatcher<>(request, new ShiftServerWatcher(mHttpDnsConfig,
|
||||
mScheduleService, mCategoryController));
|
||||
// 重试一次
|
||||
request = new RetryHttpRequest<>(request, 1);
|
||||
try {
|
||||
mHttpDnsConfig.getResolveWorker().execute(
|
||||
new HttpRequestTask<>(request, callback));
|
||||
} catch (Throwable e) {
|
||||
callback.onFail(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置状态
|
||||
*/
|
||||
public void resetStatus() {
|
||||
mCategoryController.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置嗅探模式的请求时间间隔
|
||||
*/
|
||||
public void setSniffTimeInterval(int timeInterval) {
|
||||
mCategoryController.setSniffTimeInterval(timeInterval);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置sdns的全局参数
|
||||
*/
|
||||
public void setSdnsGlobalParams(Map<String, String> params) {
|
||||
this.mGlobalParams.clear();
|
||||
if (params != null) {
|
||||
this.mGlobalParams.putAll(params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除sdns的全局参数
|
||||
*/
|
||||
public void clearSdnsGlobalParams() {
|
||||
this.mGlobalParams.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 解析的结果
|
||||
*/
|
||||
public class ResolveHostResponse {
|
||||
|
||||
private final ArrayList<HostItem> mHostItems;
|
||||
private String mServerIp;
|
||||
|
||||
public ResolveHostResponse(ArrayList<HostItem> items, String serverIp) {
|
||||
this.mHostItems = items;
|
||||
mServerIp = serverIp;
|
||||
}
|
||||
|
||||
public HostItem getHostItem(String host, RequestIpType type) {
|
||||
for (HostItem item : mHostItems) {
|
||||
if (item.mHost.equals(host) && item.mIpType == type) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<HostItem> getItems() {
|
||||
return mHostItems;
|
||||
}
|
||||
|
||||
public String getServerIp() {
|
||||
return mServerIp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder ret = new StringBuilder();
|
||||
if (mHostItems != null) {
|
||||
for (int i = 0; i < mHostItems.size(); i++) {
|
||||
ret.append(mHostItems.get(i).toString());
|
||||
if (i != mHostItems.size() - 1) {
|
||||
ret.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
public static class HostItem {
|
||||
private final String mHost;
|
||||
private final RequestIpType mIpType;
|
||||
private final String[] mIps;
|
||||
private final int mTtl;
|
||||
|
||||
private final String mExtra;
|
||||
|
||||
private final String noIpCode;
|
||||
|
||||
public HostItem(String host, RequestIpType type, String[] ips, int ttl, String extra, String noIpCode) {
|
||||
this.mHost = host;
|
||||
this.mIpType = type;
|
||||
this.mIps = ips;
|
||||
if (ttl <= 0) {
|
||||
this.mTtl = 60;
|
||||
} else {
|
||||
this.mTtl = ttl;
|
||||
}
|
||||
this.mExtra = extra;
|
||||
this.noIpCode = noIpCode;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return mHost;
|
||||
}
|
||||
|
||||
public RequestIpType getIpType() {
|
||||
return mIpType;
|
||||
}
|
||||
|
||||
public String[] getIps() {
|
||||
return mIps;
|
||||
}
|
||||
|
||||
public int getTtl() {
|
||||
return mTtl;
|
||||
}
|
||||
|
||||
public String getExtra() {
|
||||
return mExtra;
|
||||
}
|
||||
|
||||
public String getNoIpCode() {
|
||||
return noIpCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder ret = new StringBuilder(
|
||||
"host: " + mHost + " ip cnt: " + (mIps != null ? mIps.length : 0) + " ttl: "
|
||||
+ mTtl);
|
||||
if (mIps != null) {
|
||||
for (String ip : mIps) {
|
||||
ret.append("\n ip: ").append(ip);
|
||||
}
|
||||
}
|
||||
|
||||
ret.append("\n extra: ").append(mExtra);
|
||||
ret.append("\n noIpCode: ").append(noIpCode);
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static ResolveHostResponse fromResponse(String serverIp, String body) throws JSONException {
|
||||
|
||||
ArrayList<HostItem> items = new ArrayList<>();
|
||||
JSONObject jsonObject = new JSONObject(body);
|
||||
|
||||
if (jsonObject.has("answers")) {
|
||||
JSONArray answers = jsonObject.getJSONArray("answers");
|
||||
for (int i = 0; i < answers.length(); i++) {
|
||||
JSONObject answer = answers.getJSONObject(i);
|
||||
String hostName = null;
|
||||
int ttl = 0;
|
||||
String extra = null;
|
||||
String[] ips = null;
|
||||
String[] ipsv6 = null;
|
||||
String noIpCode = null;
|
||||
if (answer.has("dn")) {
|
||||
hostName = answer.getString("dn");
|
||||
}
|
||||
|
||||
if (answer.has("v4")) {
|
||||
JSONObject ipv4 = answer.getJSONObject("v4");
|
||||
if (ipv4.has("ips")) {
|
||||
JSONArray ipArray = ipv4.getJSONArray("ips");
|
||||
if (ipArray.length() != 0) {
|
||||
ips = new String[ipArray.length()];
|
||||
for (int j = 0; j < ipArray.length(); j++) {
|
||||
ips[j] = ipArray.getString(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ipv4.has("ttl")) {
|
||||
ttl = ipv4.getInt("ttl");
|
||||
}
|
||||
if (ipv4.has("extra")) {
|
||||
extra = ipv4.getString("extra");
|
||||
}
|
||||
if (ipv4.has("no_ip_code")) {
|
||||
noIpCode = ipv4.getString("no_ip_code");
|
||||
}
|
||||
items.add(new HostItem(hostName, RequestIpType.v4, ips, ttl, extra, noIpCode));
|
||||
if (!TextUtils.isEmpty(noIpCode)) {
|
||||
HttpDnsLog.w("RESOLVE FAIL, HOST:" + hostName + ", QUERY:4, "
|
||||
+ "Msg:" + noIpCode);
|
||||
}
|
||||
}
|
||||
if (answer.has("v6")) {
|
||||
JSONObject ipv6 = answer.getJSONObject("v6");
|
||||
if (ipv6.has("ips")) {
|
||||
JSONArray ipArray = ipv6.getJSONArray("ips");
|
||||
if (ipArray.length() != 0) {
|
||||
ipsv6 = new String[ipArray.length()];
|
||||
for (int j = 0; j < ipArray.length(); j++) {
|
||||
ipsv6[j] = ipArray.getString(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ipv6.has("ttl")) {
|
||||
ttl = ipv6.getInt("ttl");
|
||||
}
|
||||
if (ipv6.has("extra")) {
|
||||
extra = ipv6.getString("extra");
|
||||
}
|
||||
if (ipv6.has("no_ip_code")) {
|
||||
noIpCode = ipv6.getString("no_ip_code");
|
||||
}
|
||||
items.add(new HostItem(hostName, RequestIpType.v6, ipsv6, ttl, extra, noIpCode));
|
||||
if (!TextUtils.isEmpty(noIpCode)) {
|
||||
HttpDnsLog.w("RESOLVE FAIL, HOST:" + hostName + ", QUERY:6, "
|
||||
+ "Msg:" + noIpCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return new ResolveHostResponse(items, serverIp);
|
||||
}
|
||||
|
||||
public static ResolveHostResponse createEmpty(List<String> hostList, RequestIpType type,
|
||||
int ttl) {
|
||||
ArrayList<HostItem> list = new ArrayList<>();
|
||||
for (String host : hostList) {
|
||||
if (type == RequestIpType.v4 || type == RequestIpType.both) {
|
||||
list.add(new HostItem(host, RequestIpType.v4, null, ttl, null, null));
|
||||
}
|
||||
if (type == RequestIpType.v6 || type == RequestIpType.both) {
|
||||
list.add(new HostItem(host, RequestIpType.v6, null, ttl, null, null));
|
||||
}
|
||||
}
|
||||
return new ResolveHostResponse(list, "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.impl.AESEncryptService;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.request.ResponseParser;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class ResolveHostResponseParser implements
|
||||
ResponseParser<ResolveHostResponse> {
|
||||
|
||||
private final AESEncryptService mAESEncryptService;
|
||||
|
||||
public ResolveHostResponseParser(AESEncryptService aesEncryptService) {
|
||||
mAESEncryptService = aesEncryptService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResolveHostResponse parse(String serverIp, String response) throws Throwable {
|
||||
String data = "";
|
||||
JSONObject jsonResponse = new JSONObject(response);
|
||||
if (jsonResponse.has("code")) {
|
||||
String code = jsonResponse.getString("code");
|
||||
if (TextUtils.equals(code, "success")) {
|
||||
if (jsonResponse.has("data")) {
|
||||
data = jsonResponse.getString("data");
|
||||
if (!TextUtils.isEmpty(data)) {
|
||||
//解密
|
||||
AESEncryptService.EncryptionMode mode = AESEncryptService.EncryptionMode.PLAIN;
|
||||
if (jsonResponse.has("mode")) {
|
||||
String serverEncryptMode = jsonResponse.getString("mode");
|
||||
if (TextUtils.equals(serverEncryptMode, AESEncryptService.EncryptionMode.AES_GCM.getMode())) {
|
||||
mode = AESEncryptService.EncryptionMode.AES_GCM;
|
||||
} else if (TextUtils.equals(serverEncryptMode, AESEncryptService.EncryptionMode.AES_CBC.getMode())) {
|
||||
mode = AESEncryptService.EncryptionMode.AES_CBC;
|
||||
}
|
||||
}
|
||||
data = mAESEncryptService.decrypt(data, mode);
|
||||
if (TextUtils.isEmpty(data)) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.e("response data decrypt fail");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.e("response data is empty");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.e("解析失败,原因为" + code);
|
||||
}
|
||||
throw new Exception(code);
|
||||
}
|
||||
}else {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.e("response don't have code");
|
||||
}
|
||||
}
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("request success " + data);
|
||||
}
|
||||
return ResolveHostResponse.fromResponse(serverIp, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.CacheTtlChanger;
|
||||
import com.alibaba.sdk.android.httpdns.HTTPDNSResultWrapper;
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.cache.HostRecord;
|
||||
import com.alibaba.sdk.android.httpdns.cache.RecordDBHelper;
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsConfig;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.ranking.IPRankingCallback;
|
||||
import com.alibaba.sdk.android.httpdns.ranking.IPRankingService;
|
||||
|
||||
/**
|
||||
* 域名解析结果
|
||||
*/
|
||||
public class ResolveHostResultRepo {
|
||||
|
||||
private final RecordDBHelper mDBHelper;
|
||||
private boolean mEnableCache = false;
|
||||
private final HttpDnsConfig mHttpDnsConfig;
|
||||
private final IPRankingService mIpIPRankingService;
|
||||
private final ResolveHostCacheGroup mCacheGroup;
|
||||
private CacheTtlChanger mCacheTtlChanger;
|
||||
private final ArrayList<String> mHostListWhichIpFixed = new ArrayList<>();
|
||||
private volatile boolean mFinishReadFromDb = false;
|
||||
private final Object mDbLock = new Object();
|
||||
private long mExpiredThresholdMillis = 0L;
|
||||
|
||||
public ResolveHostResultRepo(HttpDnsConfig config, IPRankingService ipIPRankingService,
|
||||
RecordDBHelper dbHelper, ResolveHostCacheGroup cacheGroup) {
|
||||
this.mHttpDnsConfig = config;
|
||||
this.mIpIPRankingService = ipIPRankingService;
|
||||
this.mDBHelper = dbHelper;
|
||||
this.mCacheGroup = cacheGroup;
|
||||
}
|
||||
|
||||
public void ensureReadFromDB() {
|
||||
if (!mEnableCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFinishReadFromDb) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mDbLock) {
|
||||
if (mFinishReadFromDb) {
|
||||
return;
|
||||
}
|
||||
|
||||
readFromDB(mExpiredThresholdMillis);
|
||||
mFinishReadFromDb = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void readFromDB(long expiredThresholdMillis) {
|
||||
final String region = mHttpDnsConfig.getRegion();
|
||||
List<HostRecord> records = mDBHelper.readFromDb(region);
|
||||
for (HostRecord record : records) {
|
||||
|
||||
// 过期缓存不加载到内存中
|
||||
if (System.currentTimeMillis() >= record.getQueryTime() + record.getTtl() * 1000L + expiredThresholdMillis) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (record.getIps() == null || record.getIps().length == 0) {
|
||||
// 空解析,按照ttl来,不区分是否来自数据库
|
||||
record.setFromDB(false);
|
||||
}
|
||||
if (mHostListWhichIpFixed.contains(record.getHost())) {
|
||||
// 固定IP,按照ttl来,不区分是否来自数据库
|
||||
record.setFromDB(false);
|
||||
}
|
||||
ResolveHostCache cache = mCacheGroup.getCache(record.getCacheKey());
|
||||
cache.put(record);
|
||||
}
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("cache ready");
|
||||
}
|
||||
|
||||
//清理db
|
||||
ArrayList<HostRecord> expired = new ArrayList<>();
|
||||
for (HostRecord record : records) {
|
||||
//达到过期阈值
|
||||
if (System.currentTimeMillis() >= record.getQueryTime() + record.getTtl() * 1000L + expiredThresholdMillis) {
|
||||
expired.add(record);
|
||||
}
|
||||
}
|
||||
mDBHelper.delete(expired);
|
||||
|
||||
if (!mHttpDnsConfig.getRegion().equals(region)) {
|
||||
// 防止刚读取完,region变化了
|
||||
mCacheGroup.clearAll();
|
||||
} else {
|
||||
for (final HostRecord record : records) {
|
||||
RequestIpType type = RequestIpType.values()[record.getType()];
|
||||
if (!record.isExpired() && type == RequestIpType.v4) {
|
||||
mIpIPRankingService.probeIpv4(record.getHost(), record.getIps(),
|
||||
new IPRankingCallback() {
|
||||
@Override
|
||||
public void onResult(String host, String[] sortedIps) {
|
||||
update(host, RequestIpType.v4, record.getCacheKey(), sortedIps);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private HostRecord save(String region, String host, RequestIpType type, String extra,
|
||||
String cacheKey, String[] ips, int ttl, String serverIp, String noIpCode) {
|
||||
|
||||
if (mCacheTtlChanger != null) {
|
||||
ttl = mCacheTtlChanger.changeCacheTtl(host, type, ttl);
|
||||
}
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("save host " + host + " for type " + type.name() + " ttl is " + ttl);
|
||||
}
|
||||
|
||||
ResolveHostCache cache = mCacheGroup.getCache(cacheKey);
|
||||
return cache.update(region, host, type, extra, cacheKey, ips, ttl, serverIp, noIpCode);
|
||||
}
|
||||
|
||||
private void updateInner(String host, RequestIpType type, String cacheKey, String[] ips) {
|
||||
ResolveHostCache cache = mCacheGroup.getCache(cacheKey);
|
||||
HostRecord record = cache.updateIps(host, type, ips);
|
||||
if (mEnableCache || mHostListWhichIpFixed.contains(host)) {
|
||||
final ArrayList<HostRecord> records = new ArrayList<>();
|
||||
records.add(record);
|
||||
try {
|
||||
mHttpDnsConfig.getDbWorker().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mDBHelper.insertOrUpdate(records);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存预解析结果
|
||||
*/
|
||||
public void save(String region, ResolveHostResponse resolveHostResponse, String cacheKey) {
|
||||
final ArrayList<HostRecord> records = new ArrayList<>();
|
||||
for (ResolveHostResponse.HostItem item : resolveHostResponse.getItems()) {
|
||||
HostRecord record = save(region, item.getHost(), item.getIpType(), item.getExtra(), cacheKey,
|
||||
item.getIps(), item.getTtl(), resolveHostResponse.getServerIp(), item.getNoIpCode());
|
||||
if (mEnableCache
|
||||
|| mHostListWhichIpFixed.contains(item.getHost())
|
||||
|| (item.getIps() == null || item.getIps().length == 0)
|
||||
) {
|
||||
records.add(record);
|
||||
}
|
||||
}
|
||||
if (records.size() > 0) {
|
||||
try {
|
||||
mHttpDnsConfig.getDbWorker().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mDBHelper.insertOrUpdate(records);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新ip, 一般用于更新ip的顺序
|
||||
*/
|
||||
public void update(String host, RequestIpType type, String cacheKey, String[] ips) {
|
||||
switch (type) {
|
||||
case v4:
|
||||
updateInner(host, RequestIpType.v4, cacheKey, ips);
|
||||
break;
|
||||
case v6:
|
||||
updateInner(host, RequestIpType.v6, cacheKey, ips);
|
||||
break;
|
||||
default:
|
||||
HttpDnsLog.e("update both is impossible for " + host);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取之前保存的解析结果
|
||||
*/
|
||||
public HTTPDNSResultWrapper getIps(String host, RequestIpType type, String cacheKey) {
|
||||
ensureReadFromDB();
|
||||
|
||||
ResolveHostCache cache = mCacheGroup.getCache(cacheKey);
|
||||
return cache.getResult(host, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅清除内容缓存
|
||||
*/
|
||||
public void clearMemoryCache() {
|
||||
mCacheGroup.clearAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅清除非主站域名的内存缓存
|
||||
*/
|
||||
public void clearMemoryCacheForHostWithoutFixedIP() {
|
||||
mCacheGroup.clearAll(new ArrayList<String>(getAllHostWithoutFixedIP().keySet()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有已解析结果
|
||||
*/
|
||||
public void clear() {
|
||||
final List<HostRecord> recordsToBeDeleted = mCacheGroup.clearAll();
|
||||
if (mEnableCache && recordsToBeDeleted.size() > 0) {
|
||||
try {
|
||||
mHttpDnsConfig.getDbWorker().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mDBHelper.delete(recordsToBeDeleted);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定域名的已解析结果
|
||||
*/
|
||||
public void clear(ArrayList<String> hosts) {
|
||||
if (hosts == null || hosts.size() == 0) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
final List<HostRecord> recordsToBeDeleted = mCacheGroup.clearAll(hosts);
|
||||
if (recordsToBeDeleted.size() > 0 && mEnableCache) {
|
||||
try {
|
||||
mHttpDnsConfig.getDbWorker().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mDBHelper.delete(recordsToBeDeleted);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前所有已缓存结果的域名
|
||||
*/
|
||||
public HashMap<String, RequestIpType> getAllHostWithoutFixedIP() {
|
||||
HashMap<String, RequestIpType> result = mCacheGroup.getAllHostFromNetworkCaches();
|
||||
for (String host : mHostListWhichIpFixed) {
|
||||
result.remove(host);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置 本地缓存开关,触发缓存读取逻辑
|
||||
*/
|
||||
public void setCachedIPEnabled(boolean enable, long expiredThresholdMillis) {
|
||||
mEnableCache = enable;
|
||||
mExpiredThresholdMillis = expiredThresholdMillis;
|
||||
try {
|
||||
mHttpDnsConfig.getDbWorker().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ensureReadFromDB();
|
||||
}
|
||||
});
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置自定义ttl的接口,用于控制缓存的时长
|
||||
*/
|
||||
public void setCacheTtlChanger(CacheTtlChanger changer) {
|
||||
mCacheTtlChanger = changer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主站域名,主站域名的缓存策略和其它域名不同
|
||||
* 1. 内存缓存不轻易清除
|
||||
* 2. 默认本地缓存,本地缓存的ttl有效
|
||||
*/
|
||||
public void setHostListWhichIpFixed(List<String> hostListWhichIpFixed) {
|
||||
this.mHostListWhichIpFixed.clear();
|
||||
if (hostListWhichIpFixed != null) {
|
||||
this.mHostListWhichIpFixed.addAll(hostListWhichIpFixed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存组,供外部访问网络缓存
|
||||
*
|
||||
* @return 缓存组
|
||||
*/
|
||||
public ResolveHostCacheGroup getCacheGroup() {
|
||||
return mCacheGroup;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,636 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.HTTPDNSResult;
|
||||
import com.alibaba.sdk.android.httpdns.HTTPDNSResultWrapper;
|
||||
import com.alibaba.sdk.android.httpdns.HttpDnsCallback;
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.impl.HostResolveLocker;
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsConfig;
|
||||
import com.alibaba.sdk.android.httpdns.log.HttpDnsLog;
|
||||
import com.alibaba.sdk.android.httpdns.observable.ObservableConstants;
|
||||
import com.alibaba.sdk.android.httpdns.observable.event.CallSdkApiEvent;
|
||||
import com.alibaba.sdk.android.httpdns.ranking.IPRankingCallback;
|
||||
import com.alibaba.sdk.android.httpdns.ranking.IPRankingService;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpException;
|
||||
import com.alibaba.sdk.android.httpdns.request.RequestCallback;
|
||||
import com.alibaba.sdk.android.httpdns.utils.CommonUtil;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
/**
|
||||
* 域名解析服务
|
||||
*/
|
||||
public class ResolveHostService {
|
||||
|
||||
private final HttpDnsConfig mHttpDnsConfig;
|
||||
private final ResolveHostResultRepo mResultRepo;
|
||||
private final ResolveHostRequestHandler mRequestHandler;
|
||||
private final IPRankingService mIpIPRankingService;
|
||||
private final HostFilter mFilter;
|
||||
private boolean mEnableExpiredIp = true;
|
||||
private final HostResolveLocker mAsyncLocker;
|
||||
private final HostResolveLocker mLocker;
|
||||
|
||||
public ResolveHostService(HttpDnsConfig config, IPRankingService ipIPRankingService,
|
||||
ResolveHostRequestHandler requestHandler,
|
||||
ResolveHostResultRepo repo, HostFilter filter,
|
||||
HostResolveLocker locker) {
|
||||
this.mHttpDnsConfig = config;
|
||||
this.mIpIPRankingService = ipIPRankingService;
|
||||
this.mRequestHandler = requestHandler;
|
||||
this.mResultRepo = repo;
|
||||
this.mFilter = filter;
|
||||
this.mAsyncLocker = locker;
|
||||
this.mLocker = new HostResolveLocker();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析域名
|
||||
*/
|
||||
public HTTPDNSResult resolveHostSyncNonBlocking(final String host, final RequestIpType type,
|
||||
final Map<String, String> extras,
|
||||
final String cacheKey) {
|
||||
if (mFilter.isFiltered(host)) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("request host " + host + ", which is filtered");
|
||||
}
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d(
|
||||
"sync non blocking request host " + host + " with type " + type + " extras : " + CommonUtil.toString(
|
||||
extras) + " cacheKey " + cacheKey);
|
||||
}
|
||||
HTTPDNSResultWrapper result = mResultRepo.getIps(host, type, cacheKey);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("host " + host + " result is " + CommonUtil.toString(result));
|
||||
}
|
||||
if (result == null || result.isExpired()) {
|
||||
if (type == RequestIpType.both) {
|
||||
// 过滤掉 未过期的请求
|
||||
HTTPDNSResultWrapper resultV4 = mResultRepo.getIps(host, RequestIpType.v4, cacheKey);
|
||||
HTTPDNSResultWrapper resultV6 = mResultRepo.getIps(host, RequestIpType.v6, cacheKey);
|
||||
boolean v4Invalid =
|
||||
resultV4 == null || resultV4.isExpired();
|
||||
boolean v6Invalid =
|
||||
resultV6 == null || resultV6.isExpired();
|
||||
if (v4Invalid && v6Invalid) {
|
||||
// 都过期,不过滤
|
||||
asyncResolveHostInner(host, type, extras, cacheKey);
|
||||
} else if (v4Invalid) {
|
||||
// 仅v4过期
|
||||
asyncResolveHostInner(host, RequestIpType.v4, extras, cacheKey);
|
||||
} else if (v6Invalid) {
|
||||
// 仅v6过期
|
||||
asyncResolveHostInner(host, RequestIpType.v6, extras, cacheKey);
|
||||
}
|
||||
} else {
|
||||
asyncResolveHostInner(host, type, extras, cacheKey);
|
||||
}
|
||||
}
|
||||
if (result != null && (!result.isExpired() || mEnableExpiredIp)) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i(
|
||||
"request host " + host + " for " + type + " and return " + result.toString()
|
||||
+ " immediately");
|
||||
}
|
||||
|
||||
addCallSdkApiEvent(getCallSdkApiEvent(ObservableConstants.RESOLVE_API_SYN_NON_BLOCKING, host, type, true, result, start),
|
||||
result);
|
||||
return result.getHTTPDNSResult();
|
||||
} else {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i("request host " + host + " and return empty immediately");
|
||||
}
|
||||
|
||||
addCallSdkApiEvent(getCallSdkApiEvent(ObservableConstants.RESOLVE_API_SYN_NON_BLOCKING, host, type, false, result, start),
|
||||
null);
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
private void asyncResolveHostInner(final String host, final RequestIpType type,
|
||||
Map<String, String> extras, final String cacheKey) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i("async start request for " + host + " " + type);
|
||||
}
|
||||
|
||||
if (mAsyncLocker.beginResolve(host, type, cacheKey)) {
|
||||
final String region = mHttpDnsConfig.getRegion();
|
||||
mRequestHandler.requestResolveHost(host, type, extras, cacheKey,
|
||||
new RequestCallback<ResolveHostResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final ResolveHostResponse resolveHostResponse) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i("ip request for " + host + " " + type + " return "
|
||||
+ resolveHostResponse.toString());
|
||||
}
|
||||
mResultRepo.save(region, resolveHostResponse, cacheKey);
|
||||
|
||||
if (type == RequestIpType.v4 || type == RequestIpType.both) {
|
||||
for (final ResolveHostResponse.HostItem item :
|
||||
resolveHostResponse.getItems()) {
|
||||
if (item.getIpType() == RequestIpType.v4) {
|
||||
mIpIPRankingService.probeIpv4(host, item.getIps(),
|
||||
new IPRankingCallback() {
|
||||
@Override
|
||||
public void onResult(String host, String[] sortedIps) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i(
|
||||
"ip probe for " + host + " " + type + " return "
|
||||
+ CommonUtil.translateStringArray(sortedIps));
|
||||
}
|
||||
mResultRepo.update(host, RequestIpType.v4, cacheKey,
|
||||
sortedIps);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
mAsyncLocker.endResolve(host, type, cacheKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFail(Throwable throwable) {
|
||||
HttpDnsLog.w("ip request for " + host + " fail", throwable);
|
||||
if (throwable instanceof Exception) {
|
||||
String query = "4";
|
||||
if (type == RequestIpType.v6) {
|
||||
query = "6";
|
||||
}else if (type == RequestIpType.both) {
|
||||
query = "4,6";
|
||||
}
|
||||
String errorMsg = (throwable instanceof HttpException) ? throwable.getMessage() : throwable.toString();
|
||||
HttpDnsLog.w("RESOLVE FAIL, HOST:" + host + ", QUERY:" + query
|
||||
+ ", Msg:" + errorMsg);
|
||||
}
|
||||
if (throwable instanceof HttpException
|
||||
&& ((HttpException)throwable).shouldCreateEmptyCache()) {
|
||||
ArrayList<String> targetHost = new ArrayList<>();
|
||||
targetHost.add(host);
|
||||
ResolveHostResponse emptyResponse = ResolveHostResponse.createEmpty(
|
||||
targetHost, type, 60 * 60);
|
||||
mResultRepo.save(region, emptyResponse, cacheKey);
|
||||
}
|
||||
|
||||
mAsyncLocker.endResolve(host, type, cacheKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析域名
|
||||
*/
|
||||
public HTTPDNSResult resolveHostSync(final String host, final RequestIpType type,
|
||||
final Map<String, String> extras, final String cacheKey) {
|
||||
if (mFilter.isFiltered(host)) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("request host " + host + ", which is filtered");
|
||||
}
|
||||
return degradationLocalDns(host);
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("sync request host " + host + " with type " + type + " extras : "
|
||||
+ CommonUtil.toString(extras) + " cacheKey " + cacheKey);
|
||||
}
|
||||
HTTPDNSResultWrapper result = mResultRepo.getIps(host, type, cacheKey);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("host " + host + " result in cache is " + CommonUtil.toString(result));
|
||||
}
|
||||
|
||||
if (result != null && (!result.isExpired() || mEnableExpiredIp)) {
|
||||
//有缓存,如果没过期或者允许使用过期解析结果,直接返回
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i(
|
||||
"request host " + host + " for " + type + " and return " + result.toString()
|
||||
+ " immediately");
|
||||
}
|
||||
|
||||
if (result.isExpired()) {
|
||||
//如果缓存已经过期了,发起异步解析更新缓存
|
||||
asyncResolveHostInner(host, type, extras, cacheKey);
|
||||
}
|
||||
|
||||
//可能是空结果,如果开启降级local dns,走local dns解析
|
||||
if ((result.getIps() == null || result.getIps().length == 0)
|
||||
&& (result.getIpv6s() == null || result.getIpv6s().length == 0)) {
|
||||
if (mHttpDnsConfig.isEnableDegradationLocalDns()) {
|
||||
return degradationLocalDns(host);
|
||||
}
|
||||
}
|
||||
|
||||
addCallSdkApiEvent(getCallSdkApiEvent(ObservableConstants.RESOLVE_API_SYNC, host, type, true, result, start), result);
|
||||
|
||||
return result.getHTTPDNSResult();
|
||||
}
|
||||
|
||||
//如果没有缓存,或者有过期缓存但是不允许使用过期缓存
|
||||
if (result == null || result.isExpired()) {
|
||||
// 没有缓存,或者缓存过期,或者是从数据库读取的 需要解析
|
||||
if (type == RequestIpType.both) {
|
||||
// 过滤掉 未过期的请求
|
||||
HTTPDNSResultWrapper resultV4 = mResultRepo.getIps(host, RequestIpType.v4, cacheKey);
|
||||
HTTPDNSResultWrapper resultV6 = mResultRepo.getIps(host, RequestIpType.v6, cacheKey);
|
||||
boolean v4Invalid =
|
||||
resultV4 == null || resultV4.isExpired();
|
||||
boolean v6Invalid =
|
||||
resultV6 == null || resultV6.isExpired();
|
||||
if (v4Invalid && v6Invalid) {
|
||||
// 都过期,不过滤
|
||||
syncResolveHostInner(host, type, extras, cacheKey, result);
|
||||
} else if (v4Invalid) {
|
||||
// 仅v4过期
|
||||
syncResolveHostInner(host, RequestIpType.v4, extras, cacheKey, result);
|
||||
} else if (v6Invalid) {
|
||||
// 仅v6过期
|
||||
syncResolveHostInner(host, RequestIpType.v6, extras, cacheKey, result);
|
||||
}
|
||||
} else {
|
||||
syncResolveHostInner(host, type, extras, cacheKey, result);
|
||||
}
|
||||
}
|
||||
|
||||
CallSdkApiEvent callSdkApiEvent = getCallSdkApiEvent(ObservableConstants.RESOLVE_API_SYNC, host, type, false, result, start);
|
||||
result = mResultRepo.getIps(host, type, cacheKey);
|
||||
|
||||
if (result != null && (!result.isExpired() || mEnableExpiredIp)) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i(
|
||||
"request host " + host + " for " + type + " and return " + result.toString()
|
||||
+ " after request");
|
||||
}
|
||||
|
||||
//可能是空结果,如果开启降级local dns,走local dns解析
|
||||
if ((result.getIps() == null || result.getIps().length == 0)
|
||||
&& (result.getIpv6s() == null || result.getIpv6s().length == 0)) {
|
||||
if (mHttpDnsConfig.isEnableDegradationLocalDns()) {
|
||||
return degradationLocalDns(host);
|
||||
}
|
||||
}
|
||||
|
||||
addCallSdkApiEvent(callSdkApiEvent, result);
|
||||
return result.getHTTPDNSResult();
|
||||
} else {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i("request host " + host + " and return empty after request");
|
||||
}
|
||||
|
||||
addCallSdkApiEvent(callSdkApiEvent, null);
|
||||
return degradationLocalDns(host);
|
||||
}
|
||||
}
|
||||
|
||||
private HTTPDNSResult degradationLocalDns(String host) {
|
||||
if (mHttpDnsConfig.isEnableDegradationLocalDns()) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("request host " + host + " via local dns");
|
||||
}
|
||||
try {
|
||||
InetAddress[] addresses = InetAddress.getAllByName(host);
|
||||
if (addresses != null && addresses.length > 0) {
|
||||
List<String> ips = new ArrayList<>();
|
||||
List<String> ipv6s = new ArrayList<>();
|
||||
for (InetAddress address : addresses) {
|
||||
if (address instanceof Inet4Address) {
|
||||
ips.add(address.getHostAddress());
|
||||
} else if (address instanceof Inet6Address) {
|
||||
ipv6s.add(address.getHostAddress());
|
||||
}
|
||||
}
|
||||
|
||||
HTTPDNSResult result = new HTTPDNSResult(host, ips.toArray(new String[0]), ipv6s.toArray(new String[0]), null, false, false, true);
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i("request host " + host + " via local dns return " + result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.e("failed request host " + host + " via local dns", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Constants.EMPTY;
|
||||
}
|
||||
|
||||
private void syncResolveHostInner(final String host, final RequestIpType type,
|
||||
Map<String, String> extras, final String cacheKey,
|
||||
HTTPDNSResultWrapper result) {
|
||||
long start = System.currentTimeMillis();
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i("sync start request for " + host + " " + type);
|
||||
}
|
||||
if (mLocker.beginResolve(host, type, cacheKey)) {
|
||||
final String region = mHttpDnsConfig.getRegion();
|
||||
// 没有正在进行的解析,发起新的解析
|
||||
mRequestHandler.requestResolveHost(host, type, extras, cacheKey,
|
||||
new RequestCallback<ResolveHostResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final ResolveHostResponse resolveHostResponse) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i("ip request for " + host + " " + type + " return "
|
||||
+ resolveHostResponse.toString());
|
||||
}
|
||||
mResultRepo.save(region, resolveHostResponse, cacheKey);
|
||||
|
||||
if (type == RequestIpType.v4 || type == RequestIpType.both) {
|
||||
for (final ResolveHostResponse.HostItem item :
|
||||
resolveHostResponse.getItems()) {
|
||||
if (item.getIpType() == RequestIpType.v4) {
|
||||
mIpIPRankingService.probeIpv4(host, item.getIps(),
|
||||
new IPRankingCallback() {
|
||||
@Override
|
||||
public void onResult(String host, String[] sortedIps) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i(
|
||||
"ip probe for " + host + " " + type + " return "
|
||||
+ CommonUtil.translateStringArray(sortedIps));
|
||||
}
|
||||
mResultRepo.update(host, RequestIpType.v4, cacheKey,
|
||||
sortedIps);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
mLocker.endResolve(host, type, cacheKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFail(Throwable throwable) {
|
||||
HttpDnsLog.w("ip request for " + host + " fail", throwable);
|
||||
if (throwable instanceof Exception) {
|
||||
String query = "4";
|
||||
if (type == RequestIpType.v6) {
|
||||
query = "6";
|
||||
}else if (type == RequestIpType.both) {
|
||||
query = "4,6";
|
||||
}
|
||||
String errorMsg = (throwable instanceof HttpException) ? throwable.getMessage() : throwable.toString();
|
||||
HttpDnsLog.w("RESOLVE FAIL, HOST:" + host + ", QUERY:" + query
|
||||
+ ", Msg:" + errorMsg);
|
||||
}
|
||||
if (throwable instanceof HttpException
|
||||
&& ((HttpException)throwable).shouldCreateEmptyCache()) {
|
||||
ArrayList<String> targetHost = new ArrayList<>();
|
||||
targetHost.add(host);
|
||||
ResolveHostResponse emptyResponse = ResolveHostResponse.createEmpty(
|
||||
targetHost, type, 60 * 60);
|
||||
mResultRepo.save(region, emptyResponse, cacheKey);
|
||||
}
|
||||
|
||||
mLocker.endResolve(host, type, cacheKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (result == null || !mEnableExpiredIp) {
|
||||
// 有结果,但是过期了,不允许返回过期结果,等请求结束
|
||||
/* 同步锁超时时间不能大于5s,不论请求是否已结束,保证同步锁都会在至多5s内结束 */
|
||||
int timeout = mHttpDnsConfig.getTimeout();
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("the httpDnsConfig timeout is: " + timeout);
|
||||
}
|
||||
//锁的超时上限不能大于5s
|
||||
timeout = Math.min(timeout, Constants.SYNC_TIMEOUT_MAX_LIMIT);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("final timeout is: " + timeout);
|
||||
}
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("wait for request finish");
|
||||
}
|
||||
try {
|
||||
boolean waitResult = mLocker.await(host, type, cacheKey, timeout, TimeUnit.MILLISECONDS);
|
||||
if (!waitResult) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("lock await timeout finished");
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 不管什么情况释放锁
|
||||
mLocker.endResolve(host, type, cacheKey);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("sync resolve time is: " + (System.currentTimeMillis() - start));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void resolveHostAsync(final String host, final RequestIpType type, final Map<String, String> extras,
|
||||
final String cacheKey, HttpDnsCallback callback) {
|
||||
if (mFilter.isFiltered(host)) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("request host " + host + ", which is filtered");
|
||||
}
|
||||
if (callback != null) {
|
||||
if (mHttpDnsConfig.isEnableDegradationLocalDns()) {
|
||||
mHttpDnsConfig.getResolveWorker().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callback.onHttpDnsCompleted(degradationLocalDns(host));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback.onHttpDnsCompleted(Constants.EMPTY);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d(
|
||||
"async request host " + host + " with type " + type + " extras : " + CommonUtil.toString(
|
||||
extras) + " cacheKey " + cacheKey);
|
||||
}
|
||||
HTTPDNSResultWrapper result = mResultRepo.getIps(host, type, cacheKey);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("host " + host + " result in cache is " + CommonUtil.toString(result));
|
||||
}
|
||||
|
||||
if (result != null && (!result.isExpired() || mEnableExpiredIp)) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i(
|
||||
"request host " + host + " for " + type + " and return " + result.toString()
|
||||
+ " immediately");
|
||||
}
|
||||
|
||||
if (result.isExpired()) {
|
||||
//如果缓存已经过期了,发起异步解析更新缓存
|
||||
asyncResolveHostInner(host, type, extras, cacheKey);
|
||||
}
|
||||
|
||||
//可能是空结果,如果开启降级local dns,走local dns解析
|
||||
if ((result.getIps() == null || result.getIps().length == 0)
|
||||
&& (result.getIpv6s() == null || result.getIpv6s().length == 0)) {
|
||||
if (mHttpDnsConfig.isEnableDegradationLocalDns()) {
|
||||
mHttpDnsConfig.getResolveWorker().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callback.onHttpDnsCompleted(degradationLocalDns(host));
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
addCallSdkApiEvent(getCallSdkApiEvent(ObservableConstants.RESOLVE_API_ASYNC, host, type, true, result, start), result);
|
||||
|
||||
if (callback != null) {
|
||||
callback.onHttpDnsCompleted(result.getHTTPDNSResult());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CallSdkApiEvent callSdkApiEvent = getCallSdkApiEvent(ObservableConstants.RESOLVE_API_ASYNC, host, type, false, result, start);
|
||||
//没有可用的解析结果,需要发起请求
|
||||
if (type == RequestIpType.both) {
|
||||
// 过滤掉 未过期的请求
|
||||
HTTPDNSResultWrapper resultV4 = mResultRepo.getIps(host, RequestIpType.v4, cacheKey);
|
||||
HTTPDNSResultWrapper resultV6 = mResultRepo.getIps(host, RequestIpType.v6, cacheKey);
|
||||
boolean v4Invalid =
|
||||
resultV4 == null || resultV4.isExpired();
|
||||
boolean v6Invalid =
|
||||
resultV6 == null || resultV6.isExpired();
|
||||
if (v4Invalid && v6Invalid) {
|
||||
// 都过期,不过滤
|
||||
asyncResolveHost(host, type, extras, cacheKey, callback, callSdkApiEvent);
|
||||
} else if (v4Invalid) {
|
||||
// 仅v4过期
|
||||
asyncResolveHost(host, RequestIpType.v4, extras, cacheKey, callback, callSdkApiEvent);
|
||||
} else if (v6Invalid) {
|
||||
// 仅v6过期
|
||||
asyncResolveHost(host, RequestIpType.v6, extras, cacheKey, callback, callSdkApiEvent);
|
||||
}
|
||||
} else {
|
||||
asyncResolveHost(host, type, extras, cacheKey, callback, callSdkApiEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private void asyncResolveHost(String host, RequestIpType type, Map<String, String> extras,
|
||||
String cacheKey, HttpDnsCallback callback, CallSdkApiEvent event) {
|
||||
mHttpDnsConfig.getResolveWorker().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
asyncResolveHostInner(host, type, extras, cacheKey);
|
||||
|
||||
/* 同步锁超时时间不能大于5s,不论请求是否已结束,保证同步锁都会在至多5s内结束 */
|
||||
int timeout = mHttpDnsConfig.getTimeout();
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("the httpDnsConfig timeout is: " + timeout);
|
||||
}
|
||||
//锁的超时上限不能大于5s
|
||||
timeout = Math.min(timeout, Constants.SYNC_TIMEOUT_MAX_LIMIT);
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("final timeout is: " + timeout);
|
||||
}
|
||||
//发起异步请求,有无并发,统一在这里通过锁同步机制,从缓存中处理解析结果
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("wait for request finish");
|
||||
}
|
||||
try {
|
||||
boolean waitResult = mAsyncLocker.await(host, type, cacheKey, timeout, TimeUnit.MILLISECONDS);
|
||||
if (!waitResult) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.d("lock await timeout finished");
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
HttpDnsLog.e(e.getMessage(), e);
|
||||
}
|
||||
|
||||
HTTPDNSResultWrapper result = mResultRepo.getIps(host, type, cacheKey);
|
||||
|
||||
addCallSdkApiEvent(event, result);
|
||||
|
||||
if (result != null && !result.isExpired()) {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i(
|
||||
"request host " + host + " for " + type + " and return " + result.toString()
|
||||
+ " after request");
|
||||
}
|
||||
|
||||
if (callback != null) {
|
||||
//可能是空结果,如果开启降级local dns,走local dns解析
|
||||
if ((result.getIps() == null || result.getIps().length == 0)
|
||||
&& (result.getIpv6s() == null || result.getIpv6s().length == 0)) {
|
||||
if (mHttpDnsConfig.isEnableDegradationLocalDns()) {
|
||||
callback.onHttpDnsCompleted(degradationLocalDns(host));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
callback.onHttpDnsCompleted(result.getHTTPDNSResult());
|
||||
}
|
||||
} else {
|
||||
if (HttpDnsLog.isPrint()) {
|
||||
HttpDnsLog.i("request host " + host + " and return empty after request");
|
||||
}
|
||||
|
||||
if (callback != null) {
|
||||
callback.onHttpDnsCompleted(degradationLocalDns(host));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setEnableExpiredIp(boolean enableExpiredIp) {
|
||||
this.mEnableExpiredIp = enableExpiredIp;
|
||||
}
|
||||
|
||||
private void addCallSdkApiEvent(CallSdkApiEvent event, HTTPDNSResultWrapper result) {
|
||||
event.setCostTime((int) (System.currentTimeMillis() - event.getTimestamp()));
|
||||
|
||||
if (result != null) {
|
||||
event.setServerIp(result.getServerIp());
|
||||
event.setHttpDnsIps(result.getIps(), result.getIpv6s());
|
||||
if (TextUtils.isEmpty(event.getHttpDnsIps())) {
|
||||
event.setStatusCode(204);
|
||||
} else {
|
||||
event.setResultStatus(ObservableConstants.NOT_EMPTY_RESULT);
|
||||
event.setStatusCode(200);
|
||||
}
|
||||
} else {
|
||||
event.setHttpDnsIps(null, null);
|
||||
event.setStatusCode(204);
|
||||
}
|
||||
|
||||
mHttpDnsConfig.getObservableManager().addObservableEvent(event);
|
||||
}
|
||||
|
||||
private CallSdkApiEvent getCallSdkApiEvent(int api, String host, RequestIpType type, boolean hitCache, HTTPDNSResultWrapper cacheResult, long start) {
|
||||
CallSdkApiEvent callSdkApiEvent = new CallSdkApiEvent(start);
|
||||
callSdkApiEvent.setRequestType(type);
|
||||
callSdkApiEvent.setHostName(host);
|
||||
callSdkApiEvent.setInvokeApi(api);
|
||||
int cacheScene = cacheResult == null ? ObservableConstants.CACHE_NONE : (hitCache ? (cacheResult.isExpired() ? ObservableConstants.CACHE_EXPIRED_USE : ObservableConstants.CACHE_NOT_EXPIRED) : ObservableConstants.CACHE_EXPIRED_NOT_USE);
|
||||
callSdkApiEvent.setCacheScene(cacheScene);
|
||||
|
||||
return callSdkApiEvent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
import com.alibaba.sdk.android.httpdns.RequestIpType;
|
||||
import com.alibaba.sdk.android.httpdns.impl.HttpDnsConfig;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpException;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequestConfig;
|
||||
import com.alibaba.sdk.android.httpdns.request.HttpRequestWatcher;
|
||||
import com.alibaba.sdk.android.httpdns.serverip.RegionServerScheduleService;
|
||||
import com.alibaba.sdk.android.httpdns.utils.Constants;
|
||||
|
||||
/**
|
||||
* 请求失败时,切换当前Region的服务IP,服务IP都切换过,更新服务IP
|
||||
*/
|
||||
public class ShiftServerWatcher implements HttpRequestWatcher.Watcher {
|
||||
|
||||
private final HttpDnsConfig mHttpDnsConfig;
|
||||
private final RegionServerScheduleService mScheduleService;
|
||||
private final StatusControl mStatusControl;
|
||||
private long mBeginRequestTime;
|
||||
|
||||
public ShiftServerWatcher(HttpDnsConfig config, RegionServerScheduleService scheduleService,
|
||||
StatusControl statusControl) {
|
||||
this.mHttpDnsConfig = config;
|
||||
this.mScheduleService = scheduleService;
|
||||
this.mStatusControl = statusControl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(HttpRequestConfig config) {
|
||||
mBeginRequestTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(HttpRequestConfig requestConfig, Object data) {
|
||||
if (requestConfig.getIpType() == RequestIpType.v6) {
|
||||
if (this.mHttpDnsConfig.getCurrentServer().markOkServerV6(requestConfig.getIp(),
|
||||
requestConfig.getPort())) {
|
||||
if (mStatusControl != null) {
|
||||
mStatusControl.turnUp();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.mHttpDnsConfig.getCurrentServer().markOkServer(requestConfig.getIp(),
|
||||
requestConfig.getPort())) {
|
||||
if (mStatusControl != null) {
|
||||
mStatusControl.turnUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFail(HttpRequestConfig requestConfig, Throwable throwable) {
|
||||
long cost = System.currentTimeMillis() - mBeginRequestTime;
|
||||
// 是否切换服务IP, 超过超时时间,我们也切换ip,花费时间太长,说明这个ip可能也有问题
|
||||
if (shouldShiftServer(throwable) || cost > requestConfig.getTimeout()) {
|
||||
// 切换和更新请求的服务IP
|
||||
boolean isBackToFirstServer;
|
||||
if (requestConfig.getIpType() == RequestIpType.v6) {
|
||||
isBackToFirstServer = this.mHttpDnsConfig.getCurrentServer().shiftServerV6(
|
||||
requestConfig.getIp(), requestConfig.getPort());
|
||||
requestConfig.setIp(this.mHttpDnsConfig.getCurrentServer().getServerIpForV6());
|
||||
requestConfig.setPort(this.mHttpDnsConfig.getCurrentServer().getPortForV6());
|
||||
} else {
|
||||
isBackToFirstServer = this.mHttpDnsConfig.getCurrentServer().shiftServer(
|
||||
requestConfig.getIp(), requestConfig.getPort());
|
||||
requestConfig.setIp(this.mHttpDnsConfig.getCurrentServer().getServerIp());
|
||||
requestConfig.setPort(this.mHttpDnsConfig.getCurrentServer().getPort());
|
||||
}
|
||||
|
||||
// 所有服务IP都尝试过了,通知上层进一步处理
|
||||
if (isBackToFirstServer && mScheduleService != null) {
|
||||
mScheduleService.updateRegionServerIps(Constants.UPDATE_REGION_SERVER_SCENES_SERVER_UNAVAILABLE);
|
||||
}
|
||||
if (mStatusControl != null) {
|
||||
mStatusControl.turnDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldShiftServer(Throwable throwable) {
|
||||
if (throwable instanceof HttpException) {
|
||||
return ((HttpException)throwable).shouldShiftServer();
|
||||
}
|
||||
// 除了特定的一些错误(sdk问题或者客户配置问题,不是服务网络问题),都切换服务IP,这边避免个别服务节点真的访问不上
|
||||
// 一方面尽可能提高可用性,另一方面当客户发生异常时,也方便根据服务IP来判断是否是真的无网络,因为多个服务IP都访问不上的可能性较低
|
||||
// 还有一方面是 sniff模式是在切换服务IP的前提下触发的,这样也提高的sniff模式触发几率,在真的网络异常时,降低网络请求频次
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.alibaba.sdk.android.httpdns.resolve;
|
||||
|
||||
public class SniffException extends Exception {
|
||||
|
||||
public SniffException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user