package com.topjohnwu.magisk.net; import android.os.AsyncTask; import com.topjohnwu.magisk.utils.APKInstall; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; import java.io.FileOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.util.Scanner; import java.util.concurrent.Executor; public class Request implements Closeable { private final HttpURLConnection conn; private Executor executor = null; private int code = -1; ErrorHandler err = null; private interface Requestor { T request() throws Exception; } public class Result { T result; public T getResult() { return result; } public int getCode() { return code; } public boolean isSuccess() { return code >= 200 && code <= 299; } public HttpURLConnection getConnection() { return conn; } } Request(HttpURLConnection c) { conn = c; } @Override public void close() { conn.disconnect(); } public Request addHeaders(String key, String value) { conn.setRequestProperty(key, value); return this; } public Request setErrorHandler(ErrorHandler handler) { err = handler; return this; } public Request setExecutor(Executor e) { executor = e; return this; } public Result connect() { try { connect0(); } catch (IOException e) { if (err != null) err.onError(conn, e); } return new Result<>(); } public Result execForInputStream() { return exec(this::getInputStream); } public void getAsInputStream(ResponseListener rs) { submit(this::getInputStream, rs); } public void getAsFile(File out, ResponseListener rs) { submit(() -> dlFile(out), rs); } public void execForFile(File out) { exec(() -> dlFile(out)); } public void getAsBytes(ResponseListener rs) { submit(this::dlBytes, rs); } public Result execForBytes() { return exec(this::dlBytes); } public void getAsString(ResponseListener rs) { submit(this::dlString, rs); } public Result execForString() { return exec(this::dlString); } public void getAsJSONObject(ResponseListener rs) { submit(this::dlJSONObject, rs); } public Result execForJSONObject() { return exec(this::dlJSONObject); } public void getAsJSONArray(ResponseListener rs) { submit(this::dlJSONArray, rs); } public Result execForJSONArray() { return exec(this::dlJSONArray); } private void connect0() throws IOException { conn.connect(); code = conn.getResponseCode(); } private Result exec(Requestor req) { Result res = new Result<>(); try { res.result = req.request(); } catch (Exception e) { if (err != null) err.onError(conn, e); } return res; } private void submit(Requestor req, ResponseListener rs) { AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { try { T t = req.request(); Runnable cb = () -> rs.onResponse(t); if (executor == null) Networking.mainHandler.post(cb); else executor.execute(cb); } catch (Exception e) { if (err != null) err.onError(conn, e); } }); } private BufferedInputStream getInputStream() throws IOException { connect0(); InputStream in = new FilterInputStream(conn.getInputStream()) { @Override public void close() throws IOException { super.close(); conn.disconnect(); } }; return new BufferedInputStream(in); } private String dlString() throws IOException { try (Scanner s = new Scanner(getInputStream(), "UTF-8")) { s.useDelimiter("\\A"); return s.next(); } } private JSONObject dlJSONObject() throws IOException, JSONException { return new JSONObject(dlString()); } private JSONArray dlJSONArray() throws IOException, JSONException { return new JSONArray(dlString()); } private File dlFile(File f) throws IOException { try (InputStream in = getInputStream(); OutputStream out = new BufferedOutputStream(new FileOutputStream(f))) { APKInstall.transfer(in, out); } return f; } private byte[] dlBytes() throws IOException { int len = conn.getContentLength(); len = len > 0 ? len : 32; ByteArrayOutputStream out = new ByteArrayOutputStream(len); try (InputStream in = getInputStream()) { APKInstall.transfer(in, out); } return out.toByteArray(); } }