/* * Created by LuaView. * Copyright (c) 2017, Alibaba Group. All rights reserved. * * This source code is licensed under the MIT. * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree. */ package com.taobao.luaview.userdata.net; import android.os.Build; import android.support.annotation.NonNull; import android.text.TextUtils; import com.taobao.luaview.userdata.base.BaseUserdata; import com.taobao.luaview.util.IOUtil; import com.taobao.luaview.util.LogUtil; import com.taobao.luaview.util.LuaUtil; import com.taobao.luaview.util.LuaViewUtil; import org.luaj.vm2.Globals; import org.luaj.vm2.LuaFunction; import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Varargs; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * Http 用户数据封装 * * @author song * @date 15/9/6 */ public class UDHttp extends BaseUserdata { private LuaFunction mCallback; public static final String METHOD_GET = "GET"; public static final String METHOD_POST = "POST"; public static final int DEFAULT_RETRY_TIMES = 3; public static final int DEFAULT_TIMEOUT = 30; private String mMethod = METHOD_POST; private String mUrl; private LuaTable mParams; private int mRetryTimes = DEFAULT_RETRY_TIMES; private int mTimeout = DEFAULT_TIMEOUT; //令牌 private Future<?> mFuture; private static ExecutorService mExecutor; private static synchronized ExecutorService getExecutor() { if (mExecutor == null) { mExecutor = Executors.newFixedThreadPool(5); } return mExecutor; } public UDHttp(Globals globals, LuaValue metatable, Varargs varargs) { super(globals, metatable, varargs); init(); } /** * 初始化mtop调用 */ private void init() { initVarargs(); } /** * 初始化数据 */ private void initVarargs() { final LuaValue param1 = getInitParam1(); final LuaFunction callback = LuaUtil.getFunction(initParams, 2); final String method = LuaUtil.getString(param1, "method"); final LuaTable params = LuaUtil.getTable(param1, "params"); setMethod(method); setParams(params); setCallback(callback); disableConnectionReuseIfNecessary(); } /** * 请求 url * * @param url * @return */ public UDHttp setUrl(String url) { this.mUrl = url; return this; } public String getUrl() { return this.mUrl; } /** * 请求方法 post/get/put/delete等 * * @param mMethod * @return */ public UDHttp setMethod(String mMethod) { this.mMethod = mMethod; return this; } public String getMethod() { return this.mMethod; } /** * 请求参数,使用table构建 * * @param mParams * @return */ public UDHttp setParams(LuaTable mParams) { this.mParams = mParams; return this; } public LuaValue getParams() { return this.mParams != null ? this.mParams : NIL; } /** * 重试次数 * * @param retryTimes * @return */ public UDHttp setRetryTimes(int retryTimes) { this.mRetryTimes = retryTimes; return this; } public int getRetryTimes() { return this.mRetryTimes; } /** * 请求超时时长 * * @param timeOut * @return */ public UDHttp setTimeout(int timeOut) { this.mTimeout = timeOut; return this; } public int getTimeout() { return this.mTimeout; } /** * 清空net cookies */ public UDHttp clearNetCookies() { CookieManager.clearNetCookies(); return this; } /** * 回调 * * @param luaFunction */ public UDHttp setCallback(final LuaFunction luaFunction) { this.mCallback = luaFunction; return this; } public LuaFunction getCallback() { return this.mCallback; } /** * 发送get请求 * * @return */ public UDHttp get(String url, LuaTable params, LuaFunction callback) { setMethod(METHOD_GET); if (url != null) { setUrl(url); } if (params != null) { setParams(params); } if (callback != null) { setCallback(callback); } return request(); } public UDHttp post(String url, LuaTable params, LuaFunction callback) { setMethod(METHOD_POST); if (url != null) { setUrl(url); } if (params != null) { setParams(params); } if (callback != null) { setCallback(callback); } return request(); } /** * 请求mtop */ public synchronized UDHttp request() { if (mFuture == null) { mFuture = getExecutor().submit(buildRunnable()); } return this; } public synchronized UDHttp cancel() { if (mFuture != null && !mFuture.isCancelled()) { mFuture.cancel(true); mFuture = null; } return this; } /** * 调用回调,注意:需要在主线程执行该操作 * * @param result */ private void callCallback(final LuaValue result) { LuaViewUtil.runOnUiThread(getContext(), new Runnable() { @Override public void run() { LuaUtil.callFunction(mCallback, result); } }); } /** * build a runnable to execute http request * TODO retry times重试次数设置。 * * @return */ private Runnable buildRunnable() { return new Runnable() { @Override public void run() { InputStream input = null; OutputStream output = null; HttpURLConnection connection = null; final UDHttpResponse udHttpResponse = new UDHttpResponse(getGlobals(), getmetatable(), null); try { final URL url = new URL(mUrl); connection = (HttpURLConnection) url.openConnection(); connection.setInstanceFollowRedirects(true);//支持301重定向 HttpURLConnection.setFollowRedirects(true);//支持重定向 //method connection.setRequestMethod(mMethod); //timeout connection.setConnectTimeout(mTimeout * 1000); //请求的cookie CookieManager.handleRequestCookies(connection, mUrl); if (mParams != null) { for (LuaValue key : mParams.keys()) { final LuaValue value = mParams.get(key); final String requestKey = key != null ? key.optjstring(null) : null; final String requestValue = value != null ? value.optjstring(null) : null; if (!TextUtils.isEmpty(requestKey) && !TextUtils.isEmpty(requestValue)) { connection.setRequestProperty(requestKey, requestValue); } } } //连接服务器 connection.connect(); //301重定向&重定向cookie connection = handle301Redirect(connection); //code udHttpResponse.setStatusCode(connection.getResponseCode()); if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { LogUtil.e("[Server Returned HTTP] ", connection.getResponseCode(), connection.getResponseMessage()); //msg udHttpResponse.setResponseMsg(connection.getResponseMessage()); return; } input = connection.getInputStream(); final byte[] fileData = IOUtil.toBytes(input, getContentCharset(connection)); //data udHttpResponse.setData(fileData); //header udHttpResponse.setHeaders(connection.getHeaderFields()); //response cookie // CookieManager.handleResponseCookies(connection, mUrl); } catch (Exception e) { LogUtil.e("[Http error] ", e); e.printStackTrace(); udHttpResponse.setResponseMsg(e.toString()); } finally { try { if (output != null) { output.flush(); output.close(); } if (input != null) { input.close(); } } catch (IOException ignored) { ignored.printStackTrace(); } if (connection != null) { connection.disconnect(); } //调用回调 callCallback(udHttpResponse); } } }; } @NonNull private HttpURLConnection handle301Redirect(HttpURLConnection connection) throws IOException { if (connection != null) { boolean redirect = false; // normally, 3xx is redirect int status = connection.getResponseCode(); if (status != HttpURLConnection.HTTP_OK) { if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM || status == HttpURLConnection.HTTP_SEE_OTHER) redirect = true; } if (redirect) { // get redirect url from "location" header field String newUrl = connection.getHeaderField("Location"); // get the cookie if need, for login String cookies = connection.getHeaderField(com.taobao.luaview.userdata.net.CookieManager.COOKIES_HEADER); // open the new connnection again connection = (HttpURLConnection) new URL(newUrl).openConnection(); connection.setRequestProperty(com.taobao.luaview.userdata.net.CookieManager.COOKIE, cookies); //连接服务器 connection.connect(); //重复301重定向 connection = handle301Redirect(connection); } } return connection; } private String getContentCharset(HttpURLConnection connection) { if (connection != null) { final String contentType = connection.getContentType(); if (contentType != null) { final String[] values = contentType.split(";"); // values.length should be 2 String charset = null; if (values != null) { for (String value : values) { value = value.trim(); if (value != null && value.toLowerCase().startsWith("charset=")) { charset = value.substring("charset=".length()); } } } return charset; } } return null; } private void disableConnectionReuseIfNecessary() { // Work around pre-Froyo bugs in HTTP connection reuse. if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) { System.setProperty("http.keepAlive", "false"); } } }