package com.qiniu.android.common;
import com.qiniu.android.dns.DnsManager;
import com.qiniu.android.http.Client;
import com.qiniu.android.http.CompletionHandler;
import com.qiniu.android.http.ResponseInfo;
import com.qiniu.android.storage.UpToken;
import com.qiniu.android.utils.UrlSafeBase64;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created by long on 2016/9/29.
*/
public final class AutoZone extends Zone {
private Map<ZoneIndex, ZoneInfo> zones = new ConcurrentHashMap<>();
private Client client = new Client();
private final String ucServer;
/**
* 自动判断机房
*/
public static final AutoZone autoZone = new AutoZone(null);
public AutoZone(DnsManager dns) {
this("https://uc.qbox.me", dns);
}
AutoZone(String ucServer, DnsManager dns) {
this.ucServer = ucServer;
}
private void getZoneJsonAsync(ZoneIndex index, CompletionHandler handler) {
String address = ucServer + "/v2/query?ak=" + index.accessKey + "&bucket=" + index.bucket;
client.asyncGet(address, null, UpToken.NULL, handler);
}
private ResponseInfo getZoneJsonSync(ZoneIndex index) {
String address = ucServer + "/v2/query?ak=" + index.accessKey + "&bucket=" + index.bucket;
return client.syncGet(address, null);
}
// only for test public
ZoneInfo zoneInfo(String ak, String bucket) {
ZoneIndex index = new ZoneIndex(ak, bucket);
return zones.get(index);
}
// only for test public
ZoneInfo queryByToken(String token) {
try {
// http://developer.qiniu.com/article/developer/security/upload-token.html
// http://developer.qiniu.com/article/developer/security/put-policy.html
String[] strings = token.split(":");
String ak = strings[0];
String policy = new String(UrlSafeBase64.decode(strings[2]), Constants.UTF_8);
JSONObject obj = new JSONObject(policy);
String scope = obj.getString("scope");
String bkt = scope.split(":")[0];
return zoneInfo(ak, bkt);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//async
void preQueryIndex(final ZoneIndex index, final QueryHandler complete) {
if (index == null) {
complete.onFailure(ResponseInfo.InvalidToken);
return;
}
ZoneInfo info = zones.get(index);
if (info != null) {
complete.onSuccess();
return;
}
getZoneJsonAsync(index, new CompletionHandler() {
@Override
public void complete(ResponseInfo info, JSONObject response) {
if (info.isOK() && response != null) {
try {
ZoneInfo info2 = ZoneInfo.buildFromJson(response);
zones.put(index, info2);
complete.onSuccess();
} catch (JSONException e) {
e.printStackTrace();
complete.onFailure(ResponseInfo.NetworkError);
}
}
}
});
}
//sync
boolean preQueryIndex(final ZoneIndex index) {
boolean success = false;
if (index != null) {
ZoneInfo info = zones.get(index);
if (info != null) {
success = true;
} else {
try {
ResponseInfo responseInfo = getZoneJsonSync(index);
ZoneInfo info2 = ZoneInfo.buildFromJson(responseInfo.response);
zones.put(index, info2);
success = true;
} catch (JSONException e) {
e.printStackTrace();
}
}
}
return success;
}
@Override
public synchronized String upHost(String token, boolean useHttps, String frozenDomain) {
ZoneInfo info = queryByToken(token);
if (info != null) {
return super.upHost(info, useHttps, frozenDomain);
} else {
return null;
}
}
@Override
public void preQuery(String token, QueryHandler complete) {
ZoneIndex index = ZoneIndex.getFromToken(token);
preQueryIndex(index, complete);
}
@Override
public boolean preQuery(String token) {
ZoneIndex index = ZoneIndex.getFromToken(token);
return preQueryIndex(index);
}
@Override
public synchronized void frozenDomain(String upHostUrl) {
if (upHostUrl != null) {
URI uri = URI.create(upHostUrl);
//frozen domain
String frozenDomain = uri.getHost();
ZoneInfo zoneInfo = null;
for (Map.Entry<ZoneIndex, ZoneInfo> entry : this.zones.entrySet()) {
ZoneInfo eachZoneInfo = entry.getValue();
if (eachZoneInfo.upDomainsList.contains(frozenDomain)) {
zoneInfo = eachZoneInfo;
break;
}
}
if (zoneInfo != null) {
zoneInfo.frozenDomain(frozenDomain);
}
}
}
static class ZoneIndex {
final String accessKey;
final String bucket;
ZoneIndex(String accessKey, String bucket) {
this.accessKey = accessKey;
this.bucket = bucket;
}
static ZoneIndex getFromToken(String token) {
// http://developer.qiniu.com/article/developer/security/upload-token.html
// http://developer.qiniu.com/article/developer/security/put-policy.html
String[] strings = token.split(":");
String ak = strings[0];
String policy = null;
try {
policy = new String(UrlSafeBase64.decode(strings[2]), Constants.UTF_8);
JSONObject obj = new JSONObject(policy);
String scope = obj.getString("scope");
String bkt = scope.split(":")[0];
return new ZoneIndex(ak, bkt);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public int hashCode() {
return accessKey.hashCode() * 37 + bucket.hashCode();
}
public boolean equals(Object obj) {
return obj == this || !(obj == null || !(obj instanceof ZoneIndex))
&& ((ZoneIndex) obj).accessKey.equals(accessKey) && ((ZoneIndex) obj).bucket.equals(bucket);
}
}
}