/** * iOS specific http request implementation. */ import * as http from "../../http"; import * as types from "../../utils/types"; import * as imageSourceModule from "../../image-source"; import * as fsModule from "../../file-system"; import * as utils from "../../utils/utils"; import getter = utils.ios.getter; import * as domainDebugger from "../../debugger/debugger"; import { getFilenameFromUrl } from "./http-request-common"; export enum HttpResponseEncoding { UTF8, GBK } var currentDevice = utils.ios.getter(UIDevice, UIDevice.currentDevice); var device = currentDevice.userInterfaceIdiom === UIUserInterfaceIdiom.Phone ? "Phone" : "Pad"; var osVersion = currentDevice.systemVersion; var GET = "GET"; var USER_AGENT_HEADER = "User-Agent"; var USER_AGENT = `Mozilla/5.0 (i${device}; CPU OS ${osVersion.replace(".", "_")} like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/${osVersion} Mobile/10A5355d Safari/8536.25`; var sessionConfig = getter(NSURLSessionConfiguration, NSURLSessionConfiguration.defaultSessionConfiguration); var queue = getter(NSOperationQueue, NSOperationQueue.mainQueue); function parseJSON(source: string): any { var src = source.trim(); if (src.lastIndexOf(")") === src.length - 1) { return JSON.parse(src.substring(src.indexOf("(") + 1, src.lastIndexOf(")"))); } return JSON.parse(src); } class NSURLSessionTaskDelegateImpl extends NSObject implements NSURLSessionTaskDelegate { public static ObjCProtocols = [NSURLSessionTaskDelegate]; public URLSessionTaskWillPerformHTTPRedirectionNewRequestCompletionHandler(session: NSURLSession, task: NSURLSessionTask, response: NSHTTPURLResponse, request: NSURLRequest, completionHandler: (p1: NSURLRequest) => void): void { completionHandler(null); } } var sessionTaskDelegateInstance: NSURLSessionTaskDelegateImpl = NSURLSessionTaskDelegateImpl.new(); var defaultSession; function ensureDefaultSession() { if (!defaultSession) { defaultSession = NSURLSession.sessionWithConfigurationDelegateDelegateQueue(sessionConfig, null, queue); } } var sessionNotFollowingRedirects; function ensureSessionNotFollowingRedirects() { if (!sessionNotFollowingRedirects) { sessionNotFollowingRedirects = NSURLSession.sessionWithConfigurationDelegateDelegateQueue(sessionConfig, sessionTaskDelegateInstance, queue); } } var imageSource: typeof imageSourceModule; function ensureImageSource() { if (!imageSource) { imageSource = require("image-source"); } } export function request(options: http.HttpRequestOptions): Promise { return new Promise((resolve, reject) => { try { var network = domainDebugger.getNetwork(); var debugRequest = network && network.create(); var urlRequest = NSMutableURLRequest.requestWithURL( NSURL.URLWithString(options.url)); urlRequest.HTTPMethod = types.isDefined(options.method) ? options.method : GET; urlRequest.setValueForHTTPHeaderField(USER_AGENT, USER_AGENT_HEADER); if (options.headers) { for (var header in options.headers) { urlRequest.setValueForHTTPHeaderField(options.headers[header] + "", header); } } if (types.isString(options.content) || options.content instanceof FormData) { urlRequest.HTTPBody = NSString.stringWithString(options.content.toString()).dataUsingEncoding(4); } if (types.isNumber(options.timeout)) { urlRequest.timeoutInterval = options.timeout / 1000; } var session; if (types.isBoolean(options.dontFollowRedirects) && options.dontFollowRedirects) { ensureSessionNotFollowingRedirects(); session = sessionNotFollowingRedirects; } else { ensureDefaultSession(); session = defaultSession; } var dataTask = session.dataTaskWithRequestCompletionHandler(urlRequest, function (data: NSData, response: NSHTTPURLResponse, error: NSError) { if (error) { reject(new Error(error.localizedDescription)); } else { var headers: http.Headers = {}; if (response && response.allHeaderFields) { var headerFields = response.allHeaderFields; headerFields.enumerateKeysAndObjectsUsingBlock((key, value, stop) => { addHeader(headers, key, value); }); } if (debugRequest) { debugRequest.mimeType = response.MIMEType; debugRequest.data = data; var debugResponse = { url: options.url, status: response.statusCode, statusText: NSHTTPURLResponse.localizedStringForStatusCode(response.statusCode), headers: headers, mimeType: response.MIMEType, fromDiskCache: false } debugRequest.responseReceived(debugResponse); debugRequest.loadingFinished(); } resolve({ content: { raw: data, toString: (encoding?: any) => NSDataToString(data, encoding), toJSON: (encoding?: any) => parseJSON(NSDataToString(data, encoding)), toImage: () => { ensureImageSource(); return new Promise((resolve, reject) => { (UIImage).tns_decodeImageWithDataCompletion(data, image => { if (image) { resolve(imageSource.fromNativeSource(image)) } else { reject(new Error("Response content may not be converted to an Image")); } }); }); }, toFile: (destinationFilePath?: string) => { var fs: typeof fsModule = require("file-system"); if (!destinationFilePath) { destinationFilePath = getFilenameFromUrl(options.url); } if (data instanceof NSData) { data.writeToFileAtomically(destinationFilePath, true); return fs.File.fromPath(destinationFilePath); } else { reject(new Error(`Cannot save file with path: ${destinationFilePath}.`)); } } }, statusCode: response.statusCode, headers: headers }); } }); if (options.url && debugRequest) { var request = { url: options.url, method: "GET", headers: options.headers }; debugRequest.requestWillBeSent(request); } dataTask.resume(); } catch (ex) { reject(ex); } }); } function NSDataToString(data: any, encoding?: HttpResponseEncoding): string { let code = 4; //UTF8 if (encoding === HttpResponseEncoding.GBK) { code = 1586; } return NSString.alloc().initWithDataEncoding(data, code).toString(); } export function addHeader(headers: http.Headers, key: string, value: string): void { if (!headers[key]) { headers[key] = value; } else if (Array.isArray(headers[key])) { (headers[key]).push(value); } else { let values: string[] = [headers[key]]; values.push(value); headers[key] = values; } }