/** * Copyright (c) 2012-2013, Michael Yang 杨福海 (www.yangfuhai.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 net.tsz.afinal.http; import android.content.Intent; import android.os.SystemClock; import com.mfh.comna.bizz.BizApplication; import com.mfh.comna.api.Constants; import com.mfh.comna.api.utils.MLog; import com.mfh.comna.bizz.login.logic.MfhLoginService; import com.mfh.comna.network.NetFactory; import net.tsz.afinal.core.AsyncTask; import net.tsz.afinal.http.entityhandler.EntityCallBack; import net.tsz.afinal.http.entityhandler.FileEntityHandler; import net.tsz.afinal.http.entityhandler.StringEntityHandler; import org.apache.commons.lang3.StringUtils; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.HttpResponseException; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.AbstractHttpClient; import org.apache.http.protocol.HttpContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URI; import java.net.UnknownHostException; public class HttpHandler <T> extends AsyncTask<Object, Object, Object> implements EntityCallBack { private final AbstractHttpClient client; private final HttpContext context; private final StringEntityHandler mStrEntityHandler = new StringEntityHandler(); private final FileEntityHandler mFileEntityHandler = new FileEntityHandler(); private final AjaxCallBack<T> callback; private int executionCount = 0; private String targetUrl = null; //下载的路径 private boolean isResume = false; //是否断点续传 private String charset; protected Logger logger = LoggerFactory.getLogger(this.getClass());//add by zhangyz public HttpHandler(AbstractHttpClient client, HttpContext context, AjaxCallBack<T> callback,String charset) { this.client = client; this.context = context; this.callback = callback; this.charset = charset; } private void makeRequestWithRetries(HttpUriRequest request) throws IOException { if(isResume && targetUrl!= null){ File downloadFile = new File(targetUrl); long fileLen = 0; if(downloadFile.isFile() && downloadFile.exists()){ fileLen = downloadFile.length(); } if(fileLen > 0) request.setHeader("RANGE", "bytes="+fileLen+"-"); } boolean retry = true; IOException cause = null; HttpRequestRetryHandler retryHandler = client.getHttpRequestRetryHandler();//自动重试机制 while (retry) { try { if(isCancelled()){ return; } //POST:无参数;GET:有参数 String requestUrl = request.getURI().toString(); String requestMethod = request.getMethod(); MLog.d(String.format("makeRequestWithRetries.request <%d><%s> %s", executionCount, requestMethod, requestUrl)); for(Header header : request.getAllHeaders()){ MLog.d(String.format("makeRequestWithRetries.request.header <%s><%s>", header.getName(), header.getValue())); } //重新组合url StringBuilder requestParams = new StringBuilder(); if (requestMethod.equals("POST")){ HttpEntityEnclosingRequestBase requestBase = (HttpEntityEnclosingRequestBase)request; if(requestBase != null){ HttpEntity entity = requestBase.getEntity(); if(entity != null){ InputStream is = entity.getContent(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String line = null; try { while ((line = reader.readLine()) != null) { // sb.append(line + "/n"); // Log.d("Nat: makeRequestWithRetries.request.Params.line", line); requestParams.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } // byte[] paramsArray = EntityUtils.toByteArray(entity); // String paramsStr = EntityUtils.toString(entity, "UTF-8"); // Log.d("Nat: makeRequestWithRetries.request.Params(2)", paramsStr); // Log.d("Nat: makeRequestWithRetries.request.Params(3)", EntityUtils.toString(entity)); } // Log.d("Nat: makeRequestWithRetries.request.Params(RAW)", requestParams.toString()); } //执行网络请求 HttpResponse response = client.execute(request, context); // for(Header header : response.getAllHeaders()){ // Log.d("Nat", String.format(" makeRequestWithRetries.respoonse.header <%s><%s>", header.getName(), header.getValue())); // } if (isCancelled()){ // Log.d("Nat: makeRequestWithRetries", "request caneled."); return; } //POST:无参数;GET:有参数 request.getRequestLine();//GET MLog.d("makeRequestWithRetries.requestLine:" +request.getRequestLine().toString()); /*if (request.getMethod().equals("post")) { HttpParams params = request.getParams(); params.setParameter(NetFactory.CLIENTSESSION, ""); request.snetParams(params); }*/ //会话超时后重新自动登录并重新执行原来请求 //executionCount < 1,重试过一次就不再需要重试,测试。 if (response.containsHeader("needLogin")/* && executionCount < 1*/) { //不论url中是否有JSSIONID字段都需要先执行重登录去刷新cookie. //自动重新登录(同步执行),sessionId String newSid = MfhLoginService.get().doLogin(); if(newSid == null){ //登录失败,登录页面 Intent intent = new Intent(Constants.ACTION_REDIRECT_TO_LOGIN_H5); BizApplication.getAppContext().sendBroadcast(intent); break; } if (requestMethod.equals("POST")){ // //修改Entity中的JSSIONID字段 String newParams = replaceParam(requestParams.toString(), NetFactory.CLIENTSESSION, newSid); // HttpEntity entity = new StringEntity(newParams); HttpEntity entity = convertToAjaxParams(newParams).getEntity(); ((HttpEntityEnclosingRequestBase) request).setEntity(entity); } else if(requestMethod.equals("GET")){ //修改URL中的JSSIONID字段 String newRequestUrl = replaceParam(requestUrl, NetFactory.CLIENTSESSION, newSid); // newRequestUrl = replaceParam(newRequestUrl, "lastupdate", "0"); URI uri = new URI(newRequestUrl); // Log.d("Nat: makeRequestWithRetries.autoLogin.URI", uri.toString()); // HttpEntityEnclosingRequestBase requestFact = (HttpEntityEnclosingRequestBase)request; // requestFact.setURI(uri); ((HttpEntityEnclosingRequestBase) request).setURI(uri); } //TODO,重新执行请求 retry = (++executionCount <= 1) || retryHandler.retryRequest(new IOException("Exception"), executionCount, context); //这样后面重新执行... continue; } //执行正常流程 handleResponse(response); return; } catch (UnknownHostException e) { MLog.e("UnknownHostException:" + e.toString()); publishProgress(UPDATE_FAILURE, e, "unknownHostException:can't resolve host"); return; } catch (IOException e) { MLog.e("IOException: " + e.toString()); cause = e; retry = retryHandler.retryRequest(cause, ++executionCount, context); } catch (NullPointerException e) { if(e != null){ MLog.e("NullPointerException: " + e.toString()); // here's a bug in HttpClient 4.0.x that on some occasions causes // DefaultRequestExecutor to throw an NPE, see // http://code.google.com/p/android/issues/detail?id=5255 cause = new IOException("NPE in HttpClient: " + e.getMessage()); retry = retryHandler.retryRequest(cause, ++executionCount, context); }else{ MLog.e("NullPointerException: e is null"); } }catch (Exception e) { MLog.e("Exception: " + e.toString()); cause = new IOException("Unhandled Exception" + e.getMessage()); retry = retryHandler.retryRequest(cause, ++executionCount, context); } } // cleaned up to throw IOException if(cause != null){ throw cause; } // else{ // //TODO // throw new IOException("未知网络错误"); // } } /** * 替换字符串指定字符串对应的值 * */ private String replaceParam(String rawData, String paramName, String newParamValue){ if (rawData == null){ return ""; } int index = StringUtils.indexOfIgnoreCase(rawData, paramName); if (index > 0) { int endIndex = rawData.indexOf("&", index + 1); String newData; if (endIndex > 0) newData = rawData.substring(0, index) + paramName + "=" + newParamValue + rawData.substring(endIndex); else newData = rawData.substring(0, index) + paramName + "=" + newParamValue; return newData; } return rawData; } /** * 将URL参数转换为AjaxParams * @param rawData name1:value1&name2:value2&... * */ private AjaxParams convertToAjaxParams(String rawData){ AjaxParams ajaxParams = new AjaxParams(); try{ if(rawData != null){ return ajaxParams; } String[] params = rawData.split("&"); if (params != null && params.length > 0){ String name, value; for (String param : params){ if (!param.contains("=")){ continue; } int index = StringUtils.indexOfIgnoreCase(param, "="); name = param.substring(0, index); value = param.substring(index + 1); ajaxParams.put(name, value); } } } catch(Exception e){ MLog.e("convertToAjaxParams:" + e.toString()); } return ajaxParams; } @Override protected Object doInBackground(Object... params) { if(params!=null && params.length == 3){ targetUrl = String.valueOf(params[1]); isResume = (Boolean) params[2]; } try { publishProgress(UPDATE_START); // 开始 makeRequestWithRetries((HttpUriRequest)params[0]); } catch (IOException e) { publishProgress(UPDATE_FAILURE,e,e.getMessage()); // 结束 } return null; } private final static int UPDATE_START = 1; private final static int UPDATE_LOADING = 2; private final static int UPDATE_FAILURE = 3; private final static int UPDATE_SUCCESS = 4; @SuppressWarnings("unchecked") @Override protected void onProgressUpdate(Object... values) { int update = Integer.valueOf(String.valueOf(values[0])); if (values.length > 1){ // Log.d("Nat: makeRequestWithRetries.onProgressUpdate", String.format("%s/%s", String.valueOf(values[0]), String.valueOf(values[1]))); }else if (values.length > 0){ // Log.d("Nat: makeRequestWithRetries.onProgressUpdate",String.valueOf(values[0])); } switch (update) { case UPDATE_START: if(callback!=null) callback.onStart(); break; case UPDATE_LOADING: if(callback!=null) callback.onLoading(Long.valueOf(String.valueOf(values[1])),Long.valueOf(String.valueOf(values[2]))); break; case UPDATE_FAILURE: if(callback!=null) callback.onFailure((Throwable)values[1],(String)values[2]); break; case UPDATE_SUCCESS: if(callback!=null) callback.onSuccess((T)values[1]); break; default: break; } super.onProgressUpdate(values); } public boolean isStop() { return mFileEntityHandler.isStop(); } /** * 停止下载任务 */ public void stop() { mFileEntityHandler.setStop(true); } private void handleResponse(HttpResponse response) { StatusLine status = response.getStatusLine(); MLog.d(String.format("handleResponse:%d/%s", status.getStatusCode(), status.getReasonPhrase())); if (status.getStatusCode() >= 300) { String errorMsg = "response status error code: "+status.getStatusCode(); if(status.getStatusCode() == 416 && isResume){ errorMsg += " \n maybe you have download complete."; } publishProgress(UPDATE_FAILURE, new HttpResponseException(status.getStatusCode(), status.getReasonPhrase()),errorMsg); } else { try { HttpEntity entity = response.getEntity(); Object responseBody = null; if (entity != null) { time = SystemClock.uptimeMillis(); if(targetUrl!=null){ responseBody = mFileEntityHandler.handleEntity(entity,this,targetUrl,isResume); } else{ responseBody = mStrEntityHandler.handleEntity(entity,this,charset); } } publishProgress(UPDATE_SUCCESS,responseBody); } catch (IOException e) { publishProgress(UPDATE_FAILURE,e,e.getMessage()); } } } private long time; @Override public void callBack(long count, long current,boolean mustNoticeUI) { if(callback!=null && callback.isProgress()){ if(mustNoticeUI){ publishProgress(UPDATE_LOADING,count,current); }else{ long thisTime = SystemClock.uptimeMillis(); if(thisTime - time >= callback.getRate()){ time = thisTime ; publishProgress(UPDATE_LOADING,count,current); } } } } }