Skip to content

Instantly share code, notes, and snippets.

@wanghq
Created May 2, 2017 17:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wanghq/5eea665d8046b5dac5b718dfdae4d4e1 to your computer and use it in GitHub Desktop.
Save wanghq/5eea665d8046b5dac5b718dfdae4d4e1 to your computer and use it in GitHub Desktop.
Client to call FC
/*
* 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.");
}
}
}
/*
* 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