/* Copyright 2012-2013, Polyvi Inc. (http://polyvi.github.io/openxface) This program is distributed under the terms of the GNU General Public License. This file is part of xFace. xFace is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. xFace is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with xFace. If not, see <http://www.gnu.org/licenses/>. */ package com.polyvi.xface.extension.xmlhttprequest; import java.io.IOException; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import org.apache.http.Header; import org.apache.http.client.HttpResponseException; import org.apache.http.message.BasicHeader; import android.content.Context; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import com.polyvi.xface.http.XAsyncHttpResponseHandler; import com.polyvi.xface.http.XHttpWorker; /** * 参照W3C规范 实现的ajax请求对象 * */ public class XXMLHttpRequest extends XAsyncHttpResponseHandler implements XIAjaxDataInterface { //FIXME:后续可以添加其他方法的支持 private static final String GET = "GET"; private static final String POST = "POST"; private static String[] SupportMethods = { GET, POST }; /* 状态 名称 描述 0 UNSEND 初始化状态, XMLHttpRequest 对象已创建或已被 abort() 方法重置; 1 OPENED open()方法已调用; 2 HEADERS_RECEIVED 所有响应头部都已经接收到 3 LOADING 正在接收服务器响应体数据 4 DONE 数据接收完毕或者请求被中断 */ static enum State { UNSEND, OPENED, HEADERS_RECEIVED, LOADING, DONE } private State mState; private Map<String, String> mRequestHeaders; // 请求头部 private XHttpWorker mNetWork; //网络请求服务对象 private String mUrl; private String mMethod; private String mPostData; private XAjaxRequestListener mListener; private boolean mError; private int mHttpStatusCode;// http状态码 private String mResponseText; // 响应文本 private Header[] mResponseHeaders; // 响应头部 private Context mContext; public XXMLHttpRequest(Context context) { super(); mRequestHeaders = new ConcurrentHashMap<String, String>(); mContext = context; mNetWork = new XHttpWorker(); mResponseText = ""; mState = State.UNSEND; } public void open(String method, String url) throws XAjaxException { // FIXME: 暂不支持用户名和密码 if (!isMethodSupported(method)) { throw new XAjaxException( XAjaxException.ErrorCode.METHOD_NOT_SUPPORT); } internalAbort(); State previouseState = mState; mState = State.UNSEND; mError = false; clearRequest(); clearResponse(); if (mState.ordinal() != State.UNSEND.ordinal()) { throw new XAjaxException(XAjaxException.ErrorCode.INVALID_STATE_ERR); } mUrl = url; mMethod = method; // 检查前一个状态 防止应用多次调用open派发多次stateReadyChange事件 if (previouseState.ordinal() != State.OPENED.ordinal()) { this.changeState(State.OPENED); } else { mState = State.OPENED; } } public void send(String data) throws XAjaxException { // FIXME:暂时不支持二进制的发送 if (mState != State.OPENED) { throw new XAjaxException(XAjaxException.ErrorCode.INVALID_STATE_ERR); } mPostData = data; mError = false; doRequest(); } public void setRequestHeader(String name, String value) throws XAjaxException { if (mState != State.OPENED) { throw new XAjaxException(XAjaxException.ErrorCode.INVALID_STATE_ERR); } if (!isHeaderValueValid(value)) { throw new XAjaxException( XAjaxException.ErrorCode.INVALID_HEADER_VALUE); } mRequestHeaders.put(name, value); } /** * 中断请求 */ public void abort() { internalAbort(); clearResponse(); clearRequest(); if (mListener != null) mListener.onAbort(); if (mState.ordinal() > State.OPENED.ordinal() && mState.ordinal() != State.DONE.ordinal()) { changeState(State.DONE); } mState = State.UNSEND; } /** * 设置ajax请求监听器 * * @param listener */ public void setRequestListener(XAjaxRequestListener listener) { mListener = listener; if( null != mListener ) mListener.setAjaxDataInterface(this); } public XAjaxRequestListener getRequestListener() { return mListener; } /** * 是否是有效的值 * * @param value */ private boolean isHeaderValueValid(String value) { return !value.contains("\r") && !value.contains("\n"); } /** * ajax状态改变 * * @param newState */ private void changeState(State newState) { if (mState.ordinal() != newState.ordinal()) { mState = newState; if (null != mListener) mListener.onReadyStateChanged(); } } /** * 发生网络错误 */ private void networkError() { clearRequest(); clearResponse(); mError = true; if (null != mListener) mListener.onNetworkError(); changeState(State.DONE); internalAbort(); } /** * 发送ajax请求 * * @return * @throws XAjaxException */ private void doRequest() throws XAjaxException { addCookie(mUrl); if (mMethod.equalsIgnoreCase(GET)) { mNetWork.get(mUrl, this.convertHeaders(mRequestHeaders), this); } else if (mMethod.equalsIgnoreCase(POST)) { try { mNetWork.post(mUrl, this.convertHeaders(mRequestHeaders), mPostData, null, this); } catch (IOException e) { throw new XAjaxException( XAjaxException.ErrorCode.HTTP_REQUEST_ERROR); } } else { throw new XAjaxException( XAjaxException.ErrorCode.METHOD_NOT_SUPPORT); } } /** * 增加cookie属性 * * @param url * 请求的url地址 */ private void addCookie(String url) { // 默认增加cookie的支持 CookieSyncManager cookieSyncManager = CookieSyncManager .createInstance(mContext); cookieSyncManager.startSync(); String cookie = CookieManager.getInstance().getCookie(url); if (cookie != null) { mRequestHeaders.put("Cookie", cookie); } } /** * 将hash表的头部转换为header[] * * @return */ private Header[] convertHeaders(Map<String, String> mapHeaders) { Header[] headers = new Header[mapHeaders.size()]; Iterator<Entry<String, String>> iter = mapHeaders.entrySet().iterator(); int i = 0; while (iter.hasNext()) { Entry<String, String> entry = iter.next(); Header header = new BasicHeader(entry.getKey(), entry.getValue()); headers[i++] = header; } return headers; } /** * 清空对象状态 */ private void internalAbort() { // 取消请求 mNetWork.cancelRequest(true); mError = true; } /** * 清空请求头部 */ private void clearRequest() { mRequestHeaders.clear(); mPostData = null; mMethod = null; mUrl = null; } /** * 清空响应 */ private void clearResponse() { mResponseHeaders = null; mResponseText = ""; mHttpStatusCode = 0; } /** * 检查方法名的有效性 * * @param name * @return */ private boolean isMethodSupported(String name) { for (String method : SupportMethods) { if (method.equalsIgnoreCase(name)) { return true; } } return false; } @Override public int getReadyState() { return mState.ordinal(); } @Override public int getStatus() { return mHttpStatusCode; } @Override public String getResponseText() { return mResponseText; } public String getResponseHeader(String name) { if (null == mResponseHeaders) return null; for (Header header : mResponseHeaders) { if (header.getName().equals(name)) { return header.getValue(); } } return null; } @Override public Header[] getAllResponseHeader() { return mResponseHeaders; } @Override public void onSuccess(int statusCode, Header[] headers, String content) { // 如果已经出错 则不再处理 if (mError) { return; } // FIXME:底层的数据是一次性返回的 无需考虑多次接收数据 this.changeState(State.HEADERS_RECEIVED); mHttpStatusCode = statusCode; mResponseText = content; mResponseHeaders = headers; this.changeState(State.LOADING); this.changeState(State.DONE); } @Override public void onFailure(Throwable error, String content) { // 如果已经出错 则不再处理 if (mError) { return; } error.printStackTrace(); if (error instanceof HttpResponseException) { mHttpStatusCode = ((HttpResponseException) error).getStatusCode(); } mResponseText = content; this.networkError(); } }