495 lines
14 KiB
Plaintext
495 lines
14 KiB
Plaintext
import { RequestOptions, RequestSuccess, RequestTask, UploadFileOptions, UploadFile, UploadTask, OnProgressUpdateResult, UploadFileSuccess, UploadFileProgressUpdateCallback, DownloadFile, DownloadTask, DownloadFileOptions, OnProgressDownloadResult, DownloadFileProgressUpdateCallback, DownloadFileSuccess } from '../interface'
|
|
import { RequestFailImpl, UploadFileFailImpl, DownloadFileFailImpl, getErrcode } from '../unierror';
|
|
import { NetworkManager, NetworkRequestListener, NetworkUploadFileListener, NetworkDownloadFileListener } from './network/NetworkManager.uts'
|
|
import Pattern from 'java.util.regex.Pattern';
|
|
import Locale from 'java.util.Locale';
|
|
import TextUtils from 'android.text.TextUtils';
|
|
import { StatusCode } from './network/StatusCode.uts'
|
|
import Key from 'kotlinx.coroutines.CoroutineExceptionHandler.Key';
|
|
import JSONObject from 'com.alibaba.fastjson.JSONObject';
|
|
import ArrayList from 'java.util.ArrayList';
|
|
import ProgressListener from 'android.os.RecoverySystem.ProgressListener';
|
|
import Handler from 'android.os.Handler';
|
|
import Looper from 'android.os.Looper';
|
|
import Class from 'java.lang.Class';
|
|
import Type from 'java.lang.reflect.Type';
|
|
import Gson from "io.dcloud.uts.gson.Gson";
|
|
import ByteBuffer from 'java.nio.ByteBuffer';
|
|
import { NetworkUtil } from './network/utils/NetworkUtil.uts';
|
|
|
|
let charsetPattern = Pattern.compile('charset=([a-z0-9-]+)')
|
|
|
|
class RunnableTask extends Runnable {
|
|
private callback : () => void | null;
|
|
private looper : Looper | null = null;
|
|
constructor(looper : Looper | null, callback : () => void) {
|
|
super();
|
|
this.looper = looper;
|
|
this.callback = callback
|
|
}
|
|
|
|
override run() {
|
|
this.callback?.()
|
|
}
|
|
|
|
public execute() {
|
|
if (this.looper == null) {
|
|
this.run();
|
|
} else {
|
|
new Handler(this.looper!!).post(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class RequestNetworkListener<T> extends NetworkRequestListener {
|
|
private param : RequestOptions<T> | null = null;
|
|
private headers : UTSJSONObject = {};
|
|
private looper : Looper | null = null;
|
|
private type : Type | null = null;
|
|
private clzName : string | null = null;
|
|
constructor(param : RequestOptions<T>, type : Type, clzName : string) {
|
|
super();
|
|
this.param = param;
|
|
this.type = type;
|
|
this.clzName = clzName;
|
|
this.looper = Looper.myLooper();
|
|
}
|
|
override onStart() : void {
|
|
}
|
|
|
|
override onHeadersReceived(statusCode : number, headers : MutableMap<String, MutableList<String>>) : void {
|
|
let simpleHeaders = {};
|
|
if (headers != null) {
|
|
let it = headers.iterator();
|
|
while (it.hasNext()) {
|
|
let entry = it.next();
|
|
let key = entry.key;
|
|
let value = entry.value;
|
|
|
|
let tmpKey = '_';
|
|
if (key == null) {
|
|
key = tmpKey;
|
|
}
|
|
|
|
if (value.size == 0) {
|
|
continue;
|
|
} else if (value.size == 1) {
|
|
simpleHeaders[key] = value.get(0);
|
|
} else {
|
|
simpleHeaders[key] = value.toString();
|
|
}
|
|
}
|
|
}
|
|
this.headers = simpleHeaders;
|
|
}
|
|
|
|
override onProgress(progress : number) : void {
|
|
}
|
|
|
|
override onComplete(option : UTSJSONObject) : void {
|
|
let kParam = this.param;
|
|
let result = {};
|
|
if (kParam != null) {
|
|
if (option == null || '-1' == option['statusCode']) {
|
|
if (this.headers != null) {
|
|
result['header'] = this.headers;
|
|
}
|
|
let exception = option['cause']! as Exception;
|
|
const originalErrMsg = option['errorMsg']! as string;
|
|
let cause = exception.cause.toString();
|
|
if(exception.cause == null) {
|
|
cause = originalErrMsg
|
|
}
|
|
let errMsg = originalErrMsg
|
|
let errCode = (option['errorCode']! as string).toInt();
|
|
if (errMsg.indexOf("timeout") != -1) {
|
|
errCode = 5;
|
|
errMsg = "time out";
|
|
} else if (cause.contains("Connection refused")) {
|
|
errCode = 1000;
|
|
} else if (cause.contains("Network is unreachable")) {
|
|
errCode = 600003;
|
|
} else if (cause.contains("invalid URL")) {
|
|
errCode = 600009;
|
|
}
|
|
|
|
|
|
let failResult = new RequestFailImpl(getErrcode(errCode));
|
|
failResult.cause = new SourceError(cause);
|
|
new RunnableTask(this.looper, () => {
|
|
if (kParam != null) {
|
|
let fail = kParam.fail;
|
|
if (fail != null) {
|
|
fail(failResult);
|
|
}
|
|
let complete = kParam.complete;
|
|
if (complete != null) {
|
|
complete(failResult);
|
|
}
|
|
}
|
|
}).execute();
|
|
} else {
|
|
result['statusCode'] = option['statusCode'];
|
|
if (option['originalData'] == null) {
|
|
if ("java.lang.Object".equals(this.clzName, true)) {
|
|
let errMsg = option['errorMsg'];
|
|
if (errMsg != null) {
|
|
let errMsgJson = JSON.parse((option['errorMsg']! as string));
|
|
if (errMsgJson != null) {
|
|
result['data'] = errMsgJson;
|
|
} else {
|
|
result['data'] = errMsg;
|
|
}
|
|
} else {
|
|
result['data'] = "error";
|
|
}
|
|
} else if("java.lang.String".equals(this.clzName, true)){
|
|
result['data'] = option['errorMsg'] ?? "error";
|
|
} else if("io.dcloud.uts.ArrayBuffer".equals(this.clzName, true)){
|
|
let textDecoder : TextEncoder = new TextEncoder()
|
|
let error :string= (option['errorMsg'] ?? "error") as string
|
|
let uint8Array = textDecoder.encode(error)
|
|
result['data'] = uint8Array.buffer
|
|
}else {
|
|
let errMsg = option['errorMsg'];
|
|
if (errMsg != null) {
|
|
let errMsgJson = JSON.parse<T>(errMsg as string, this.type);
|
|
if (errMsgJson != null) {
|
|
result['data'] = errMsgJson;
|
|
} else {
|
|
let failResult = new RequestFailImpl(getErrcode(100002));
|
|
failResult.data = errMsg
|
|
failResult.cause = new SourceError("Error message parsing failed")
|
|
new RunnableTask(this.looper, () => {
|
|
if (kParam != null) {
|
|
let fail = kParam.fail;
|
|
if (fail != null) {
|
|
fail(failResult);
|
|
}
|
|
let complete = kParam.complete;
|
|
if (complete != null) {
|
|
complete(failResult);
|
|
}
|
|
}
|
|
}).execute();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
let charset = "";
|
|
let headers = this.headers.toJSONObject() as JSONObject;
|
|
if (headers != null) {
|
|
for (key in headers.keys) {
|
|
if (key.equals("Content-Type", true)) {
|
|
charset = headers[key] as string;
|
|
}
|
|
}
|
|
}
|
|
let data :any | null;
|
|
if("io.dcloud.uts.ArrayBuffer".equals(this.clzName, true)){
|
|
let by= option['originalData'] as ByteArray
|
|
data =ArrayBuffer.fromByteBuffer(ByteBuffer.wrap(by))
|
|
}else{
|
|
let strData = this.readAsString(option['originalData'] as ByteArray, charset);
|
|
|
|
let type = kParam.responseType != null ? kParam.responseType : kParam.dataType;
|
|
if (type == null) {
|
|
type = charset;
|
|
}
|
|
if (kParam.method == "HEAD") {
|
|
type = "";
|
|
}
|
|
|
|
data = this.parseData(strData, type);
|
|
if (data == null) {
|
|
let failResult = new RequestFailImpl(getErrcode(100001));
|
|
|
|
new RunnableTask(this.looper, () => {
|
|
if (kParam != null) {
|
|
let fail = kParam.fail;
|
|
if (fail != null) {
|
|
fail(failResult);
|
|
}
|
|
let complete = kParam.complete;
|
|
if (complete != null) {
|
|
complete(failResult);
|
|
}
|
|
}
|
|
}).execute();
|
|
return;
|
|
}
|
|
|
|
}
|
|
result['data'] = data;
|
|
}
|
|
result['statusText'] = StatusCode.getStatus(option['statusCode'] as string);
|
|
if (this.headers != null) {
|
|
result['header'] = this.headers;
|
|
}
|
|
|
|
let tmp : RequestSuccess<T> = {
|
|
data: result['data'] as T,
|
|
statusCode: (result['statusCode'] as string).toInt(),
|
|
header: result['header']!,
|
|
cookies: NetworkUtil.parseCookie(this.headers)
|
|
};
|
|
new RunnableTask(this.looper, () => {
|
|
if (kParam != null) {
|
|
let success = kParam.success;
|
|
if (success != null) {
|
|
success(tmp);
|
|
}
|
|
let complete = kParam.complete;
|
|
if (complete != null) {
|
|
complete(tmp);
|
|
}
|
|
}
|
|
}).execute();
|
|
}
|
|
}
|
|
}
|
|
|
|
private readAsString(byteArray : ByteArray, type : string) : string {
|
|
let charsetType = "utf-8";
|
|
|
|
if (type != null) {
|
|
let matcher = charsetPattern.matcher(type.lowercase(Locale.ENGLISH));
|
|
if (matcher.find()) {
|
|
charsetType = matcher.group(1);
|
|
}
|
|
}
|
|
|
|
try {
|
|
return new String(byteArray, charset(charsetType));
|
|
} catch (e : Exception) {
|
|
return new String(byteArray);
|
|
}
|
|
}
|
|
|
|
private parseData(data : string, type : string) : any | null {
|
|
if ("java.lang.Object".equals(this.clzName, true)) {
|
|
if (type.indexOf("json") != -1) {
|
|
return JSON.parse(data);
|
|
} else if (type == 'jsonp') {
|
|
if (TextUtils.isEmpty(data)) {
|
|
return {};
|
|
}
|
|
let start = data.indexOf('(') + 1;
|
|
let end = data.indexOf(')');
|
|
if (start == 0 || start >= end) {
|
|
return {};
|
|
}
|
|
let tmp = data.substring(start, end);
|
|
return JSON.parse(tmp);
|
|
} else {
|
|
let jsonData: any | null = null;
|
|
try{
|
|
const gson = new Gson();
|
|
jsonData = gson.fromJson<any>(data, this.type);
|
|
}catch(e){
|
|
}
|
|
return jsonData ?? data;
|
|
}
|
|
} else if ("java.lang.String".equals(this.clzName, true)){
|
|
return data;
|
|
} else {
|
|
return JSON.parse<T>(data, this.type);
|
|
}
|
|
}
|
|
|
|
private parseCookie(header : UTSJSONObject | null) : string[] {
|
|
if (header == null) {
|
|
return []
|
|
}
|
|
let cookiesStr = header.getString('Set-Cookie')
|
|
if (cookiesStr == null) {
|
|
cookiesStr = header.getString('set-cookie')
|
|
}
|
|
if (cookiesStr == null) {
|
|
return []
|
|
}
|
|
let cookiesArr = new Array<string>()
|
|
if (cookiesStr.charAt(0) == "[" && cookiesStr.charAt(cookiesStr.length - 1) == "]") {
|
|
cookiesStr = cookiesStr.slice(1, -1)
|
|
}
|
|
|
|
const handleCookiesArr = cookiesStr.split(';')
|
|
for (let i = 0; i < handleCookiesArr.length; i++) {
|
|
if (handleCookiesArr[i].indexOf('Expires=') != -1 || handleCookiesArr[i].indexOf('expires=') != -1) {
|
|
cookiesArr.push(handleCookiesArr[i].replace(',', ''))
|
|
} else {
|
|
cookiesArr.push(handleCookiesArr[i])
|
|
}
|
|
}
|
|
cookiesArr = cookiesArr.join(';').split(',')
|
|
|
|
return cookiesArr
|
|
}
|
|
|
|
}
|
|
|
|
|
|
class UploadNetworkListener implements NetworkUploadFileListener {
|
|
private param : UploadFileOptions | null = null;
|
|
public progressListeners = new ArrayList<UploadFileProgressUpdateCallback>();
|
|
private looper : Looper | null = null;
|
|
constructor(param : UploadFileOptions) {
|
|
this.param = param;
|
|
this.looper = Looper.myLooper();
|
|
}
|
|
|
|
onProgress(progressUpdate : OnProgressUpdateResult) {
|
|
if (this.progressListeners.size != 0) {
|
|
new RunnableTask(this.looper, () => {
|
|
for (let i : Int = 0; i < this.progressListeners.size; i++) {
|
|
let listener = this.progressListeners.get(i);
|
|
listener(progressUpdate);
|
|
}
|
|
}).execute();
|
|
}
|
|
}
|
|
|
|
onComplete(option : UTSJSONObject) {
|
|
let kParam = this.param;
|
|
if (kParam != null) {
|
|
const errorMsg = option["errorMsg"];
|
|
if (errorMsg != null) {
|
|
let errCode = (option['errorCode']! as string).toInt();
|
|
let failResult = new UploadFileFailImpl(getErrcode(errCode));
|
|
let cause = option['cause'];
|
|
if (cause != null) {
|
|
failResult.cause = cause as SourceError;
|
|
}
|
|
|
|
new RunnableTask(this.looper, () => {
|
|
if (kParam != null) {
|
|
let fail = kParam.fail;
|
|
if (fail != null) {
|
|
fail(failResult);
|
|
}
|
|
let complete = kParam.complete;
|
|
if (complete != null) {
|
|
complete(failResult);
|
|
}
|
|
}
|
|
}).execute();
|
|
|
|
} else {
|
|
let kData = option["data"];
|
|
let data = "";
|
|
if (kData != null) {
|
|
data = kData as string;
|
|
}
|
|
let statusCode = (option['statusCode']! as string).toInt();
|
|
let successResult : UploadFileSuccess = {
|
|
data: data,
|
|
statusCode: statusCode
|
|
}
|
|
new RunnableTask(this.looper, () => {
|
|
if (kParam != null) {
|
|
let success = kParam.success;
|
|
if (success != null) {
|
|
success(successResult);
|
|
}
|
|
let complete = kParam.complete;
|
|
if (complete != null) {
|
|
complete(successResult);
|
|
}
|
|
}
|
|
}).execute();
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
class DownloadNetworkListener implements NetworkDownloadFileListener {
|
|
private param : DownloadFileOptions | null = null;
|
|
public progressListeners = new ArrayList<DownloadFileProgressUpdateCallback>();
|
|
private looper : Looper | null = null;
|
|
constructor(param : DownloadFileOptions) {
|
|
this.param = param;
|
|
this.looper = Looper.myLooper();
|
|
}
|
|
|
|
onProgress(progressUpdate : OnProgressDownloadResult) {
|
|
if (this.progressListeners.size != 0) {
|
|
new RunnableTask(this.looper, () => {
|
|
for (let i : Int = 0; i < this.progressListeners.size; i++) {
|
|
let listener = this.progressListeners.get(i);
|
|
listener(progressUpdate);
|
|
}
|
|
}).execute();
|
|
}
|
|
}
|
|
onComplete(option : UTSJSONObject) {
|
|
let kParam = this.param;
|
|
if (kParam != null) {
|
|
let errMsg = option['errorMsg'];
|
|
if (errMsg != null) {
|
|
let errCode = (option['errorCode']! as string).toInt();
|
|
let failResult = new DownloadFileFailImpl(getErrcode(errCode));
|
|
failResult.errMsg = errMsg as string;
|
|
let cause = option['cause'];
|
|
if (cause != null) {
|
|
failResult.cause = cause as SourceError;
|
|
}
|
|
|
|
new RunnableTask(this.looper, () => {
|
|
if (kParam != null) {
|
|
let fail = kParam.fail;
|
|
if (fail != null) {
|
|
fail(failResult);
|
|
}
|
|
let complete = kParam.complete;
|
|
if (complete != null) {
|
|
complete(failResult);
|
|
}
|
|
}
|
|
}).execute();
|
|
} else {
|
|
let kTempFilePath = option["tempFilePath"];
|
|
let tempFilePath = "";
|
|
if (kTempFilePath != null) {
|
|
tempFilePath = kTempFilePath as string;
|
|
}
|
|
let statusCode = (option['statusCode']! as string).toInt();
|
|
let successResult : DownloadFileSuccess = {
|
|
tempFilePath: tempFilePath,
|
|
statusCode: statusCode
|
|
}
|
|
|
|
new RunnableTask(this.looper, () => {
|
|
if (kParam != null) {
|
|
let success = kParam.success;
|
|
if (success != null) {
|
|
success(successResult);
|
|
}
|
|
let complete = kParam.complete;
|
|
if (complete != null) {
|
|
complete(successResult);
|
|
}
|
|
}
|
|
}).execute();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@UTSAndroid.keyword("inline")
|
|
@UTSAndroid.keyword('reified')
|
|
export function request<T>(options : RequestOptions<T>) : RequestTask {
|
|
const type = UTSAndroid.getGenericType<T>();
|
|
const clzName = UTSAndroid.getGenericClassName<T>();
|
|
return NetworkManager.getInstance().request<T>(options, new RequestNetworkListener<T>(options, type, clzName));
|
|
}
|
|
|
|
export const uploadFile : UploadFile = (options : UploadFileOptions) : UploadTask => {
|
|
return NetworkManager.getInstance().uploadFile(options, new UploadNetworkListener(options));
|
|
}
|
|
|
|
export const downloadFile : DownloadFile = (options : DownloadFileOptions) : DownloadTask => {
|
|
return NetworkManager.getInstance().downloadFile(options, new DownloadNetworkListener(options));
|
|
} |