com.shenit.commons.utils.HttpUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.shenit.commons.utils.HttpUtils.java

Source

/***********************************************************************************************************************
 * 
 * Copyright (C) 2013, 2014 by huanju (http://www.yy.com)
 * http://www.yy.com/
 *
 ***********************************************************************************************************************
 *
 * Licensed 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.shenit.commons.utils;

import com.google.common.net.MediaType;
import com.shenit.commons.mvc.model.JsonSerializable;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.http.*;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.Map.Entry;

public final class HttpUtils {
    private static final Logger LOG = LoggerFactory.getLogger(HttpUtils.class);
    private static final String DEFAULT_TEST_PROXY_URL = "http://www.baidu.com";

    public static final String JSONP_CALLBACK = "callback";
    public static final String ENC_UTF8 = "utf-8";
    // nginx?IP
    public static final String HEADER_REAL_IP = "X-Real-IP";
    // nginx?IP
    public static final String HEADER_FORWARDED_FOR = "X-Forwarded-For";
    // ?
    public static final String HEADER_USER_AGENT = "User-Agent";

    public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";

    public static final String QUERY_CHAR = "?";
    public static final String HASH = "#";
    public static final String AMP = "&";
    public static final String EQ = "=";
    public static final String SLASH = "/";

    public static final int DEFUALT_CONNECTION_TIMEOUT = 30000;
    public static final int DEFAULT_SO_TIMEOUT = 30000;

    public static final String HTTP = "http";
    public static final String HTTPS = "https";

    public static final String URL_HTTP_PREFIX = "http://";
    public static final String URL_SSL_PREFIX = "https://";

    public static final MediaType APPLICATION_JSON = MediaType.create("application", "json");
    public static final MediaType PLAIN_TEXT = MediaType.create("plain", "text");
    public static final ContentType CONTENT_TYPE_PLAIN_TEXT = ContentType.create("plain/text", ENC_UTF8);
    public static final String STR_CONTENT_TYPE_FILE = ContentType.create("application/octet-stream", ENC_UTF8)
            .toString();
    public static final String STR_CONTENT_TYPE_JSON = APPLICATION_JSON.toString();

    private static final String CALLBACK_PATTERN = "%s(%s);";

    public static final String HASH_CHAR = "#";

    private static final Object SECRET_STRING = "******";
    private static final String PASS_PATTERN = "pass(?:word)?";

    public static enum Encoding {
        DEFAULT("US-ASCII"), UTF8("utf-8"), UTF16("utf-16");
        String code;

        private Encoding(String name) {
            this.code = name;
        }

        public String getCode() {
            return code;
        }
    }

    /**
     * ?
     * @param proxy
     * @param address
     * @return
     */
    public static long testProxy(Proxy proxy, String address, int times) {
        try {
            return testProxy(proxy, new URL(address), 0, 0, times);
        } catch (MalformedURLException e) {
            LOG.warn("[testProxy] Could not parse url -> {}", address, e);
        }
        return -1;
    }

    /**
     * ?
     * @param proxy
     * @param address
     * @param cTimeout ?
     * @param rTimeout ??
     * @return
     */
    public static long testProxy(Proxy proxy, String address, int cTimeout, int rTimeout, int testTimes) {
        try {
            return testProxy(proxy, new URL(address), cTimeout, rTimeout, testTimes);
        } catch (MalformedURLException e) {
            LOG.warn("[testProxy] Could not parse url -> {}", address, e);
        }
        return -1;
    }

    /**
     * ?IP
     * 
     * @param req
     * @return
     */
    public static String getClientIp(HttpServletRequest req) {
        String ip = req.getHeader(HEADER_REAL_IP);
        if (StringUtils.isBlank(ip)) {
            ip = req.getHeader(HEADER_FORWARDED_FOR);
        }
        if (StringUtils.isBlank(ip)) {
            ip = req.getRemoteAddr();
        }
        return ip;
    }

    /**
     * Get?
     * 
     * @param url
     *            ?
     * @return
     */
    public static String getAsString(String url) {
        HttpGet request = new HttpGet(url);
        HttpClientBuilder clientBuilder = HttpClientBuilder.create();
        CloseableHttpClient client = clientBuilder.build();
        LOG.info("[getAsString] execute url >> {}", url);
        try {
            CloseableHttpResponse resp = client.execute(request);
            int status = resp.getStatusLine().getStatusCode();
            if (status == 200) {
                String result = EntityUtils.toString(resp.getEntity(), ENC_UTF8);
                LOG.info("[getAsString] execute result for {} >> {}", url, result);
                return StringUtils.trim(result);
            } else {
                LOG.error("execute with incorrect status code >> {}", status);
            }
        } catch (IOException e) {
            LOG.error("execute with exception >> {}", e.getMessage());
        }
        return null;
    }

    /**
     * ???query string
     * 
     * @param params
     * @return
     */
    public static String encodeParams(Map<String, String> params) {
        return encodeParams(params, ENC_UTF8);
    }

    /**
     * ??
     * 
     * @param params
     * @param enc
     *            ??
     * @return
     */
    public static String encodeParams(Map<String, String> params, String enc) {
        if (params == null)
            return null;
        // copy one
        StringBuilder builder = new StringBuilder();
        String key;
        for (Iterator<String> keyIter = params.keySet().iterator(); keyIter.hasNext();) {
            key = keyIter.next();
            builder.append(key).append(EQ)
                    .append(StringUtils.isEmpty(enc) ? params.get(key) : encodeUrl(params.get(key)));
            if (keyIter.hasNext())
                builder.append(AMP);
        }
        return builder.toString();
    }

    /**
     * Request url result with get method
     * 
     * @param request
     * @param cTimeout
     * @param sotimeout
     * @return
     */
    public static String execute(HttpRequestBase request, int cTimeout, int sotimeout) {
        return execute(request, null, cTimeout, sotimeout);
    }

    /**
     * Execute url without proxy
     * @param request
     * @param context
     * @param cTimeout
     * @param soTimeout
     * @return
     */
    public static String execute(HttpRequestBase request, HttpContext context, int cTimeout, int soTimeout) {
        return execute(request, context, null, cTimeout, soTimeout);
    }

    /**
     * Request url result with get method
     * 
     * @param request
     * @param context
     * @param proxy
     * @param cTimeout
     * @param soTimeout
     * @return
     */
    public static String execute(HttpRequestBase request, HttpContext context, HttpHost proxy, int cTimeout,
            int soTimeout) {
        RequestConfig config = RequestConfig.custom().setConnectTimeout(cTimeout)
                .setConnectionRequestTimeout(soTimeout).build();
        request.setConfig(config);

        String result = null;
        CloseableHttpClient client = HttpClientBuilder.create().setProxy(proxy).build();
        try {
            if (LOG.isDebugEnabled())
                LOG.debug("[execute] request -> \n>>>>>>>>>>>>>>>>\n{}\n<<<<<<<<<<<<<<<<\n", dumpRequest(request));
            HttpResponse resp = client.execute(request, context);
            result = EntityUtils.toString(resp.getEntity(), getContentEncoding(resp));
            if (LOG.isDebugEnabled())
                LOG.debug("[execute] response -> \n>>>>>>>>>>>>>>>>\n{}\n<<<<<<<<<<<<<<<<\n", result);
        } catch (ClientProtocolException e) {
            LOG.warn("[execute]Error when calling url >> " + request.getURI(), e);
        } catch (IOException e) {
            LOG.warn("[execute]IOException when calling url >> " + request.getURI(), e);
        }
        return result;
    }

    /**
     * 
     * 
     * @param request
     * @return
     */
    public static String execute(HttpRequestBase request) {
        return execute(request, null, DEFUALT_CONNECTION_TIMEOUT, DEFAULT_SO_TIMEOUT);
    }

    /**
     * 
     * 
     * @param request
     * @return
     */
    public static String execute(HttpRequestBase request, HttpContext context) {
        return execute(request, context, DEFUALT_CONNECTION_TIMEOUT, DEFAULT_SO_TIMEOUT);
    }

    /**
     * Execute a url
     * @param request
     * @param context
     * @param proxy
     * @return
     */
    public static String execute(HttpRequestBase request, HttpContext context, HttpHost proxy) {
        return execute(request, context, proxy, DEFUALT_CONNECTION_TIMEOUT, DEFAULT_SO_TIMEOUT);
    }

    /**
     * Encode url to specific character set
     * 
     * @param str
     * @param charset
     * @return
     */
    public static String encodeUrl(String str, String charset) {
        if (str == null)
            return null;
        charset = charset == null ? ENC_UTF8 : charset;
        String url = str;
        try {
            url = URLEncoder.encode(str, charset);
        } catch (UnsupportedEncodingException e) {
            LOG.warn("[encodeUrl]Could not encode url. Error >>> {}", e.getMessage());
        }
        return url;
    }

    /**
     * Encode url using utf-8
     * 
     * @param str
     * @return
     */
    public static String encodeUrl(String str) {
        return encodeUrl(str, ENC_UTF8);
    }

    public static HttpUriRequest multipartFormArrayList(HttpPost request, ArrayList<Object> keyVals) {
        int length = keyVals.size();
        Object[] kvs = new Object[length];
        for (int i = 0; i < length; i++) {
            kvs[i] = keyVals.get(i);
        }
        return multipartForm(request, kvs);
    }

    public static HttpUriRequest multipartFormArrayLists(HttpPost request, List<String[]> keyVals) {
        int length = keyVals.size();
        Object[] kvs = new Object[length * 2];
        for (int i = 0; i < length; i += 2) {
            String[] item = keyVals.get(i);
            kvs[i] = item[0];
            kvs[i + 1] = item[1];
        }
        return multipartForm(request, kvs);
    }

    /**
     * Create a url encoded form
     * 
     * @param request
     *            Request object
     * @param keyVals
     *            Key and value pairs, the even(begins with 0) position params
     *            are key and the odds are values
     * @return
     */
    public static HttpPost urlEncodedForm(HttpPost request, Object... keyVals) {
        return urlEncodedForm(request, Encoding.UTF8, keyVals);
    }

    /**
     * Create a url encoded form
     * 
     * @param request
     *            Request object
     * @param enc
     *            Request body encoding, default is utf8
     * @param keyVals
     *            Key and value pairs, the even(begins with 0) position params
     *            are key and the odds are values
     * @return
     */
    public static HttpPost urlEncodedForm(HttpPost request, Encoding enc, Object... keyVals) {
        if (request == null || ValidationUtils.isEmpty(keyVals))
            return request;
        ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();
        for (int i = 0; i < keyVals.length; i += 2) {
            pairs.add(new BasicNameValuePair(ShenStrings.str(keyVals[i]),
                    i + 1 < keyVals.length ? ShenStrings.str(keyVals[i + 1]) : StringUtils.EMPTY));
        }
        try {
            request.setEntity(new UrlEncodedFormEntity(pairs, enc.code));
        } catch (UnsupportedEncodingException e) {
            LOG.warn("[urlEncodedForm]Fill data to form entity failed. Caused by {}", e.getMessage());
        }
        return request;
    }

    /**
     * Create a url encoded form using utf8 charset
     * 
     * @param request
     * @param keyVals
     * @return
     */
    public static <K, V> HttpPost urlEncodedFormByMap(HttpPost request, Map<K, V> keyVals) {
        return urlEncodedFormByMap(request, Encoding.UTF8, keyVals);
    }

    /**
     * Create a url encoded form
     * 
     * @param request
     *            Request object
     * @param enc
     *            Request body encoding, default is utf8
     * @param keyVals
     *            Key and value pairs, the even(begins with 0) position params
     *            are key and the odds are values
     * @return
     */
    public static <K, V> HttpPost urlEncodedFormByMap(HttpPost request, Encoding enc, Map<K, V> keyVals) {
        if (request == null || ValidationUtils.isEmpty(keyVals))
            return request;
        ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();
        for (Object key : keyVals.keySet()) {
            pairs.add(new BasicNameValuePair(ShenStrings.str(key), ShenStrings.str(keyVals.get(key))));
        }
        try {
            if (LOG.isDebugEnabled())
                LOG.debug("[urlEncodedFormByMap] enc:{} pairs:{}", new Object[] { enc.code, pairs });
            request.setEntity(new UrlEncodedFormEntity(pairs, enc.code));
        } catch (UnsupportedEncodingException e) {
            LOG.warn("[urlEncodedForm]Fill data to form entity failed. Caused by {}", e.getMessage());
        }
        return request;
    }

    /**
     * Create a multipart form
     * 
     * @param request
     *            Request object
     * @param keyVals
     *            Key and value pairs, the even(begins with 0) position params
     *            are key and the odds are values
     * @return
     */
    public static HttpUriRequest multipartForm(HttpPost request, Object... keyVals) {
        if (request == null || ValidationUtils.isEmpty(keyVals))
            return request;
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        boolean hasVal = false;

        String key;
        for (int i = 0; i < keyVals.length; i += 2) {
            key = ShenStrings.str(keyVals[i]);
            hasVal = i + 1 < keyVals.length;

            if (!hasVal || keyVals[i + 1] == null) {
                builder.addTextBody(key, StringUtils.EMPTY, CONTENT_TYPE_PLAIN_TEXT);
                break;
            }

            if (keyVals[i + 1].getClass().isAssignableFrom(File.class)) {
                builder.addPart(key, new FileBody((File) keyVals[i + 1]));
            } else {
                builder.addTextBody(key, keyVals[i + 1].toString(), CONTENT_TYPE_PLAIN_TEXT);
            }
        }
        request.setEntity(builder.build());
        return request;
    }

    /**
     * Post url
     * @param url
     * @return
     */
    public static String post(String url) {
        return execute(new HttpPost(url));
    }

    /**
     * 
     * @param url
     * @param body
     *            ?utf-8
     * @return
     */
    public static String post(String url, String body) {
        return post(url, body, DEFUALT_CONNECTION_TIMEOUT, DEFAULT_SO_TIMEOUT);
    }

    /**
     * Send a post request
     * @param url URL to visit
     * @param body Body to the request
     * @param connTimeout Connection time out time
     * @param socketTimeout
     * @return
     */
    public static String post(String url, String body, int connTimeout, int socketTimeout) {
        try {
            return post(url, body.getBytes(Encoding.UTF8.code), null, connTimeout, socketTimeout);
        } catch (UnsupportedEncodingException e) {
            String err = String.format("[post] error url:%s, body:%s", url, body);
            LOG.error(err, e);
        }

        return null;
    }

    /**
     * Send a post request.
     * @param url URL to send
     * @param body Body to send
     * @param proxy HttpProxy to use in request
     * @return
     */
    public static String post(String url, String body, HttpHost proxy) {
        try {
            return post(url, body.getBytes(Encoding.UTF8.code), proxy, DEFUALT_CONNECTION_TIMEOUT,
                    DEFAULT_SO_TIMEOUT);
        } catch (UnsupportedEncodingException e) {
            LOG.warn("[post] Illegal encoding not support"); //which should not happen
        }
        return null;
    }

    /**
     * Send a post request.
     * @param url URL to send
     * @param body Body to send
     * @param proxy HttpProxy to use in request
     * @return
     */
    public static String post(String url, byte[] body, HttpHost proxy) {
        return post(url, body, proxy, DEFUALT_CONNECTION_TIMEOUT, DEFAULT_SO_TIMEOUT);
    }

    /**
     * Send a post request.
     * @param url URL to send
     * @param body Body to send
     * @return
     */
    public static String post(String url, byte[] body) {
        return post(url, body, null, DEFUALT_CONNECTION_TIMEOUT, DEFAULT_SO_TIMEOUT);
    }

    /**
     * Send a post request
     * @param url URL to send
     * @param body Body to send
     * @param connectionTimeout Connection timeout
     * @param socketTimeout Socket timeout
     * @return
     */
    public static String post(String url, byte[] body, HttpHost proxy, int connectionTimeout, int socketTimeout) {
        HttpEntity entity = new ByteArrayEntity(body);
        HttpPost post = new HttpPost(url);
        post.setEntity(entity);
        return execute(post, null, proxy, connectionTimeout, socketTimeout);
    }

    /**
     * Execute a get request and return the string result
     * 
     * @param url
     * @return
     */
    public static String get(String url) {
        return execute(new HttpGet(url));
    }

    /**
     * Execute a get request and return the string result
     * 
     * @param url
     * @return
     */
    public static String get(String url, HttpContext context) {
        return execute(new HttpGet(url), context, null);
    }

    public static String get(String url, HttpContext context, HttpHost proxy) {
        return execute(new HttpGet(url), context, proxy);
    }

    /**
     * Get response content encoding value. With utf-8 as default value if not
     * exists
     * 
     * @param resp
     * @return
     */
    public static String getContentEncoding(HttpResponse resp) {
        return getContentEncoding(resp, ENC_UTF8);
    }

    /**
     * * Get response content encoding value. With <i>defaultEncoding</i> as
     * default value if not exists
     * 
     * @param resp
     * @param defaultEncoding
     * @return
     */
    public static String getContentEncoding(HttpResponse resp, String defaultEncoding) {
        String encode = defaultEncoding;
        if (resp != null && resp.getEntity() != null && resp.getEntity().getContentEncoding() != null) {
            encode = resp.getEntity().getContentEncoding().getValue();
        }
        return encode;
    }

    /**
     * Get client agent from http request.
     * 
     * @param req
     *            HttpServletRequest
     * @return
     */
    public static String getAgent(HttpServletRequest req) {
        return req.getHeader(HEADER_USER_AGENT);
    }

    /**
     * ?cookie
     * 
     * @param resp
     * @param copyToSession
     *            ??session
     * @param cookies
     */
    public static void save(HttpServletRequest req, HttpServletResponse resp, boolean copyToSession,
            Cookie... cookies) {
        HttpSession session = req.getSession(true);
        for (Cookie cookie : cookies) {
            resp.addCookie(cookie);
            if (copyToSession)
                session.setAttribute(cookie.getName(), cookie.getValue());
        }
    }

    /**
     * ?cookie
     * 
     * @param req
     * @param resp
     * @param andSession
     *            ??session
     * @param names
     */
    public static void purge(HttpServletRequest req, HttpServletResponse resp, boolean andSession,
            String... names) {

        if (andSession)
            purgeSessions(req, names);
        purgeCookies(req, resp, names);
    }

    /**
     * sessionnames?session
     * 
     * @param req
     * @param names
     */
    public static void purgeSessions(HttpServletRequest req, String... names) {
        HttpSession session = req.getSession(false);
        if (session == null) {
            if (LOG.isDebugEnabled())
                LOG.debug("[purgeSessions] No sessions to purge");
            return;
        }
        if (ValidationUtils.isEmpty(names)) {
            // ?session
            Enumeration<String> namesEnum = session.getAttributeNames();
            for (; namesEnum.hasMoreElements();) {
                session.removeAttribute(namesEnum.nextElement());
            }
            return;
        }
        // ???
        for (String name : names) {
            session.removeAttribute(name);
        }
    }

    /**
     * ?cookie
     * 
     * @param req
     *            
     * @param names
     *            cookie??
     */
    public static void purgeCookies(HttpServletRequest req, HttpServletResponse resp, String... names) {
        Set<String> nameSet = ValidationUtils.isEmpty(names) ? null : new HashSet<String>(Arrays.asList(names));
        boolean removeAll = ValidationUtils.isEmpty(nameSet);
        for (Cookie cookie : req.getCookies()) {
            if (removeAll || nameSet.contains(cookie.getName())) {
                cookie.setMaxAge(0);
                cookie.setValue(null);
                resp.addCookie(cookie);
                if (!removeAll)
                    nameSet.remove(cookie.getName());
                ;
            }
        }
    }

    /**
     * ??SessionCookie?
     * 
     * @param name
     *            ??
     * @param req
     * @return
     */
    public static Object getValue(String name, HttpServletRequest req) {
        return getValue(name, null, req);
    }

    /**
     * ??SessionCookie?
     * 
     * @param name
     *            ??
     * @param defaultVal
     *            
     * @param req
     * @return
     */
    public static Object getValue(String name, Object defaultVal, HttpServletRequest req) {
        // get from request first
        Object value = collapseValue(req.getParameterValues(name));
        if (value != null)
            return value;
        // get from session
        value = getSessionAttribute(name, null, req);
        if (value != null)
            return value;
        value = getCookieValue(name, null, req);
        if (value != null)
            return value;
        // nothing found
        return defaultVal;
    }

    /**
     * ?Session
     * 
     * @param name
     *            ??
     * @param req
     * @return
     */
    public static Object getSession(String name, HttpServletRequest req) {
        return getSessionAttribute(name, null, req);
    }

    /**
     * ?Session
     * 
     * @param name
     *            ??
     * @param defaultVal
     *            
     * @param req
     * @return
     */
    public static Object getSessionAttribute(String name, Object defaultVal, HttpServletRequest req) {
        HttpSession session = req.getSession();
        return session != null && session.getAttribute(name) != null ? session.getAttribute(name) : defaultVal;
    }

    /**
     * Cookie
     * 
     * @param name
     *            ??
     * @param req
     *            
     * @return
     */
    public static String getCookie(String name, HttpServletRequest req) {
        return getCookieValue(name, null, req);
    }

    /**
     * Cookie
     * 
     * @param name
     *            ??
     * @param defaultVal
     *            
     * @param req
     *            
     * @return
     */

    public static String getCookieValue(String name, String defaultVal, HttpServletRequest req) {
        if (req.getCookies() != null) {
            for (Cookie cookie : req.getCookies()) {
                if (cookie.getName().equals(name))
                    return cookie.getValue();
            }
        }
        // nothing found
        return defaultVal;
    }

    /**
     * cookie.
     * 
     * @param name
     *            Cookie??
     * @param val
     *            
     * @param expiry
     *            
     * @return
     */
    public static Cookie cookie(String name, Object val, Integer expiry) {
        return cookie(name, val, expiry, null, null, true, false);
    }

    /**
     * cookie
     * 
     * @param name
     *            Cookie??
     * @param val
     *            
     * @param expiry
     *            
     * @param domain
     *            
     * @param path
     *            
     * @param httpOnly
     *            ??HTTPcooie
     * @param secure
     *            ?SSL
     * @return
     */
    public static Cookie cookie(String name, Object val, Integer expiry, String domain, String path,
            boolean httpOnly, boolean secure) {
        Cookie cookie = new Cookie(name, val == null ? null : val.toString());
        if (expiry != null)
            cookie.setMaxAge(expiry);
        if (!StringUtils.isEmpty(domain))
            cookie.setDomain(domain);
        cookie.setSecure(secure);
        if (!StringUtils.isEmpty(path))
            cookie.setPath(path);
        cookie.setHttpOnly(httpOnly);
        return cookie;
    }

    /**
     * ?
     * 
     * @param vals
     *            
     * @return
     */
    public static String collapseValue(String[] vals) {
        if (ValidationUtils.isEmpty(vals))
            return null;
        return StringUtils.join(vals, ShenStrings.DELIMITER_COMMA);
    }

    /**
     * Write contents into response' writer.
     * 
     * @param resp
     *            Response
     * @param content
     */
    public static void writeTo(HttpServletResponse resp, String content) {
        try {
            resp.getWriter().append(content);
        } catch (IOException e) {
        }
    }

    /**
     * ?
     * 
     * @param req
     *            
     * @param param
     *            ???
     * @param defaultVal
     *            
     * @return
     */
    public static Integer getIntParam(HttpServletRequest req, String param, Integer defaultVal) {
        return DataUtils.toInt(req.getParameter(param), defaultVal);
    }

    /**
     * ?URL
     * 
     * @param response
     * @return
     */
    public static String decodeUrl(String response) {
        return decodeUrl(response, ENC_UTF8);
    }

    /**
     * ?URL
     * 
     * @param response
     * @return
     */
    public static String decodeUrl(String response, String enc) {
        try {
            return URLDecoder.decode(response, enc);
        } catch (UnsupportedEncodingException e) {
            LOG.warn("[decodeUrl(String,String)] encode not supported", e);
        }
        return null;
    }

    /**
     * ?
     * 
     * @param req
     * @return
     */
    public static String dumpRequest(HttpUriRequest req) {
        if (req == null)
            return null;
        char column = ':', rtn = '\n', space = ' ';
        StringBuilder builder = new StringBuilder(req.getMethod());
        builder.append(space).append(req.getURI()).append(space).append(req.getProtocolVersion()).append(rtn)
                .append(rtn);
        builder.append("HEADERS:\n");
        Header[] headers = req.getAllHeaders();
        int length = headers.length;
        for (int i = 0; i < length; i++) {
            builder.append(headers[i].getName()).append(column).append(headers[i].getValue()).append(rtn);
        }
        if (req instanceof HttpPost || req instanceof HttpPut) {
            builder.append(rtn);
            builder.append("BODY:\n");
            if (null != ((HttpPost) req).getEntity()) {
                BufferedReader reader = null;
                try {
                    InputStreamReader isReader = new InputStreamReader(((HttpPost) req).getEntity().getContent());
                    reader = new BufferedReader(isReader);
                    String line;
                    while ((line = reader.readLine()) != null) {
                        builder.append(line);
                    }
                } catch (IllegalStateException | IOException e) {
                    if (LOG.isWarnEnabled())
                        LOG.warn("[dumpRequest] Could not read request due to exception", e);
                } finally {
                    if (reader != null)
                        try {
                            reader.close();
                        } catch (IOException e) {
                            if (LOG.isWarnEnabled())
                                LOG.warn("[dumpRequest] could not close reader due to exception", e);
                        }
                }
            }
        }
        return builder.toString();
    }

    /**
     * Dump out things from HttpServletRequest object
     * 
     * @param req
     * @return
     */
    public static String dumpRequest(HttpServletRequest req) {
        if (req == null)
            return null;
        char column = ':', rtn = '\n', space = ' ';
        StringBuilder builder = new StringBuilder(req.getMethod());
        builder.append(space).append(req.getRequestURL().toString()).append(space).append(req.getProtocol())
                .append(rtn);
        Enumeration<String> headers = req.getHeaderNames();
        builder.append("HEADERS:\n");
        String header;
        for (; headers.hasMoreElements();) {
            header = headers.nextElement();
            builder.append(header).append(column).append(req.getHeader(header)).append(rtn);
        }
        builder.append("COOKIES:\n");
        Cookie cookie;
        Cookie[] cookies = req.getCookies();
        if (!ValidationUtils.isEmpty(cookies)) {
            for (int i = 0; i < cookies.length; i++) {
                cookie = cookies[i];
                builder.append(cookie.getName()).append(column).append(GsonUtils.format(cookie)).append(rtn);
            }
        }
        builder.append("BODY:\n");
        Map<String, String[]> params = req.getParameterMap();
        for (String name : params.keySet()) {
            builder.append(name).append(ShenStrings.DELIMITER_DOT);
            builder.append(name.matches(PASS_PATTERN) ? params.get(SECRET_STRING) : params.get(name));
        }
        return builder.toString();

    }

    /**
     * queryString
     * 
     * @author jiangnan
     * 
     */
    public static class QueryString {
        public Map<String, List<String>> params;
        public String query;

        public QueryString(String query) {
            this.query = query;
            params = parseQueryInternal(query);
        }

        public Integer getInteger(String param) {
            return getInteger(param, null);
        }

        public Integer getInteger(String param, Integer defaultVal) {
            return hasParam(param) ? DataUtils.toInt(params.get(param).get(0), defaultVal) : defaultVal;
        }

        public String get(String param) {
            return get(param, null);
        }

        public String get(String param, String defaultVal) {
            return hasParam(param) ? params.get(param).get(0) : defaultVal;
        }

        public Long getLong(String param) {
            return getLong(param, null);
        }

        public Long getLong(String param, Long defaultVal) {
            return hasParam(param) ? DataUtils.toLong(params.get(param).get(0), defaultVal) : defaultVal;
        }

        public String[] getArray(String param) {
            return hasParam(param) ? params.get(param).toArray(new String[0]) : null;
        }

        /**
         * @param param
         * @return
         */
        public boolean hasParam(String param) {
            List<String> vals = params.get(param);
            return !ValidationUtils.isEmpty(vals);
        }

        private Map<String, List<String>> parseQueryInternal(String query) {
            params = new HashMap<String, List<String>>();
            if (StringUtils.isEmpty(query))
                return params;
            String[] pairs = query.split(AMP);
            String[] pairValue;
            for (String pair : pairs) {
                pairValue = pair.split(EQ);
                if (!params.containsKey(pairValue[0])) {
                    params.put(pairValue[0], new ArrayList<String>());
                }
                params.get(pairValue[0]).add(pairValue[1]);
            }
            return params;
        }
    }

    /**
     * Create a basic login context.
     * 
     * @param username
     * @param pass
     * @return
     */
    public static HttpContext basicLoginContext(String username, String pass) {
        CredentialsProvider provider = new BasicCredentialsProvider();
        UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, pass);
        provider.setCredentials(AuthScope.ANY, credentials);
        // Add AuthCache to the execution context
        HttpClientContext context = HttpClientContext.create();
        context.setCredentialsProvider(provider);
        return context;
    }

    /**
     * Get value from paramMap
     * 
     * @param params
     * @param name
     * @return
     */
    public static String getParam(Map<String, String[]> params, String name) {
        String result = null;
        if (params != null && params.containsKey(name)) {
            String[] vals = params.get(name);
            result = ValidationUtils.isEmpty(vals) ? null : CollectionUtils.join(vals, ShenStrings.DELIMITER_COMMA);
        }
        return result;
    }

    /**
     * Get parameter as long
     * 
     * @param req
     * @param name
     * @return
     */
    public static Long getAsLong(HttpServletRequest req, String name) {
        String param = req.getParameter(name);
        Long val = null;
        try {
            val = Long.parseLong(param);
        } catch (Exception ex) {
        }
        return val;
    }

    /**
     * jsonp?format
     * 
     * @param json
     * @param req
     * @return
     */
    public static String jsonp(JsonSerializable json, HttpServletRequest req) {
        return jsonp(json == null ? null : json.toJson(), req);
    }

    /**
     * jsonp?format
     * 
     * @param json
     * @param req
     * @return
     */
    public static String jsonp(String json, HttpServletRequest req) {
        String callback = req.getParameter(JSONP_CALLBACK);
        if (StringUtils.isEmpty(callback))
            return json;
        return String.format(CALLBACK_PATTERN, callback, json);
    }

    /**
     * ??Hash. Map<String,String[]>
     * ?Map<String,String>??,?
     * 
     * @param req
     * @return
     */
    public static Map<String, String> getParamMap(HttpServletRequest req) {
        Map<String, String[]> params = req.getParameterMap();
        Map<String, String> result = new HashMap<String, String>();
        Set<String> keys = params.keySet();
        for (String key : keys)
            result.put(key, getParam(params, key));
        return result;
    }

    /**
     * ??URL: joinParams("http://www.google.com", "test", "test"); //
     * http://www.google.com?test=test joinParams("http://www.google.com",
     * "test"); // http://www.google.com?test= joinParams(null, "test"); //
     * ?test= joinParams("http://www.google.com",
     * "test","test","test2","test2"); //
     * http://www.google.com?test=test&test2=test2
     * 
     * @param url
     *            - ???query
     * @param params
     *            ??
     * @return
     */
    public static String joinParams(String url, String... params) {
        if (ValidationUtils.isEmpty(params))
            return url;
        StringBuilder builder = new StringBuilder();
        if (StringUtils.isNotEmpty(url))
            builder.append(url);
        boolean hasQuery = ShenStrings.has(url, QUERY_CHAR);
        for (int i = 0; i < params.length; i += 2) {
            builder.append(hasQuery ? AMP : QUERY_CHAR);
            if (!hasQuery)
                hasQuery = true;
            builder.append(params[i]).append(EQ).append(CollectionUtils.get(params, i + 1));
        }
        return builder.toString();
    }

    /**
     * ??URL.
     * 
     * @param url
     *            - ???query
     * @param params
     *            ??
     * @return
     */
    public static String joinParams(String url, Map<String, ?> params) {
        if (ValidationUtils.isEmpty(params))
            return url;
        StringBuilder builder = new StringBuilder();
        if (StringUtils.isNotEmpty(url))
            builder.append(url);
        boolean hasQuery = ShenStrings.has(url, QUERY_CHAR);
        Object val;
        for (Entry<String, ?> kv : params.entrySet()) {
            builder.append(hasQuery ? AMP : QUERY_CHAR);
            if (!hasQuery)
                hasQuery = true;
            val = kv.getValue();
            builder.append(kv.getKey()).append(EQ)
                    .append(DataUtils.isPrimative(val) ? val.toString() : GsonUtils.format(val));
        }
        return builder.toString();
    }

    /**
     * ??
     * 
     * @param uri
     * @param req
     * @return
     */
    public static String toUrl(String uri, HttpServletRequest req) {
        StringBuffer buffer = req.getRequestURL();
        boolean isSsl = buffer.indexOf(URL_SSL_PREFIX) > -1;
        String base = buffer.substring(0, buffer.indexOf(SLASH, URL_SSL_PREFIX.length() - (isSsl ? 0 : 1)));
        return StringUtils.isEmpty(uri) ? base : base + uri;
    }

    /**
     * ?
     * 
     * @param name
     * @param resp
     */
    public static void downloadFile(String name, InputStream is, int bufferSize, HttpServletResponse resp) {
        if (is == null)
            return;
        try {
            byte[] buffer = new byte[bufferSize];
            resp.setContentType(STR_CONTENT_TYPE_FILE);
            if (StringUtils.isNotBlank(name))
                resp.setHeader(HEADER_CONTENT_DISPOSITION,
                        "attachment; filename*=UTF-8''" + HttpUtils.encodeUrl(name));
            int read = 0, total = 0;
            OutputStream os = resp.getOutputStream();
            while ((read = is.read(buffer)) > 0) {
                os.write(buffer, 0, read);
                total += read;
                os.flush();
            }
            if (LOG.isDebugEnabled())
                LOG.debug("[downloadFile] total read -> {}", total);
        } catch (IOException e) {
            LOG.error("[downloadFile] could not open url due to exception.", e);
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    /**
     * Proxy???
     * 
     * @param proxy
     *            Proxy
     * @param url
     */
    public static long testProxy(Proxy proxy, URL url, int ctimeout, int rtimeout, int testTimes) {
        if (proxy == null)
            return -1;
        if (url == null) {
            try {
                url = new URL(DEFAULT_TEST_PROXY_URL);
            } catch (MalformedURLException e1) {
                LOG.warn("[testProxy] illegal url -> {}", url);
                return -1;
            }
        }
        HttpURLConnection conn = null;
        try {
            StopWatch watch = new StopWatch();
            watch.start();
            conn = (HttpURLConnection) url.openConnection(proxy);
            if (ctimeout > 0)
                conn.setConnectTimeout(ctimeout);
            if (rtimeout > 0)
                conn.setReadTimeout(rtimeout);
            long available = conn.getInputStream().available(); // try to get input
            int code = conn.getResponseCode();
            if (available < 1 || code != 200)
                return -1; //no content get
            watch.stop();
            return watch.getTime();
        } catch (Exception e) {
            LOG.warn("[testProxy] Could not connect to proxy -> {}", proxy.address());
            if (testTimes > 0)
                return testProxy(proxy, url, ctimeout, rtimeout, testTimes - 1);
        } finally {
            if (conn != null)
                IOUtils.close(conn);
        }
        return -1;
    }
}