Created
May 2, 2017 17:11
-
-
Save wanghq/5eea665d8046b5dac5b718dfdae4d4e1 to your computer and use it in GitHub Desktop.
Client to call FC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
* | |
*/ | |
package com.aliyuncs.fc.client; | |
import com.aliyuncs.fc.constants.HeaderKeys; | |
import java.io.IOException; | |
import java.io.UnsupportedEncodingException; | |
import java.net.SocketTimeoutException; | |
import java.security.InvalidKeyException; | |
import java.security.NoSuchAlgorithmException; | |
import java.util.HashMap; | |
import java.util.Map; | |
import com.aliyuncs.fc.auth.FcSignatureComposer; | |
import com.aliyuncs.fc.config.Config; | |
import com.aliyuncs.fc.exceptions.ClientException; | |
import com.aliyuncs.fc.exceptions.ServerException; | |
import com.aliyuncs.fc.http.HttpRequest; | |
import com.aliyuncs.fc.http.HttpResponse; | |
import com.google.gson.Gson; | |
import com.aliyuncs.fc.auth.AcsURLEncoder; | |
import com.aliyuncs.fc.model.PrepareUrl; | |
import com.aliyuncs.fc.utils.ParameterHelper; | |
public class DefaultFcClient { | |
public final static Boolean AUTO_RETRY = true; | |
public final static int MAX_RETRIES = 3; | |
private final Config config; | |
public DefaultFcClient(Config config) { | |
this.config = config; | |
} | |
public String composeUrl(String endpoint, Map<String, String> queries) | |
throws UnsupportedEncodingException { | |
Map<String, String> mapQueries = queries; | |
StringBuilder urlBuilder = new StringBuilder(""); | |
urlBuilder.append(endpoint); | |
if (-1 == urlBuilder.indexOf("?")) { | |
urlBuilder.append("?"); | |
} else if (!urlBuilder.toString().endsWith("?")) { | |
urlBuilder.append("&"); | |
} | |
String url = urlBuilder.toString(); | |
if (queries != null && queries.size() > 0) { | |
String query = concatQueryString(mapQueries); | |
url = urlBuilder.append(query).toString(); | |
} | |
if (url.endsWith("?") || url.endsWith("&")) { | |
url = url.substring(0, url.length() - 1); | |
} | |
return url; | |
} | |
/** | |
* concate query string parameters (e.g. &name=foo) | |
* @param parameters | |
* @return concatenated query string | |
* @throws UnsupportedEncodingException | |
*/ | |
public String concatQueryString(Map<String, String> parameters) | |
throws UnsupportedEncodingException { | |
if (null == parameters) { | |
return null; | |
} | |
StringBuilder urlBuilder = new StringBuilder(""); | |
for (Map.Entry<String, String> entry : parameters.entrySet()) { | |
String key = entry.getKey(); | |
String val = entry.getValue(); | |
urlBuilder.append(AcsURLEncoder.encode(key)); | |
if (val != null) { | |
urlBuilder.append("=").append(AcsURLEncoder.encode(val)); | |
} | |
urlBuilder.append("&"); | |
} | |
int strIndex = urlBuilder.length(); | |
if (parameters.size() > 0) { | |
urlBuilder.deleteCharAt(strIndex - 1); | |
} | |
return urlBuilder.toString(); | |
} | |
public Map<String, String> getHeader(Map<String, String> header, byte[] Payload, String form) { | |
if (header == null) { | |
header = new HashMap<String, String>(); | |
} | |
header.put("Host", config.getHost()); | |
header.put("User-Agent", config.getUserAgent()); | |
header.put("Accept", "application/json"); | |
header.put("Content-Type", form); | |
header.put("x-fc-account-id", config.getUid()); | |
if (Payload != null) { | |
header.put("Content-MD5", ParameterHelper.md5Sum(Payload)); | |
} | |
return header; | |
} | |
public PrepareUrl signRequest(HttpRequest request, String form, String method) | |
throws InvalidKeyException, IllegalStateException, UnsupportedEncodingException, NoSuchAlgorithmException { | |
Map<String, String> imutableMap = null; | |
if (request.getHeader() != null) { | |
imutableMap = request.getHeader(); | |
} else { | |
imutableMap = new HashMap<String, String>(); | |
} | |
String accessKeyId = config.getAccessKeyID(); | |
String accessSecret = config.getAccessKeySecret(); | |
imutableMap = FcSignatureComposer.refreshSignParameters(imutableMap); | |
// Get relevent path | |
String uri = request.getPath(); | |
// Set all headers | |
imutableMap = getHeader(imutableMap, request.getPayload(), form); | |
// Sign URL | |
String strToSign = FcSignatureComposer.composeStringToSign(method, uri, imutableMap); | |
String signature = FcSignatureComposer.signString(strToSign, accessSecret); | |
// Set signature | |
imutableMap.put("Authorization", "FC " + accessKeyId + ":" + signature); | |
String allPath = composeUrl(config.getEndpoint() + request.getPath(), | |
request.getQueryParams()); | |
return new PrepareUrl(allPath, imutableMap); | |
} | |
public HttpResponse doAction(HttpRequest request, String form, String method) | |
throws ClientException, ServerException { | |
request.validate(); | |
try { | |
PrepareUrl prepareUrl = signRequest(request, form, method); | |
int retryTimes = 1; | |
HttpResponse response = HttpResponse | |
.getResponse(prepareUrl.getUrl(), prepareUrl.getHeader(), request, method); | |
while (500 <= response.getStatus() && AUTO_RETRY && retryTimes < MAX_RETRIES) { | |
prepareUrl = signRequest(request, form, method); | |
response = HttpResponse | |
.getResponse(prepareUrl.getUrl(), prepareUrl.getHeader(), request, method); | |
retryTimes++; | |
} | |
if (response.getStatus() >= 500) { | |
String stringContent = response.getContent() == null ? "" : new String(response.getContent()); | |
ServerException se = new Gson().fromJson(stringContent, ServerException.class); | |
se.setStatusCode(response.getStatus()); | |
se.setRequestId(response.getHeaderValue(HeaderKeys.REQUEST_ID)); | |
throw se; | |
} else if (response.getStatus() >= 300) { | |
String stringContent = response.getContent() == null ? "" : new String(response.getContent()); | |
ClientException ce = new Gson().fromJson(stringContent, ClientException.class); | |
ce.setStatusCode(response.getStatus()); | |
ce.setRequestId(response.getHeaderValue(HeaderKeys.REQUEST_ID)); | |
throw ce; | |
} | |
return response; | |
} catch (InvalidKeyException exp) { | |
throw new ClientException("SDK.InvalidAccessSecret", "Speicified access secret is not valid."); | |
} catch (SocketTimeoutException exp){ | |
throw new ClientException("SDK.ServerUnreachable", "SocketTimeoutException has occurred on a socket read or accept."); | |
} catch (IOException exp) { | |
throw new ClientException("SDK.ServerUnreachable", "Server unreachable: " + exp.toString()); | |
} catch (NoSuchAlgorithmException exp) { | |
throw new ClientException("SDK.InvalidMD5Algorithm", "MD5 hash is not supported by client side."); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
*/ | |
package com.aliyuncs.fc.auth; | |
import java.io.UnsupportedEncodingException; | |
import java.security.InvalidKeyException; | |
import java.security.NoSuchAlgorithmException; | |
import java.util.HashMap; | |
import java.util.Map; | |
import java.util.TreeMap; | |
import com.aliyuncs.fc.utils.Base64Helper; | |
import com.aliyuncs.fc.utils.ParameterHelper; | |
import javax.crypto.Mac; | |
import javax.crypto.spec.SecretKeySpec; | |
/** | |
* TODO: add javadoc | |
*/ | |
public class FcSignatureComposer { | |
private static FcSignatureComposer composer = null; | |
protected final static String HEADER_SEPARATOR = "\n"; | |
private final static String AGLORITHM_NAME = "HmacSHA256"; | |
public static Map<String, String> refreshSignParameters(Map<String, String> parameters) { | |
if (parameters == null) { | |
parameters = new HashMap<String, String>(); | |
} | |
parameters.put("Date", ParameterHelper.getRFC2616Date(null)); | |
return parameters; | |
} | |
public static String composeStringToSign(String method, String path, | |
Map<String, String> headers) { | |
StringBuilder sb = new StringBuilder(); | |
sb.append(method).append(HEADER_SEPARATOR); | |
if (headers.get("Content-MD5") != null) { | |
sb.append(headers.get("Content-MD5")); | |
} | |
sb.append(HEADER_SEPARATOR); | |
if (headers.get("Content-Type") != null) { | |
sb.append(headers.get("Content-Type")); | |
} | |
sb.append(HEADER_SEPARATOR); | |
if (headers.get("Date") != null) { | |
sb.append(headers.get("Date")); | |
} | |
sb.append(HEADER_SEPARATOR); | |
sb.append(buildCanonicalHeaders(headers, "x-fc")); | |
sb.append(path); | |
return sb.toString(); | |
} | |
public static String buildCanonicalHeaders(Map<String, String> headers, String headerBegin) { | |
Map<String, String> sortMap = new TreeMap<String, String>(); | |
for (Map.Entry<String, String> e : headers.entrySet()) { | |
String key = e.getKey().toLowerCase(); | |
String val = e.getValue(); | |
if (key.startsWith(headerBegin)) { | |
sortMap.put(key, val); | |
} | |
} | |
StringBuilder headerBuilder = new StringBuilder(); | |
for (Map.Entry<String, String> e : sortMap.entrySet()) { | |
headerBuilder.append(e.getKey()); | |
headerBuilder.append(':').append(e.getValue()); | |
headerBuilder.append(HEADER_SEPARATOR); | |
} | |
return headerBuilder.toString(); | |
} | |
public static String signString(String source, String accessSecret) | |
throws InvalidKeyException, IllegalStateException { | |
try { | |
Mac mac = Mac.getInstance(AGLORITHM_NAME); | |
mac.init(new SecretKeySpec( | |
accessSecret.getBytes(AcsURLEncoder.URL_ENCODING), AGLORITHM_NAME)); | |
byte[] signData = mac.doFinal(source.getBytes(AcsURLEncoder.URL_ENCODING)); | |
return Base64Helper.encode(signData); | |
} catch (NoSuchAlgorithmException e) { | |
throw new RuntimeException("HMAC-SHA1 not supported."); | |
} catch (UnsupportedEncodingException e) { | |
throw new RuntimeException("UTF-8 not supported."); | |
} | |
} | |
public String getSignerName() { | |
return "HMAC-SHA256"; | |
} | |
public String getSignerVersion() { | |
return "1.0"; | |
} | |
public static FcSignatureComposer getComposer() { | |
if (null == composer) { | |
composer = new FcSignatureComposer(); | |
} | |
return composer; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment