package com.keju.maomao.internet; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Array; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.security.AccessControlException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.multipart.FilePart; import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity; import org.apache.commons.httpclient.methods.multipart.Part; import org.apache.commons.httpclient.methods.multipart.PartBase; import org.apache.commons.httpclient.methods.multipart.StringPart; import org.apache.http.Header; import com.keju.maomao.Configuration; import com.keju.maomao.SystemException; /** * 管理 HTTP request/response. * * @author Zhoujun */ public class HttpClient implements java.io.Serializable { private static final int OK = 200;// OK: 服务器成功返回 // private static final int NOT_MODIFIED = 304;// Not Modified(未修改): // 没有返回响应时无新数据返回 // private static final int BAD_REQUEST = 400;// Bad Request(错误请求): // 请求不合法,服务器不理解请求的语法。 // private static final int NOT_AUTHORIZED = 401;// Not Authorized(未授权): // 请求要求身份验证。 // private static final int FORBIDDEN = 403;// Forbidden(禁止): 服务器拒绝请求。 // private static final int NOT_FOUND = 404;// Not Found(未找到): 服务器找不到请求的资源。 // private static final int NOT_ACCEPTABLE = 406;// Not Acceptable(不接受): // 无法使用请求的内容特性响应请求的资源 private static final int INTERNAL_SERVER_ERROR = 500;// Internal Server // Error(服务器内部错误): // 服务器遇到错误,无法完成请求。 // private static final int BAD_GATEWAY = 502;// Bad Gateway(错误网关): // 服务器作为网关或代理,从上游服务器收到无效响应。 // private static final int SERVICE_UNAVAILABLE = 503;// Service // Unavailable(服务不可用): 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。请重试。 private final static boolean DEBUG = Configuration.getDebug(); private String basic; private int retryCount = Configuration.getRetryCount();// 重试次数 private int retryIntervalMillis = Configuration.getRetryIntervalSecs() * 1000;// 重试间隔时间 private int connectionTimeout = Configuration.getConnectionTimeout();// 网络连接超时时间 private int readTimeout = Configuration.getReadTimeout(); private static final long serialVersionUID = 808018030183407996L; private static boolean isJDK14orEarlier = false; private Map<String, String> requestHeaders = new HashMap<String, String>(); static { try { String versionStr = System.getProperty("java.specification.version"); if (null != versionStr) { isJDK14orEarlier = 1.5d > Double.parseDouble(versionStr); } } catch (AccessControlException ace) { isJDK14orEarlier = true; } } public HttpClient() { this.basic = null; setRequestHeader("Accept-Encoding", "gzip"); } public int getConnectionTimeout() { return connectionTimeout; } /** * 当发送一个网络请求连接时使用 * * @param connectionTimeout * - 连接超时毫秒数 */ public void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = Configuration.getConnectionTimeout(connectionTimeout); } public int getReadTimeout() { return readTimeout; } /** * Sets the read timeout to a specified timeout, in milliseconds. System * property -Dsinat4j.http.readTimeout overrides this attribute. * * @param readTimeout * - an int that specifies the timeout value to be used in * milliseconds */ public void setReadTimeout(int readTimeout) { this.readTimeout = Configuration.getReadTimeout(readTimeout); } public void setRetryCount(int retryCount) { if (retryCount >= 0) { this.retryCount = Configuration.getRetryCount(retryCount); } else { throw new IllegalArgumentException("RetryCount cannot be negative."); } } public void setRetryIntervalSecs(int retryIntervalSecs) { if (retryIntervalSecs >= 0) { this.retryIntervalMillis = Configuration.getRetryIntervalSecs(retryIntervalSecs) * 1000; } else { throw new IllegalArgumentException("RetryInterval cannot be negative."); } } public Response multPartURL(String url, PostParameter[] params, ImageItem item) throws SystemException { PostMethod post = new PostMethod(url); try { org.apache.commons.httpclient.HttpClient client = new org.apache.commons.httpclient.HttpClient(); long t = System.currentTimeMillis(); Part[] parts = null; if (params == null) { parts = new Part[1]; } else { parts = new Part[params.length + 1]; } if (params != null) { int i = 0; for (PostParameter entry : params) { parts[i++] = new StringPart(entry.getName(), (String) entry.getValue(), "UTF-8"); } parts[parts.length - 1] = new ByteArrayPart(item.getContent(), item.getName(), item.getImageType()); } post.setRequestEntity(new MultipartRequestEntity(parts, post.getParams())); List<Header> headers = new ArrayList<Header>(); client.getHostConfiguration().getParams().setParameter("http.default-headers", headers); client.executeMethod(post); Response response = new Response(); response.setResponseAsString(post.getResponseBodyAsString()); response.setStatusCode(post.getStatusCode()); log("multPartURL URL:" + url + ", result:" + response + ", time:" + (System.currentTimeMillis() - t)); return response; } catch (Exception ex) { throw new SystemException(ex.getMessage(), ex, -1); } finally { post.releaseConnection(); } } // 这个方案是上传文件的 就是文件必须要上传的 如果即可穿文件 也可以传别的内容 不可用此方案 public Response multPartURL(String fileParamName, String url, PostParameter[] params, File file) throws SystemException { PostMethod post = new PostMethod(url); org.apache.commons.httpclient.HttpClient client = new org.apache.commons.httpclient.HttpClient(); try { long t = System.currentTimeMillis(); Part[] parts = null; if (params == null) { parts = new Part[1]; } else { parts = new Part[params.length + 1]; } if (params != null) { int i = 0; for (PostParameter entry : params) { parts[i++] = new StringPart(entry.getName(), (String) entry.getValue(), "UTF-8"); } } FilePart filePart = new FilePart(fileParamName, file.getName(), file, "multipart/form-data", "UTF-8"); filePart.setTransferEncoding("binary"); parts[parts.length - 1] = filePart; post.setRequestEntity(new MultipartRequestEntity(parts, post.getParams())); List<Header> headers = new ArrayList<Header>(); client.getHostConfiguration().getParams().setParameter("http.default-headers", headers); client.executeMethod(post); Response response = new Response(); response.setResponseAsString(post.getResponseBodyAsString()); response.setStatusCode(post.getStatusCode()); log("multPartURL URL:" + url + ", result:" + response + ", time:" + (System.currentTimeMillis() - t)); return response; } catch (Exception ex) { throw new SystemException(ex.getMessage(), ex, -1); } finally { post.releaseConnection(); client = null; } } private static class ByteArrayPart extends PartBase { private byte[] mData; private String mName; public ByteArrayPart(byte[] data, String name, String type) throws IOException { super(name, type, "UTF-8", "binary"); mName = name; mData = data; } protected void sendData(OutputStream out) throws IOException { out.write(mData); } protected long lengthOfData() throws IOException { return mData.length; } protected void sendDispositionHeader(OutputStream out) throws IOException { super.sendDispositionHeader(out); StringBuilder buf = new StringBuilder(); buf.append("; filename=\"").append(mName).append("\""); out.write(buf.toString().getBytes()); } } /** * 将一个数组值拷贝到另一个数组 * * @param <T> * @param source * @return */ @SuppressWarnings("unchecked") private final <T> T[] copy(T[] source) { Class type = source.getClass().getComponentType(); T[] target = (T[]) Array.newInstance(type, source.length + 1); System.arraycopy(source, 0, target, 0, source.length); return target; } /** * 删除请求 * * @param url * @return * @throws SystemException */ public Response delete(String url) throws SystemException { return httpRequest(url, null, "DELETE"); } /** * 带额外参数的post请求 * * @param url * @param PostParameters * @return * @throws SystemException */ public Response post(String url, PostParameter[] PostParameters) throws SystemException { return httpRequest(url, PostParameters); } /** * post 带默认参数请求 默认参数是版本号 * * @param url * @return * @throws SystemException */ public Response post(String url) throws SystemException { return httpRequest(url, new PostParameter[0]); } /** * get 请求,不带参数 * * @param url * @return * @throws SystemException */ public Response get(String url) throws SystemException { return get(url, null); } /** * get 请求,带参数 * * @param url * @return * @throws SystemException */ public Response get(String url, PostParameter[] params) throws SystemException { // if (url.indexOf("?") == -1) { // url += "?PCode=" + Constants.PRODCODE; // } else if (url.indexOf("version") == -1) { // url += "&PCode=" + Constants.PRODCODE; // } if (null != params && params.length > 0) { if (url.indexOf("?") == -1) { url += "?" + HttpClient.encodeParameters(params); } else { url += "&" + HttpClient.encodeParameters(params); } } return httpRequest(url, null); } /** * 设置统一参数 * * @param url * @param postParams * @return * @throws SystemException */ protected Response httpRequest(String url, PostParameter[] postParams) throws SystemException { // 可以添加统一参数version PostParameter[] newPostParameters = postParams; String method = "GET"; if (postParams != null) { method = "POST"; // newPostParameters = copy(postParams); // newPostParameters[postParams.length] = new // PostParameter("PCode",Constants.PRODUCT_ID); } return httpRequest(url, newPostParameters, method); } /** * 发送 http 请求 * * @param url * @param postParams * @param httpMethod * @return * @throws SystemException */ public Response httpRequest(String url, PostParameter[] postParams, String httpMethod) throws SystemException { int retriedCount; int retry = retryCount + 1; Response res = null; for (retriedCount = 0; retriedCount < retry; retriedCount++) { int responseCode = -1; try { HttpURLConnection con = null; OutputStream osw = null; try { con = getConnection(url); con.setDoInput(true); setHeaders(url, postParams, con, httpMethod); if (null != postParams || "POST".equals(httpMethod)) { con.setRequestMethod("POST"); con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); con.setDoOutput(true); String postParam = ""; if (postParams != null) { postParam = encodeParameters(postParams); } log("Post Params: ", postParam); byte[] bytes = postParam.getBytes("UTF-8"); con.setRequestProperty("Content-Length", Integer.toString(bytes.length)); osw = con.getOutputStream(); osw.write(bytes); osw.flush(); osw.close(); } else { con.setRequestMethod("GET"); } res = new Response(con); responseCode = con.getResponseCode(); if (DEBUG) { log("Response: "); Map<String, List<String>> responseHeaders = con.getHeaderFields(); for (String key : responseHeaders.keySet()) { List<String> values = responseHeaders.get(key); for (String value : values) { if (null != key) { log(key + ": " + value); } else { log(value); } } } } if (responseCode != OK) { if (responseCode < INTERNAL_SERVER_ERROR || retriedCount == retryCount) { // throw new SystemException(getCause(responseCode) // + "\n" + res.asString(), responseCode); throw new SystemException("Http connection failed!" + "\n" + res.asString(), responseCode); } // will retry if the status code is // INTERNAL_SERVER_ERROR } else { break; } } finally { try { osw.close(); } catch (Exception ignore) { } } } catch (IOException ioe) { // connection timeout or read timeout if (retriedCount == retryCount) { throw new SystemException(ioe.getMessage(), ioe, responseCode); } } try { if (DEBUG && null != res) { res.asString(); } log("Sleeping " + retryIntervalMillis + " millisecs for next retry."); Thread.sleep(retryIntervalMillis); } catch (InterruptedException ignore) { // nothing to do } } return res; } /** * 格式化请求参数 * * @param postParams * @return */ public static String encodeParameters(PostParameter[] postParams) { StringBuffer buf = new StringBuffer(); for (int j = 0; j < postParams.length; j++) { if (j != 0) { buf.append("&"); } // buf.append(postParams[j].name) // .append("=").append(postParams[j].value); try { buf.append(URLEncoder.encode(postParams[j].name, "UTF-8")).append("=") .append(URLEncoder.encode(postParams[j].value, "UTF-8")); } catch (java.io.UnsupportedEncodingException neverHappen) { } } return buf.toString(); } /** * 设置 HTTP headers 请求头信息 * * @param connection * HttpURLConnection * @param authenticated * boolean */ private void setHeaders(String url, PostParameter[] params, HttpURLConnection connection, String httpMethod) { log("Request: "); log(httpMethod + " ", url); for (String key : requestHeaders.keySet()) { connection.addRequestProperty(key, requestHeaders.get(key)); log(key + ": " + requestHeaders.get(key)); } } public void setRequestHeader(String name, String value) { requestHeaders.put(name, value); } public String getRequestHeader(String name) { return requestHeaders.get(name); } private HttpURLConnection getConnection(String url) throws IOException { HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(); if (connectionTimeout > 0 && !isJDK14orEarlier) { con.setConnectTimeout(connectionTimeout); } if (readTimeout > 0 && !isJDK14orEarlier) { con.setReadTimeout(readTimeout); } return con; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof HttpClient)) return false; HttpClient that = (HttpClient) o; if (connectionTimeout != that.connectionTimeout) return false; if (readTimeout != that.readTimeout) return false; if (retryCount != that.retryCount) return false; if (retryIntervalMillis != that.retryIntervalMillis) return false; if (basic != null ? !basic.equals(that.basic) : that.basic != null) return false; if (!requestHeaders.equals(that.requestHeaders)) return false; return true; } @Override public int hashCode() { int result = basic != null ? basic.hashCode() : 0; result = 31 * result + retryCount; result = 31 * result + retryIntervalMillis; result = 31 * result + connectionTimeout; result = 31 * result + readTimeout; result = 31 * result + requestHeaders.hashCode(); return result; } private static void log(String message) { if (DEBUG) { System.out.println("[" + new java.util.Date() + "]" + message); } } private static void log(String message, String message2) { if (DEBUG) { log(message + message2); } } /** * 获得网络返回状态码对应的原因 * * @param statusCode * 网络返回状态码 * @return */ // private static String getCause(int statusCode){ // String cause = null; // switch(statusCode){ // case NOT_MODIFIED: // break; // case BAD_REQUEST: // cause = // "The request was invalid. An accompanying error message will explain why. This is the status code will be returned during rate limiting."; // break; // case NOT_AUTHORIZED: // cause = "Authentication credentials were missing or incorrect."; // break; // case FORBIDDEN: // cause = // "The request is understood, but it has been refused. An accompanying error message will explain why."; // break; // case NOT_FOUND: // cause = // "The URI requested is invalid or the resource requested, such as a user, does not exists."; // break; // case NOT_ACCEPTABLE: // cause = // "Returned by the Search API when an invalid format is specified in the request."; // break; // case INTERNAL_SERVER_ERROR: // cause = // "Something is broken. Please post to the group so the team can investigate."; // break; // case BAD_GATEWAY: // cause = "Server is down or being upgraded."; // break; // case SERVICE_UNAVAILABLE: // cause = // "Service Unavailable: The servers are up, but overloaded with requests. Try again later. The search and trend methods use this to indicate when you are being rate limited."; // break; // default: // cause = ""; // } // return statusCode + ":" + cause; // } }