content: /*
   * 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.aliyun.api.gateway.demo.util;
  
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.UnsupportedEncodingException;
  import java.net.MalformedURLException;
  import java.net.URLEncoder;
  import java.nio.ByteBuffer;
  import java.nio.channels.Channels;
  import java.nio.channels.ReadableByteChannel;
  import java.nio.channels.WritableByteChannel;
  import java.security.KeyManagementException;
  import java.security.NoSuchAlgorithmException;
  import java.security.cert.X509Certificate;
  import java.util.ArrayList;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import javax.net.ssl.SSLContext;
  import javax.net.ssl.TrustManager;
  import javax.net.ssl.X509TrustManager;
  
  import org.apache.commons.lang.StringUtils;
  import org.apache.http.Header;
  import org.apache.http.HttpResponse;
  import org.apache.http.NameValuePair;
  import org.apache.http.client.HttpClient;
  import org.apache.http.client.entity.UrlEncodedFormEntity;
  import org.apache.http.client.methods.HttpDelete;
  import org.apache.http.client.methods.HttpGet;
  import org.apache.http.client.methods.HttpPost;
  import org.apache.http.client.methods.HttpPut;
  import org.apache.http.conn.ClientConnectionManager;
  import org.apache.http.conn.scheme.Scheme;
  import org.apache.http.conn.scheme.SchemeRegistry;
  import org.apache.http.conn.ssl.SSLSocketFactory;
  import org.apache.http.entity.ByteArrayEntity;
  import org.apache.http.entity.StringEntity;
  import org.apache.http.impl.client.DefaultHttpClient;
  import org.apache.http.message.BasicNameValuePair;
  import org.apache.http.params.CoreConnectionPNames;
  
  import com.aliyun.api.gateway.demo.Response;
  import com.aliyun.api.gateway.demo.constant.Constants;
  import com.aliyun.api.gateway.demo.constant.ContentType;
  import com.aliyun.api.gateway.demo.constant.HttpHeader;
  import com.aliyun.api.gateway.demo.constant.HttpMethod;
  import com.aliyun.api.gateway.demo.constant.SystemHeader;
  
  /**
   * Http工具类
   */
  public class HttpUtil {
      /**
       * HTTP GET
       * @param host
       * @param path
       * @param connectTimeout
       * @param headers
       * @param querys
       * @param signHeaderPrefixList
       * @param appKey
       * @param appSecret
       * @return
       * @throws Exception
       */
      public static Response httpGet(String host, String path, int connectTimeout, Map<String, String> headers, Map<String, String> querys, List<String> signHeaderPrefixList, String appKey, String appSecret)
              throws Exception {
          headers = initialBasicHeader(HttpMethod.GET, path, headers, querys, null, signHeaderPrefixList, appKey, appSecret);
  
          HttpClient httpClient = wrapClient(host);
          httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, getTimeout(connectTimeout));
  
          HttpGet get = new HttpGet(initUrl(host, path, querys));
  
          for (Map.Entry<String, String> e : headers.entrySet()) {
              get.addHeader(e.getKey(), MessageDigestUtil.utf8ToIso88591(e.getValue()));
          }
  
          return convert(httpClient.execute(get));
      }
  
      /**
       * HTTP POST表单
       * @param host
       * @param path
       * @param connectTimeout
       * @param headers
       * @param querys
       * @param bodys
       * @param signHeaderPrefixList
       * @param appKey
       * @param appSecret
       * @return
       * @throws Exception
       */
      public static Response httpPost(String host, String path, int connectTimeout, Map<String, String> headers, Map<String, String> querys, Map<String, String> bodys, List<String> signHeaderPrefixList, String appKey, String appSecret)
              throws Exception {
          if (headers == null) {
              headers = new HashMap<String, String>();
          }
  
          headers.put(HttpHeader.HTTP_HEADER_CONTENT_TYPE, ContentType.CONTENT_TYPE_FORM);
  
          headers = initialBasicHeader(HttpMethod.POST, path, headers, querys, bodys, signHeaderPrefixList, appKey, appSecret);
  
          HttpClient httpClient = wrapClient(host);
          httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, getTimeout(connectTimeout));
  
          HttpPost post = new HttpPost(initUrl(host, path, querys));
          for (Map.Entry<String, String> e : headers.entrySet()) {
              post.addHeader(e.getKey(), MessageDigestUtil.utf8ToIso88591(e.getValue()));
          }
  
          UrlEncodedFormEntity formEntity = buildFormEntity(bodys);
          if (formEntity != null) {
              post.setEntity(formEntity);
          }
  
          return convert(httpClient.execute(post));
      }
  
      /**
       * Http POST 字符串
       * @param host
       * @param path
       * @param connectTimeout
       * @param headers
       * @param querys
       * @param body
       * @param signHeaderPrefixList
       * @param appKey
       * @param appSecret
       * @return
       * @throws Exception
       */
      public static Response httpPost(String host, String path, int connectTimeout, Map<String, String> headers, Map<String, String> querys, String body, List<String> signHeaderPrefixList, String appKey, String appSecret)
              throws Exception {
      	headers = initialBasicHeader(HttpMethod.POST, path, headers, querys, null, signHeaderPrefixList, appKey, appSecret);
  
      	HttpClient httpClient = wrapClient(host);
          httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, getTimeout(connectTimeout));
  
          HttpPost post = new HttpPost(initUrl(host, path, querys));
          for (Map.Entry<String, String> e : headers.entrySet()) {
              post.addHeader(e.getKey(), MessageDigestUtil.utf8ToIso88591(e.getValue()));
          }
  
          if (StringUtils.isNotBlank(body)) {
              post.setEntity(new StringEntity(body, Constants.ENCODING));
  
          }
  
          return convert(httpClient.execute(post));
      }
  
      /**
       * HTTP POST 字节数组
       * @param host
       * @param path
       * @param connectTimeout
       * @param headers
       * @param querys
       * @param bodys
       * @param signHeaderPrefixList
       * @param appKey
       * @param appSecret
       * @return
       * @throws Exception
       */
      public static Response httpPost(String host, String path, int connectTimeout, Map<String, String> headers, Map<String, String> querys, byte[] bodys, List<String> signHeaderPrefixList, String appKey, String appSecret)
              throws Exception {
      	headers = initialBasicHeader(HttpMethod.POST, path, headers, querys, null, signHeaderPrefixList, appKey, appSecret);
  
      	HttpClient httpClient = wrapClient(host);
          httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, getTimeout(connectTimeout));
  
          HttpPost post = new HttpPost(initUrl(host, path, querys));
          for (Map.Entry<String, String> e : headers.entrySet()) {
              post.addHeader(e.getKey(), MessageDigestUtil.utf8ToIso88591(e.getValue()));
          }
  
          if (bodys != null) {
              post.setEntity(new ByteArrayEntity(bodys));
          }
  
          return convert(httpClient.execute(post));
      }
  
      /**
       * HTTP PUT 字符串
       * @param host
       * @param path
       * @param connectTimeout
       * @param headers
       * @param querys
       * @param body
       * @param signHeaderPrefixList
       * @param appKey
       * @param appSecret
       * @return
       * @throws Exception
       */
      public static Response httpPut(String host, String path, int connectTimeout, Map<String, String> headers, Map<String, String> querys, String body, List<String> signHeaderPrefixList, String appKey, String appSecret)
              throws Exception {
      	headers = initialBasicHeader(HttpMethod.PUT, path, headers, querys, null, signHeaderPrefixList, appKey, appSecret);
  
      	HttpClient httpClient = wrapClient(host);
          httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, getTimeout(connectTimeout));
  
          HttpPut put = new HttpPut(initUrl(host, path, querys));
          for (Map.Entry<String, String> e : headers.entrySet()) {
              put.addHeader(e.getKey(), MessageDigestUtil.utf8ToIso88591(e.getValue()));
          }
  
          if (StringUtils.isNotBlank(body)) {
              put.setEntity(new StringEntity(body, Constants.ENCODING));
  
          }
  
          return convert(httpClient.execute(put));
      }
  
      /**
       * HTTP PUT字节数组
       * @param host
       * @param path
       * @param connectTimeout
       * @param headers
       * @param querys
       * @param bodys
       * @param signHeaderPrefixList
       * @param appKey
       * @param appSecret
       * @return
       * @throws Exception
       */
      public static Response httpPut(String host, String path, int connectTimeout, Map<String, String> headers, Map<String, String> querys, byte[] bodys, List<String> signHeaderPrefixList, String appKey, String appSecret)
              throws Exception {
      	headers = initialBasicHeader(HttpMethod.PUT, path, headers, querys, null, signHeaderPrefixList, appKey, appSecret);
  
      	HttpClient httpClient = wrapClient(host);
          httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, getTimeout(connectTimeout));
  
          HttpPut put = new HttpPut(initUrl(host, path, querys));
          for (Map.Entry<String, String> e : headers.entrySet()) {
          	put.addHeader(e.getKey(), MessageDigestUtil.utf8ToIso88591(e.getValue()));
          }
  
          if (bodys != null) {
          	put.setEntity(new ByteArrayEntity(bodys));
          }
  
          return convert(httpClient.execute(put));
      }
  
      /**
       * HTTP DELETE
       * @param host
       * @param path
       * @param connectTimeout
       * @param headers
       * @param querys
       * @param signHeaderPrefixList
       * @param appKey
       * @param appSecret
       * @return
       * @throws Exception
       */
      public static Response httpDelete(String host, String path, int connectTimeout, Map<String, String> headers, Map<String, String> querys, List<String> signHeaderPrefixList, String appKey, String appSecret)
              throws Exception {
          headers = initialBasicHeader(HttpMethod.DELETE, path, headers, querys, null, signHeaderPrefixList, appKey, appSecret);
  
          HttpClient httpClient = wrapClient(host);
          httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, getTimeout(connectTimeout));
  
          HttpDelete delete = new HttpDelete(initUrl(host, path, querys));
          for (Map.Entry<String, String> e : headers.entrySet()) {
              delete.addHeader(e.getKey(), MessageDigestUtil.utf8ToIso88591(e.getValue()));
          }
  
          return convert(httpClient.execute(delete));
      }
  
      /**
       * 构建FormEntity
       * 
       * @param formParam
       * @return
       * @throws UnsupportedEncodingException
       */
      private static UrlEncodedFormEntity buildFormEntity(Map<String, String> formParam)
              throws UnsupportedEncodingException {
          if (formParam != null) {
              List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
  
              for (String key : formParam.keySet()) {
                  nameValuePairList.add(new BasicNameValuePair(key, formParam.get(key)));
              }
              UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, Constants.ENCODING);
              formEntity.setContentType(ContentType.CONTENT_TYPE_FORM);
              return formEntity;
          }
  
          return null;
      }
      
      private static String initUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException {
      	StringBuilder sbUrl = new StringBuilder();
      	sbUrl.append(host);
      	if (!StringUtils.isBlank(path)) {
      		sbUrl.append(path);
          }
      	if (null != querys) {
      		StringBuilder sbQuery = new StringBuilder();
          	for (Map.Entry<String, String> query : querys.entrySet()) {
          		if (0 < sbQuery.length()) {
          			sbQuery.append(Constants.SPE3);
          		}
          		if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
          			sbQuery.append(query.getValue());
                  }
          		if (!StringUtils.isBlank(query.getKey())) {
          			sbQuery.append(query.getKey());
          			if (!StringUtils.isBlank(query.getValue())) {
          				sbQuery.append(Constants.SPE4);
          				sbQuery.append(URLEncoder.encode(query.getValue(), Constants.ENCODING));
          			}        			
                  }
          	}
          	if (0 < sbQuery.length()) {
          		sbUrl.append(Constants.SPE5).append(sbQuery);
          	}
          }
      	
      	return sbUrl.toString();
      }
      	
  
      /**
       * 初始化基础Header
       * @param method
       * @param path
       * @param headers
       * @param querys
       * @param bodys
       * @param signHeaderPrefixList
       * @param appKey
       * @param appSecret
       * @return
       * @throws MalformedURLException
       */
      private static Map<String, String> initialBasicHeader(String method, String path,
                                                            Map<String, String> headers, 
                                                            Map<String, String> querys,
                                                            Map<String, String> bodys,
                                                            List<String> signHeaderPrefixList,
                                                            String appKey, String appSecret)
              throws MalformedURLException {
          if (headers == null) {
              headers = new HashMap<String, String>();
          }
  
          headers.put(SystemHeader.X_CA_TIMESTAMP, String.valueOf(new Date().getTime()));
          //headers.put(SystemHeader.X_CA_NONCE, UUID.randomUUID().toString());
          headers.put(SystemHeader.X_CA_KEY, appKey);
          headers.put(SystemHeader.X_CA_SIGNATURE,
                  SignUtil.sign(appSecret, method, path, headers, querys, bodys, signHeaderPrefixList));
  
          return headers;
      }
  
      /**
       * 读取超时时间
       * 
       * @param timeout
       * @return
       */
      private static int getTimeout(int timeout) {
          if (timeout == 0) {
              return Constants.DEFAULT_TIMEOUT;
          }
  
          return timeout;
      }
      
      private static Response convert(HttpResponse response) throws IOException {
      	Response res = new Response(); 
      	
      	if (null != response) {
      		res.setStatusCode(response.getStatusLine().getStatusCode());
      		for (Header header : response.getAllHeaders()) {
      			res.setHeader(header.getName(), MessageDigestUtil.iso88591ToUtf8(header.getValue()));
              }
      		
      		res.setContentType(res.getHeader("Content-Type"));
      		res.setRequestId(res.getHeader("X-Ca-Request-Id"));
      		res.setErrorMessage(res.getHeader("X-Ca-Error-Message"));
      		res.setBody(readStreamAsStr(response.getEntity().getContent()));
      		
      	} else {
      		//服务器无回应
      		res.setStatusCode(500);
      		res.setErrorMessage("No Response");
      	}
      	
      	return res;
      }
  
  
  	/**
  	 * 将流转换为字符串
  	 *
  	 * @param is
  	 * @return
  	 * @throws IOException
  	 */
  	public static String readStreamAsStr(InputStream is) throws IOException {
  	    ByteArrayOutputStream bos = new ByteArrayOutputStream();
  	    WritableByteChannel dest = Channels.newChannel(bos);
  	    ReadableByteChannel src = Channels.newChannel(is);
  	    ByteBuffer bb = ByteBuffer.allocate(4096);
  	
  	    while (src.read(bb) != -1) {
  	        bb.flip();
  	        dest.write(bb);
  	        bb.clear();
  	    }
  	    src.close();
  	    dest.close();
  	
  	    return new String(bos.toByteArray(), Constants.ENCODING);
  	}
  
  	private static HttpClient wrapClient(String host) {
  		HttpClient httpClient = new DefaultHttpClient();
  		if (host.startsWith("https://")) {
  			sslClient(httpClient);
  		}
  		
  		return httpClient;
  	}
  	
  	private static void sslClient(HttpClient httpClient) {
          try {
              SSLContext ctx = SSLContext.getInstance("TLS");
              X509TrustManager tm = new X509TrustManager() {
                  public X509Certificate[] getAcceptedIssuers() {
                      return null;
                  }
                  public void checkClientTrusted(X509Certificate[] xcs, String str) {
                  	
                  }
                  public void checkServerTrusted(X509Certificate[] xcs, String str) {
                  	
                  }
              };
              ctx.init(null, new TrustManager[] { tm }, null);
              SSLSocketFactory ssf = new SSLSocketFactory(ctx);
              ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
              ClientConnectionManager ccm = httpClient.getConnectionManager();
              SchemeRegistry registry = ccm.getSchemeRegistry();
              registry.register(new Scheme("https", 443, ssf));
          } catch (KeyManagementException ex) {
              throw new RuntimeException(ex);
          } catch (NoSuchAlgorithmException ex) {
          	throw new RuntimeException(ex);
          }
      }
  }