@klippa/nativescript-http
Version:
The best way to do HTTP requests in NativeScript, a drop-in replacement for the core HTTP with important improvements and additions like proper connection pooling, form data support and certificate pinning
424 lines • 17 kB
JavaScript
import { ImageSource, File as nsFile, Screen, } from '@nativescript/core';
import { completeSelfCheck, getFilenameFromUrl, HTTPFormData, HTTPFormDataEntry, ImageParseMethod, } from "./http.common";
import { NetworkAgent } from "@nativescript/core/debugger";
export { HTTPFormData, HTTPFormDataEntry, ImageParseMethod } from "./http.common";
export var HttpResponseEncoding;
(function (HttpResponseEncoding) {
HttpResponseEncoding[HttpResponseEncoding["UTF8"] = 0] = "UTF8";
HttpResponseEncoding[HttpResponseEncoding["GBK"] = 1] = "GBK";
})(HttpResponseEncoding || (HttpResponseEncoding = {}));
function parseJSON(source) {
const src = source.trim();
if (src.lastIndexOf(")") === src.length - 1) {
return JSON.parse(src.substring(src.indexOf("(") + 1, src.lastIndexOf(")")));
}
return JSON.parse(src);
}
let requestIdCounter = 0;
const pendingRequests = {};
let customUserAgent;
let completeCallback;
function ensureCompleteCallback() {
if (completeCallback) {
return;
}
completeCallback = new com.klippa.NativeScriptHTTP.Async.CompleteCallback({
onComplete: function (result, context) {
onRequestComplete(context, result);
},
onError: function (error, context) {
onRequestError(error, context);
},
});
}
function onRequestComplete(requestId, result) {
const callbacks = pendingRequests[requestId];
delete pendingRequests[requestId];
if (result.error) {
callbacks.rejectCallback(new Error(result.error.toString()));
return;
}
const headers = {};
if (result.headers) {
const jHeaders = result.headers;
const length = jHeaders.size();
let pair;
for (let i = 0; i < length; i++) {
pair = jHeaders.get(i);
addHeader(headers, pair.key, pair.value);
}
}
if (global.__inspector && global.__inspector.isConnected) {
NetworkAgent.responseReceived(requestId, result, headers);
}
callbacks.resolveCallback({
content: {
raw: result.raw,
toArrayBuffer: () => Uint8Array.from(result.raw.toByteArray()).buffer,
toString: (encoding) => {
let str;
if (encoding) {
str = decodeResponse(result.raw, encoding);
}
else {
str = result.responseAsString;
}
if (typeof str === "string") {
return str;
}
else {
throw new Error("Response content may not be converted to string");
}
},
toJSON: (encoding) => {
let str;
if (encoding) {
str = decodeResponse(result.raw, encoding);
}
else {
str = result.responseAsString;
}
return parseJSON(str);
},
toImage: () => {
return new Promise((resolveImage, rejectImage) => {
if (result.responseAsImage != null) {
resolveImage(new ImageSource(result.responseAsImage));
}
else {
rejectImage(new Error("Response content may not be converted to an Image"));
}
});
},
toFile: (destinationFilePath) => {
if (!destinationFilePath) {
destinationFilePath = getFilenameFromUrl(callbacks.url);
}
let stream;
try {
const file = nsFile.fromPath(destinationFilePath);
const javaFile = new java.io.File(destinationFilePath);
stream = new java.io.FileOutputStream(javaFile);
stream.write(result.raw.toByteArray());
return file;
}
catch (exception) {
throw new Error(`Cannot save file with path: ${destinationFilePath}.`);
}
finally {
if (stream) {
stream.close();
}
}
}
},
statusCode: result.statusCode,
headers: headers
});
}
function onRequestError(error, requestId) {
const callbacks = pendingRequests[requestId];
delete pendingRequests[requestId];
if (callbacks) {
callbacks.rejectCallback(new Error(error));
}
}
export function buildJavaOptions(options) {
if (typeof options.url !== "string") {
throw new Error("Http request must provide a valid url.");
}
const javaOptions = new com.klippa.NativeScriptHTTP.Async.Http.RequestOptions();
javaOptions.url = options.url;
let contentType;
let mediaType = null;
let userAgent;
if (options.headers) {
for (let key in options.headers) {
if (key.toLowerCase() === "content-type") {
contentType = options.headers[key];
}
if (key.toLowerCase() === "user-agent") {
userAgent = options.headers[key];
}
}
if (contentType) {
mediaType = okhttp3.MediaType.parse(contentType);
}
}
if (!userAgent && customUserAgent) {
if (!options.headers) {
options.headers = {};
}
options.headers["User-Agent"] = customUserAgent;
}
if (typeof options.method === "string") {
javaOptions.method = options.method;
}
const upperCaseMethod = options.method && typeof options.method === "string" ? options.method.toUpperCase() : "GET";
if (upperCaseMethod === "POST" || upperCaseMethod === "PATCH" || upperCaseMethod === "PUT" || upperCaseMethod === "PROPPATCH" || upperCaseMethod === "REPORT") {
if (typeof options.content === "string" || options.content instanceof FormData) {
if (!mediaType) {
mediaType = okhttp3.MediaType.parse("application/x-www-form-urlencoded");
}
javaOptions.content = okhttp3.RequestBody.create(mediaType, options.content.toString());
}
else if (options.content instanceof ArrayBuffer) {
if (!mediaType) {
mediaType = okhttp3.MediaType.parse("application/x-www-form-urlencoded");
}
const typedArray = new Uint8Array(options.content);
const nativeBuffer = java.nio.ByteBuffer.wrap(Array.from(typedArray));
javaOptions.content = okhttp3.RequestBody.create(mediaType, nativeBuffer.array());
}
else {
let matched = false;
if (!matched && options.content instanceof Blob) {
if (!mediaType && options.content.type) {
mediaType = okhttp3.MediaType.parse(options.content.type);
}
const typedArray = new Uint8Array(Blob.InternalAccessor.getBuffer(options.content).buffer.slice(0));
const nativeBuffer = java.nio.ByteBuffer.wrap(Array.from(typedArray));
javaOptions.content = okhttp3.RequestBody.create(mediaType, nativeBuffer.array());
matched = true;
}
if (!matched && options.content instanceof HTTPFormData) {
matched = true;
if (!mediaType) {
mediaType = okhttp3.MediaType.parse("multipart/form-data");
}
const builder = new okhttp3.MultipartBody.Builder();
builder.setType(mediaType);
const contentValues = options.content;
contentValues.forEach(((value, key) => {
if (typeof value === "string") {
builder.addFormDataPart(key, value);
}
else if (value instanceof ArrayBuffer) {
const typedArray = new Uint8Array(value);
const nativeBuffer = java.nio.ByteBuffer.wrap(Array.from(typedArray));
builder.addFormDataPart(key, null, okhttp3.RequestBody.create(null, nativeBuffer.array()));
}
else if (value instanceof Blob) {
let formDataPartMediaType = null;
if (value.type) {
formDataPartMediaType = okhttp3.MediaType.parse(value.type);
}
let filename = null;
if (value instanceof File) {
filename = value.name;
}
const typedArray = new Uint8Array(Blob.InternalAccessor.getBuffer(value).buffer.slice(0));
const nativeBuffer = java.nio.ByteBuffer.wrap(Array.from(typedArray));
builder.addFormDataPart(key, filename, okhttp3.RequestBody.create(formDataPartMediaType, nativeBuffer.array()));
}
else if (value instanceof HTTPFormDataEntry) {
let formDataPartMediaType = null;
if (value.type) {
formDataPartMediaType = okhttp3.MediaType.parse(value.type);
}
const filename = value.name || "";
if (value.data instanceof ArrayBuffer) {
const typedArray = new Uint8Array(value.data);
const nativeBuffer = java.nio.ByteBuffer.wrap(Array.from(typedArray));
builder.addFormDataPart(key, filename, okhttp3.RequestBody.create(formDataPartMediaType, nativeBuffer.array()));
}
else if (value.data instanceof Blob) {
const typedArray = new Uint8Array(Blob.InternalAccessor.getBuffer(value.data).buffer.slice(0));
const nativeBuffer = java.nio.ByteBuffer.wrap(Array.from(typedArray));
builder.addFormDataPart(key, filename, okhttp3.RequestBody.create(formDataPartMediaType, nativeBuffer.array()));
}
else {
builder.addFormDataPart(key, filename, okhttp3.RequestBody.create(formDataPartMediaType, value.data));
}
}
else {
builder.addFormDataPart(key, null, okhttp3.RequestBody.create(null, value));
}
}));
javaOptions.content = builder.build();
}
if (!matched && options.content) {
javaOptions.content = okhttp3.RequestBody.create(mediaType, options.content);
}
else if (!matched) {
javaOptions.content = okhttp3.RequestBody.create(mediaType, "");
}
}
}
if (typeof options.timeout === "number") {
javaOptions.timeout = options.timeout;
}
if (typeof options.dontFollowRedirects === "boolean") {
javaOptions.dontFollowRedirects = options.dontFollowRedirects;
}
if (options.headers) {
const arrayList = new java.util.ArrayList();
const pair = com.klippa.NativeScriptHTTP.Async.Http.KeyValuePair;
for (let key in options.headers) {
arrayList.add(new pair(key, options.headers[key] + ""));
}
javaOptions.headers = arrayList;
}
const ourScreen = Screen.mainScreen;
javaOptions.screenWidth = ourScreen.widthPixels;
javaOptions.screenHeight = ourScreen.heightPixels;
if (typeof (options.forceImageParsing) !== "undefined") {
javaOptions.forceImageParsing = options.forceImageParsing;
}
return javaOptions;
}
export function request(options) {
if (options === undefined || options === null) {
return Promise.reject("No options given");
}
if (options.url === "https://nativescript-http-integration-check.local") {
return completeSelfCheck(options);
}
return new Promise((resolve, reject) => {
try {
const javaOptions = buildJavaOptions(options);
if (global.__inspector && global.__inspector.isConnected) {
NetworkAgent.requestWillBeSent(requestIdCounter, options);
}
const callbacks = {
url: options.url,
resolveCallback: resolve,
rejectCallback: reject
};
pendingRequests[requestIdCounter] = callbacks;
ensureCompleteCallback();
com.klippa.NativeScriptHTTP.Async.Http.MakeRequest(javaOptions, completeCallback, new java.lang.Integer(requestIdCounter));
requestIdCounter++;
}
catch (ex) {
reject(ex);
}
});
}
function decodeResponse(raw, encoding) {
let charsetName = "UTF-8";
if (encoding === HttpResponseEncoding.GBK) {
charsetName = "GBK";
}
return raw.toString(charsetName);
}
export function getString(arg) {
return new Promise((resolve, reject) => {
request(typeof arg === "string" ? { url: arg, method: "GET" } : arg)
.then(r => {
try {
const str = r.content.toString();
resolve(str);
}
catch (e) {
reject(e);
}
}, e => reject(e));
});
}
export function getJSON(arg) {
return new Promise((resolve, reject) => {
request(typeof arg === "string" ? { url: arg, method: "GET" } : arg)
.then(r => {
try {
const json = r.content.toJSON();
resolve(json);
}
catch (e) {
reject(e);
}
}, e => reject(e));
});
}
export function getImage(arg) {
return new Promise((resolve, reject) => {
request(typeof arg === "string" ? { url: arg, method: "GET" } : arg)
.then(r => {
try {
resolve(r.content.toImage());
}
catch (err) {
reject(err);
}
}, err => {
reject(err);
});
});
}
export function getFile(arg, destinationFilePath) {
return new Promise((resolve, reject) => {
request(typeof arg === "string" ? { url: arg, method: "GET" } : arg)
.then(r => {
try {
const file = r.content.toFile(destinationFilePath);
resolve(file);
}
catch (e) {
reject(e);
}
}, e => reject(e));
});
}
export function getBinary(arg) {
return new Promise((resolve, reject) => {
request(typeof arg === "string" ? { url: arg, method: "GET" } : arg)
.then(r => {
try {
const arrayBuffer = r.content.toArrayBuffer();
resolve(arrayBuffer);
}
catch (e) {
reject(e);
}
}, e => reject(e));
});
}
export function addHeader(headers, key, value) {
if (!headers[key]) {
headers[key] = value;
}
else if (Array.isArray(headers[key])) {
headers[key].push(value);
}
else {
const values = [headers[key]];
values.push(value);
headers[key] = values;
}
}
export function setImageParseMethod(imageParseMethod) {
if (imageParseMethod === ImageParseMethod.ALWAYS) {
com.klippa.NativeScriptHTTP.Async.Http.SetImageParseMethod(com.klippa.NativeScriptHTTP.Async.Http.ImageParseMethod.ALWAYS);
}
else if (imageParseMethod === ImageParseMethod.CONTENTTYPE) {
com.klippa.NativeScriptHTTP.Async.Http.SetImageParseMethod(com.klippa.NativeScriptHTTP.Async.Http.ImageParseMethod.CONTENTTYPE);
}
else if (imageParseMethod === ImageParseMethod.NEVER) {
com.klippa.NativeScriptHTTP.Async.Http.SetImageParseMethod(com.klippa.NativeScriptHTTP.Async.Http.ImageParseMethod.NEVER);
}
}
export function setConcurrencyLimits(maxRequests, maxRequestsPerHost) {
com.klippa.NativeScriptHTTP.Async.Http.SetConcurrencyLimits(maxRequests, maxRequestsPerHost);
}
export function clearCookies() {
com.klippa.NativeScriptHTTP.Async.Http.ClearCookies();
}
export function setUserAgent(userAgent) {
customUserAgent = userAgent;
}
export function certificatePinningAdd(pattern, hashes) {
const newHashes = hashes.map(value => "sha256/" + value);
com.klippa.NativeScriptHTTP.Async.Http.PinCertificate(pattern, newHashes);
}
export function certificatePinningClear() {
com.klippa.NativeScriptHTTP.Async.Http.RemoveCertificatePins();
}
export const Http = {
getFile,
getImage,
getJSON,
getString,
request,
};
export { ImageCache } from './image-cache';
//# sourceMappingURL=http.android.js.map