initial commit taken from gitlab.lrz.de

This commit is contained in:
privatereese
2018-08-24 18:09:42 +02:00
parent ae54ed4c48
commit fc05486403
28494 changed files with 2159823 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule FormData
* @flow
*/
'use strict';
type FormDataValue = any;
type FormDataNameValuePair = [string, FormDataValue];
type Headers = {[name: string]: string};
type FormDataPart = {
string: string,
headers: Headers,
} | {
uri: string,
headers: Headers,
name?: string,
type?: string,
};
/**
* Polyfill for XMLHttpRequest2 FormData API, allowing multipart POST requests
* with mixed data (string, native files) to be submitted via XMLHttpRequest.
*
* Example:
*
* var photo = {
* uri: uriFromCameraRoll,
* type: 'image/jpeg',
* name: 'photo.jpg',
* };
*
* var body = new FormData();
* body.append('authToken', 'secret');
* body.append('photo', photo);
* body.append('title', 'A beautiful photo!');
*
* xhr.open('POST', serverURL);
* xhr.send(body);
*/
class FormData {
_parts: Array<FormDataNameValuePair>;
constructor() {
this._parts = [];
}
append(key: string, value: FormDataValue) {
// The XMLHttpRequest spec doesn't specify if duplicate keys are allowed.
// MDN says that any new values should be appended to existing values.
// In any case, major browsers allow duplicate keys, so that's what we'll do
// too. They'll simply get appended as additional form data parts in the
// request body, leaving the server to deal with them.
this._parts.push([key, value]);
}
getParts(): Array<FormDataPart> {
return this._parts.map(([name, value]) => {
var contentDisposition = 'form-data; name="' + name + '"';
var headers: Headers = {'content-disposition': contentDisposition};
// The body part is a "blob", which in React Native just means
// an object with a `uri` attribute. Optionally, it can also
// have a `name` and `type` attribute to specify filename and
// content type (cf. web Blob interface.)
if (typeof value === 'object' && value) {
if (typeof value.name === 'string') {
headers['content-disposition'] += '; filename="' + value.name + '"';
}
if (typeof value.type === 'string') {
headers['content-type'] = value.type;
}
return {...value, headers, fieldName: name};
}
// Convert non-object values to strings as per FormData.append() spec
return {string: String(value), headers, fieldName: name};
});
}
}
module.exports = FormData;

251
node_modules/react-native/Libraries/Network/NetInfo.js generated vendored Normal file
View File

@@ -0,0 +1,251 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule NetInfo
* @flow
*/
'use strict';
const Map = require('Map');
const NativeEventEmitter = require('NativeEventEmitter');
const NativeModules = require('NativeModules');
const Platform = require('Platform');
const RCTNetInfo = NativeModules.NetInfo;
const NetInfoEventEmitter = new NativeEventEmitter(RCTNetInfo);
const DEVICE_CONNECTIVITY_EVENT = 'networkStatusDidChange';
type ChangeEventName = $Enum<{
connectionChange: string,
change: string,
}>;
type ReachabilityStateIOS = $Enum<{
cell: string,
none: string,
unknown: string,
wifi: string,
}>;
type ConnectivityStateAndroid = $Enum<{
NONE: string,
MOBILE: string,
WIFI: string,
MOBILE_MMS: string,
MOBILE_SUPL: string,
MOBILE_DUN: string,
MOBILE_HIPRI: string,
WIMAX: string,
BLUETOOTH: string,
DUMMY: string,
ETHERNET: string,
MOBILE_FOTA: string,
MOBILE_IMS: string,
MOBILE_CBS: string,
WIFI_P2P: string,
MOBILE_IA: string,
MOBILE_EMERGENCY: string,
PROXY: string,
VPN: string,
UNKNOWN: string,
}>;
const _subscriptions = new Map();
let _isConnectedDeprecated;
if (Platform.OS === 'ios') {
_isConnectedDeprecated = function(
reachability: ReachabilityStateIOS,
): bool {
return reachability !== 'none' && reachability !== 'unknown';
};
} else if (Platform.OS === 'android') {
_isConnectedDeprecated = function(
connectionType: ConnectivityStateAndroid,
): bool {
return connectionType !== 'NONE' && connectionType !== 'UNKNOWN';
};
}
function _isConnected(connection) {
return connection.type !== 'none' && connection.type !== 'unknown';
}
const _isConnectedSubscriptions = new Map();
/**
* NetInfo exposes info about online/offline status.
*
* See https://facebook.github.io/react-native/docs/netinfo.html
*/
const NetInfo = {
/**
* Adds an event handler.
*
* See https://facebook.github.io/react-native/docs/netinfo.html#addeventlistener
*/
addEventListener(
eventName: ChangeEventName,
handler: Function
): {remove: () => void} {
let listener;
if (eventName === 'connectionChange') {
listener = NetInfoEventEmitter.addListener(
DEVICE_CONNECTIVITY_EVENT,
(appStateData) => {
handler({
type: appStateData.connectionType,
effectiveType: appStateData.effectiveConnectionType
});
}
);
} else if (eventName === 'change') {
console.warn('NetInfo\'s "change" event is deprecated. Listen to the "connectionChange" event instead.');
listener = NetInfoEventEmitter.addListener(
DEVICE_CONNECTIVITY_EVENT,
(appStateData) => {
handler(appStateData.network_info);
}
);
} else {
console.warn('Trying to subscribe to unknown event: "' + eventName + '"');
return {
remove: () => {}
};
}
_subscriptions.set(handler, listener);
return {
remove: () => NetInfo.removeEventListener(eventName, handler)
};
},
/**
* Removes the listener for network status changes.
*
* See https://facebook.github.io/react-native/docs/netinfo.html#removeeventlistener
*/
removeEventListener(
eventName: ChangeEventName,
handler: Function
): void {
const listener = _subscriptions.get(handler);
if (!listener) {
return;
}
listener.remove();
_subscriptions.delete(handler);
},
/**
* This function is deprecated. Use `getConnectionInfo` instead.
* Returns a promise that resolves with one of the deprecated connectivity
* types:
*
* The following connectivity types are deprecated. They're used by the
* deprecated APIs `fetch` and the `change` event.
*
* iOS connectivity types (deprecated):
* - `none` - device is offline
* - `wifi` - device is online and connected via wifi, or is the iOS simulator
* - `cell` - device is connected via Edge, 3G, WiMax, or LTE
* - `unknown` - error case and the network status is unknown
*
* Android connectivity types (deprecated).
* - `NONE` - device is offline
* - `BLUETOOTH` - The Bluetooth data connection.
* - `DUMMY` - Dummy data connection.
* - `ETHERNET` - The Ethernet data connection.
* - `MOBILE` - The Mobile data connection.
* - `MOBILE_DUN` - A DUN-specific Mobile data connection.
* - `MOBILE_HIPRI` - A High Priority Mobile data connection.
* - `MOBILE_MMS` - An MMS-specific Mobile data connection.
* - `MOBILE_SUPL` - A SUPL-specific Mobile data connection.
* - `VPN` - A virtual network using one or more native bearers. Requires
* API Level 21
* - `WIFI` - The WIFI data connection.
* - `WIMAX` - The WiMAX data connection.
* - `UNKNOWN` - Unknown data connection.
*
* The rest of the connectivity types are hidden by the Android API, but can
* be used if necessary.
*/
fetch(): Promise<any> {
console.warn('NetInfo.fetch() is deprecated. Use NetInfo.getConnectionInfo() instead.');
return RCTNetInfo.getCurrentConnectivity().then(resp => resp.network_info);
},
/**
* See https://facebook.github.io/react-native/docs/netinfo.html#getconnectioninfo
*/
getConnectionInfo(): Promise<any> {
return RCTNetInfo.getCurrentConnectivity().then(resp => {
return {
type: resp.connectionType,
effectiveType: resp.effectiveConnectionType,
};
});
},
/**
* An object with the same methods as above but the listener receives a
* boolean which represents the internet connectivity.
*
* See https://facebook.github.io/react-native/docs/netinfo.html#isconnected
*/
isConnected: {
addEventListener(
eventName: ChangeEventName,
handler: Function
): {remove: () => void} {
const listener = (connection) => {
if (eventName === 'change') {
handler(_isConnectedDeprecated(connection));
} else if (eventName === 'connectionChange') {
handler(_isConnected(connection));
}
};
_isConnectedSubscriptions.set(handler, listener);
NetInfo.addEventListener(
eventName,
listener
);
return {
remove: () => NetInfo.isConnected.removeEventListener(eventName, handler)
};
},
removeEventListener(
eventName: ChangeEventName,
handler: Function
): void {
const listener = _isConnectedSubscriptions.get(handler);
NetInfo.removeEventListener(
eventName,
/* $FlowFixMe(>=0.36.0 site=react_native_fb,react_native_oss) Flow error
* detected during the deploy of Flow v0.36.0. To see the error, remove
* this comment and run Flow */
listener
);
_isConnectedSubscriptions.delete(handler);
},
fetch(): Promise<any> {
return NetInfo.getConnectionInfo().then(_isConnected);
},
},
isConnectionExpensive(): Promise<boolean> {
return (
Platform.OS === 'android' ? RCTNetInfo.isConnectionMetered() : Promise.reject(new Error('Currently not supported on iOS'))
);
},
};
module.exports = NetInfo;

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTInvalidating.h>
#import <React/RCTURLRequestHandler.h>
/**
* This is the default RCTURLRequestHandler implementation for data URL requests.
*/
@interface RCTDataRequestHandler : NSObject <RCTURLRequestHandler, RCTInvalidating>
@end

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTDataRequestHandler.h"
@implementation RCTDataRequestHandler
{
NSOperationQueue *_queue;
}
RCT_EXPORT_MODULE()
- (void)invalidate
{
[_queue cancelAllOperations];
_queue = nil;
}
- (BOOL)canHandleRequest:(NSURLRequest *)request
{
return [request.URL.scheme caseInsensitiveCompare:@"data"] == NSOrderedSame;
}
- (NSOperation *)sendRequest:(NSURLRequest *)request
withDelegate:(id<RCTURLRequestDelegate>)delegate
{
// Lazy setup
if (!_queue) {
_queue = [NSOperationQueue new];
_queue.maxConcurrentOperationCount = 2;
}
__weak __block NSBlockOperation *weakOp;
__block NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// Get mime type
NSRange firstSemicolon = [request.URL.resourceSpecifier rangeOfString:@";"];
NSString *mimeType = firstSemicolon.length ? [request.URL.resourceSpecifier substringToIndex:firstSemicolon.location] : nil;
// Send response
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL
MIMEType:mimeType
expectedContentLength:-1
textEncodingName:nil];
[delegate URLRequest:weakOp didReceiveResponse:response];
// Load data
NSError *error;
NSData *data = [NSData dataWithContentsOfURL:request.URL
options:NSDataReadingMappedIfSafe
error:&error];
if (data) {
[delegate URLRequest:weakOp didReceiveData:data];
}
[delegate URLRequest:weakOp didCompleteWithError:error];
}];
weakOp = op;
[_queue addOperation:op];
return op;
}
- (void)cancelRequest:(NSOperation *)op
{
[op cancel];
}
@end

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTInvalidating.h>
#import <React/RCTURLRequestHandler.h>
/**
* This is the default RCTURLRequestHandler implementation for file requests.
*/
@interface RCTFileRequestHandler : NSObject <RCTURLRequestHandler, RCTInvalidating>
@end

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTFileRequestHandler.h"
#import <MobileCoreServices/MobileCoreServices.h>
#import <React/RCTUtils.h>
@implementation RCTFileRequestHandler
{
NSOperationQueue *_fileQueue;
}
RCT_EXPORT_MODULE()
- (void)invalidate
{
[_fileQueue cancelAllOperations];
_fileQueue = nil;
}
- (BOOL)canHandleRequest:(NSURLRequest *)request
{
return
[request.URL.scheme caseInsensitiveCompare:@"file"] == NSOrderedSame
&& !RCTIsBundleAssetURL(request.URL);
}
- (NSOperation *)sendRequest:(NSURLRequest *)request
withDelegate:(id<RCTURLRequestDelegate>)delegate
{
// Lazy setup
if (!_fileQueue) {
_fileQueue = [NSOperationQueue new];
_fileQueue.maxConcurrentOperationCount = 4;
}
__weak __block NSBlockOperation *weakOp;
__block NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// Get content length
NSError *error = nil;
NSFileManager *fileManager = [NSFileManager new];
NSDictionary<NSString *, id> *fileAttributes = [fileManager attributesOfItemAtPath:request.URL.path error:&error];
if (!fileAttributes) {
[delegate URLRequest:weakOp didCompleteWithError:error];
return;
}
// Get mime type
NSString *fileExtension = [request.URL pathExtension];
NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(
kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtension, NULL);
NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass(
(__bridge CFStringRef)UTI, kUTTagClassMIMEType);
// Send response
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL
MIMEType:contentType
expectedContentLength:[fileAttributes[NSFileSize] ?: @-1 integerValue]
textEncodingName:nil];
[delegate URLRequest:weakOp didReceiveResponse:response];
// Load data
NSData *data = [NSData dataWithContentsOfURL:request.URL
options:NSDataReadingMappedIfSafe
error:&error];
if (data) {
[delegate URLRequest:weakOp didReceiveData:data];
}
[delegate URLRequest:weakOp didCompleteWithError:error];
}];
weakOp = op;
[_fileQueue addOperation:op];
return op;
}
- (void)cancelRequest:(NSOperation *)op
{
[op cancel];
}
@end

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTInvalidating.h>
#import <React/RCTURLRequestHandler.h>
/**
* This is the default RCTURLRequestHandler implementation for HTTP requests.
*/
@interface RCTHTTPRequestHandler : NSObject <RCTURLRequestHandler, RCTInvalidating>
@end

View File

@@ -0,0 +1,148 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTHTTPRequestHandler.h"
#import <mutex>
#import "RCTNetworking.h"
@interface RCTHTTPRequestHandler () <NSURLSessionDataDelegate>
@end
@implementation RCTHTTPRequestHandler
{
NSMapTable *_delegates;
NSURLSession *_session;
std::mutex _mutex;
}
@synthesize bridge = _bridge;
RCT_EXPORT_MODULE()
- (void)invalidate
{
[_session invalidateAndCancel];
_session = nil;
}
- (BOOL)isValid
{
// if session == nil and delegates != nil, we've been invalidated
return _session || !_delegates;
}
#pragma mark - NSURLRequestHandler
- (BOOL)canHandleRequest:(NSURLRequest *)request
{
static NSSet<NSString *> *schemes = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// technically, RCTHTTPRequestHandler can handle file:// as well,
// but it's less efficient than using RCTFileRequestHandler
schemes = [[NSSet alloc] initWithObjects:@"http", @"https", nil];
});
return [schemes containsObject:request.URL.scheme.lowercaseString];
}
- (NSURLSessionDataTask *)sendRequest:(NSURLRequest *)request
withDelegate:(id<RCTURLRequestDelegate>)delegate
{
// Lazy setup
if (!_session && [self isValid]) {
NSOperationQueue *callbackQueue = [NSOperationQueue new];
callbackQueue.maxConcurrentOperationCount = 1;
callbackQueue.underlyingQueue = [[_bridge networking] methodQueue];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
[configuration setHTTPShouldSetCookies:YES];
[configuration setHTTPCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
[configuration setHTTPCookieStorage:[NSHTTPCookieStorage sharedHTTPCookieStorage]];
_session = [NSURLSession sessionWithConfiguration:configuration
delegate:self
delegateQueue:callbackQueue];
std::lock_guard<std::mutex> lock(_mutex);
_delegates = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory
valueOptions:NSPointerFunctionsStrongMemory
capacity:0];
}
NSURLSessionDataTask *task = [_session dataTaskWithRequest:request];
{
std::lock_guard<std::mutex> lock(_mutex);
[_delegates setObject:delegate forKey:task];
}
[task resume];
return task;
}
- (void)cancelRequest:(NSURLSessionDataTask *)task
{
{
std::lock_guard<std::mutex> lock(_mutex);
[_delegates removeObjectForKey:task];
}
[task cancel];
}
#pragma mark - NSURLSession delegate
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
id<RCTURLRequestDelegate> delegate;
{
std::lock_guard<std::mutex> lock(_mutex);
delegate = [_delegates objectForKey:task];
}
[delegate URLRequest:task didSendDataWithProgress:totalBytesSent];
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)task
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
id<RCTURLRequestDelegate> delegate;
{
std::lock_guard<std::mutex> lock(_mutex);
delegate = [_delegates objectForKey:task];
}
[delegate URLRequest:task didReceiveResponse:response];
completionHandler(NSURLSessionResponseAllow);
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)task
didReceiveData:(NSData *)data
{
id<RCTURLRequestDelegate> delegate;
{
std::lock_guard<std::mutex> lock(_mutex);
delegate = [_delegates objectForKey:task];
}
[delegate URLRequest:task didReceiveData:data];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
id<RCTURLRequestDelegate> delegate;
{
std::lock_guard<std::mutex> lock(_mutex);
delegate = [_delegates objectForKey:task];
[_delegates removeObjectForKey:task];
}
[delegate URLRequest:task didCompleteWithError:error];
}
@end

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <SystemConfiguration/SystemConfiguration.h>
#import <React/RCTEventEmitter.h>
@interface RCTNetInfo : RCTEventEmitter
- (instancetype)initWithHost:(NSString *)host;
@end

View File

@@ -0,0 +1,174 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTNetInfo.h"
#if !TARGET_OS_TV
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#endif
#import <React/RCTAssert.h>
#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>
// Based on the ConnectionType enum described in the W3C Network Information API spec
// (https://wicg.github.io/netinfo/).
static NSString *const RCTConnectionTypeUnknown = @"unknown";
static NSString *const RCTConnectionTypeNone = @"none";
static NSString *const RCTConnectionTypeWifi = @"wifi";
static NSString *const RCTConnectionTypeCellular = @"cellular";
// Based on the EffectiveConnectionType enum described in the W3C Network Information API spec
// (https://wicg.github.io/netinfo/).
static NSString *const RCTEffectiveConnectionTypeUnknown = @"unknown";
static NSString *const RCTEffectiveConnectionType2g = @"2g";
static NSString *const RCTEffectiveConnectionType3g = @"3g";
static NSString *const RCTEffectiveConnectionType4g = @"4g";
// The RCTReachabilityState* values are deprecated.
static NSString *const RCTReachabilityStateUnknown = @"unknown";
static NSString *const RCTReachabilityStateNone = @"none";
static NSString *const RCTReachabilityStateWifi = @"wifi";
static NSString *const RCTReachabilityStateCell = @"cell";
@implementation RCTNetInfo
{
SCNetworkReachabilityRef _reachability;
NSString *_connectionType;
NSString *_effectiveConnectionType;
NSString *_statusDeprecated;
NSString *_host;
BOOL _isObserving;
}
RCT_EXPORT_MODULE()
static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
{
RCTNetInfo *self = (__bridge id)info;
if ([self setReachabilityStatus:flags] && self->_isObserving) {
[self sendEventWithName:@"networkStatusDidChange" body:@{@"connectionType": self->_connectionType,
@"effectiveConnectionType": self->_effectiveConnectionType,
@"network_info": self->_statusDeprecated}];
}
}
#pragma mark - Lifecycle
- (instancetype)initWithHost:(NSString *)host
{
RCTAssertParam(host);
RCTAssert(![host hasPrefix:@"http"], @"Host value should just contain the domain, not the URL scheme.");
if ((self = [self init])) {
_host = [host copy];
}
return self;
}
- (NSArray<NSString *> *)supportedEvents
{
return @[@"networkStatusDidChange"];
}
- (void)startObserving
{
_isObserving = YES;
_connectionType = RCTConnectionTypeUnknown;
_effectiveConnectionType = RCTEffectiveConnectionTypeUnknown;
_statusDeprecated = RCTReachabilityStateUnknown;
_reachability = [self getReachabilityRef];
}
- (void)stopObserving
{
_isObserving = NO;
if (_reachability) {
SCNetworkReachabilityUnscheduleFromRunLoop(_reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
CFRelease(_reachability);
}
}
- (SCNetworkReachabilityRef)getReachabilityRef
{
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, _host.UTF8String ?: "apple.com");
SCNetworkReachabilityContext context = { 0, ( __bridge void *)self, NULL, NULL, NULL };
SCNetworkReachabilitySetCallback(reachability, RCTReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
return reachability;
}
- (BOOL)setReachabilityStatus:(SCNetworkReachabilityFlags)flags
{
NSString *connectionType = RCTConnectionTypeUnknown;
NSString *effectiveConnectionType = RCTEffectiveConnectionTypeUnknown;
NSString *status = RCTReachabilityStateUnknown;
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0 ||
(flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0) {
connectionType = RCTConnectionTypeNone;
status = RCTReachabilityStateNone;
}
#if !TARGET_OS_TV
else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
connectionType = RCTConnectionTypeCellular;
status = RCTReachabilityStateCell;
CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init];
if (netinfo) {
if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMA1x]) {
effectiveConnectionType = RCTEffectiveConnectionType2g;
} else if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyWCDMA] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSDPA] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSUPA] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyeHRPD]) {
effectiveConnectionType = RCTEffectiveConnectionType3g;
} else if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {
effectiveConnectionType = RCTEffectiveConnectionType4g;
}
}
}
#endif
else {
connectionType = RCTConnectionTypeWifi;
status = RCTReachabilityStateWifi;
}
if (![connectionType isEqualToString:self->_connectionType] ||
![effectiveConnectionType isEqualToString:self->_effectiveConnectionType] ||
![status isEqualToString:self->_statusDeprecated]) {
self->_connectionType = connectionType;
self->_effectiveConnectionType = effectiveConnectionType;
self->_statusDeprecated = status;
return YES;
}
return NO;
}
#pragma mark - Public API
RCT_EXPORT_METHOD(getCurrentConnectivity:(RCTPromiseResolveBlock)resolve
reject:(__unused RCTPromiseRejectBlock)reject)
{
SCNetworkReachabilityRef reachability = [self getReachabilityRef];
SCNetworkReachabilityUnscheduleFromRunLoop(reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
CFRelease(reachability);
resolve(@{@"connectionType": _connectionType ?: RCTConnectionTypeUnknown,
@"effectiveConnectionType": _effectiveConnectionType ?: RCTEffectiveConnectionTypeUnknown,
@"network_info": _statusDeprecated ?: RCTReachabilityStateUnknown});
}
@end

View File

@@ -0,0 +1,357 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
134E969A1BCEB7F800AFFDA1 /* RCTDataRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 134E96991BCEB7F800AFFDA1 /* RCTDataRequestHandler.m */; };
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */; };
13D6D66A1B5FCF8200883BE9 /* RCTNetworkTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 13D6D6691B5FCF8200883BE9 /* RCTNetworkTask.m */; };
13EF800E1BCBE015003F47DD /* RCTFileRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */; };
2D3B5F261D9B0EAB00451313 /* RCTNetworkTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 13D6D6691B5FCF8200883BE9 /* RCTNetworkTask.m */; };
2D3B5F271D9B0EB400451313 /* RCTHTTPRequestHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.mm */; };
2D3B5F281D9B0EB400451313 /* RCTFileRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */; };
2D3B5F291D9B0EB400451313 /* RCTDataRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 134E96991BCEB7F800AFFDA1 /* RCTDataRequestHandler.m */; };
2D3B5F2A1D9B0EB400451313 /* RCTNetInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */; };
2D3B5F2B1D9B0EB400451313 /* RCTNetworking.mm in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTNetworking.mm */; };
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.mm */; };
58B512081A9E6CE300147676 /* RCTNetworking.mm in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTNetworking.mm */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
134E96981BCEB7F800AFFDA1 /* RCTDataRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDataRequestHandler.h; sourceTree = "<group>"; };
134E96991BCEB7F800AFFDA1 /* RCTDataRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDataRequestHandler.m; sourceTree = "<group>"; };
1372B7351AB03E7B00659ED6 /* RCTNetInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTNetInfo.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RCTNetInfo.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
13D6D6681B5FCF8200883BE9 /* RCTNetworkTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTNetworkTask.h; sourceTree = "<group>"; };
13D6D6691B5FCF8200883BE9 /* RCTNetworkTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTNetworkTask.m; sourceTree = "<group>"; };
13EF800C1BCBE015003F47DD /* RCTFileRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFileRequestHandler.h; sourceTree = "<group>"; };
13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFileRequestHandler.m; sourceTree = "<group>"; };
2D2A28541D9B044C00D4039D /* libRCTNetwork-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTNetwork-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTHTTPRequestHandler.h; sourceTree = "<group>"; };
352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTHTTPRequestHandler.mm; sourceTree = "<group>"; };
3D5FA63F1DE4B4790058FD77 /* RCTNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTNetworking.h; sourceTree = "<group>"; };
58B511DB1A9E6C8500147676 /* libRCTNetwork.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTNetwork.a; sourceTree = BUILT_PRODUCTS_DIR; };
58B512071A9E6CE300147676 /* RCTNetworking.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTNetworking.mm; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXGroup section */
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
13D6D6681B5FCF8200883BE9 /* RCTNetworkTask.h */,
13D6D6691B5FCF8200883BE9 /* RCTNetworkTask.m */,
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */,
352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.mm */,
13EF800C1BCBE015003F47DD /* RCTFileRequestHandler.h */,
13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */,
134E96981BCEB7F800AFFDA1 /* RCTDataRequestHandler.h */,
134E96991BCEB7F800AFFDA1 /* RCTDataRequestHandler.m */,
1372B7351AB03E7B00659ED6 /* RCTNetInfo.h */,
1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */,
3D5FA63F1DE4B4790058FD77 /* RCTNetworking.h */,
58B512071A9E6CE300147676 /* RCTNetworking.mm */,
58B511DC1A9E6C8500147676 /* Products */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
};
58B511DC1A9E6C8500147676 /* Products */ = {
isa = PBXGroup;
children = (
58B511DB1A9E6C8500147676 /* libRCTNetwork.a */,
2D2A28541D9B044C00D4039D /* libRCTNetwork-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
2D2A28531D9B044C00D4039D /* RCTNetwork-tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2D2A285C1D9B044C00D4039D /* Build configuration list for PBXNativeTarget "RCTNetwork-tvOS" */;
buildPhases = (
2D2A28501D9B044C00D4039D /* Sources */,
);
buildRules = (
);
dependencies = (
);
name = "RCTNetwork-tvOS";
productName = "RCTNetwork-tvOS";
productReference = 2D2A28541D9B044C00D4039D /* libRCTNetwork-tvOS.a */;
productType = "com.apple.product-type.library.static";
};
58B511DA1A9E6C8500147676 /* RCTNetwork */ = {
isa = PBXNativeTarget;
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTNetwork" */;
buildPhases = (
58B511D71A9E6C8500147676 /* Sources */,
);
buildRules = (
);
dependencies = (
);
name = RCTNetwork;
productName = RCTDataManager;
productReference = 58B511DB1A9E6C8500147676 /* libRCTNetwork.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
58B511D31A9E6C8500147676 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0810;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
2D2A28531D9B044C00D4039D = {
CreatedOnToolsVersion = 8.0;
ProvisioningStyle = Automatic;
};
58B511DA1A9E6C8500147676 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTNetwork" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 58B511D21A9E6C8500147676;
productRefGroup = 58B511DC1A9E6C8500147676 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
58B511DA1A9E6C8500147676 /* RCTNetwork */,
2D2A28531D9B044C00D4039D /* RCTNetwork-tvOS */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
2D2A28501D9B044C00D4039D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2D3B5F2A1D9B0EB400451313 /* RCTNetInfo.m in Sources */,
2D3B5F261D9B0EAB00451313 /* RCTNetworkTask.m in Sources */,
2D3B5F281D9B0EB400451313 /* RCTFileRequestHandler.m in Sources */,
2D3B5F271D9B0EB400451313 /* RCTHTTPRequestHandler.mm in Sources */,
2D3B5F2B1D9B0EB400451313 /* RCTNetworking.mm in Sources */,
2D3B5F291D9B0EB400451313 /* RCTDataRequestHandler.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
58B511D71A9E6C8500147676 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13D6D66A1B5FCF8200883BE9 /* RCTNetworkTask.m in Sources */,
13EF800E1BCBE015003F47DD /* RCTFileRequestHandler.m in Sources */,
134E969A1BCEB7F800AFFDA1 /* RCTDataRequestHandler.m in Sources */,
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */,
58B512081A9E6CE300147676 /* RCTNetworking.mm in Sources */,
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
2D2A285A1D9B044C00D4039D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
GCC_NO_COMMON_BLOCKS = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TVOS_DEPLOYMENT_TARGET = 9.2;
};
name = Debug;
};
2D2A285B1D9B044C00D4039D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_NO_COMMON_BLOCKS = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
TVOS_DEPLOYMENT_TARGET = 9.2;
};
name = Release;
};
58B511ED1A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
WARNING_CFLAGS = (
"-Werror",
"-Wall",
);
};
name = Debug;
};
58B511EE1A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
VALIDATE_PRODUCT = YES;
WARNING_CFLAGS = (
"-Werror",
"-Wall",
);
};
name = Release;
};
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_STATIC_ANALYZER_MODE = deep;
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RCTNetwork;
RUN_CLANG_STATIC_ANALYZER = YES;
};
name = Debug;
};
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_STATIC_ANALYZER_MODE = deep;
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RCTNetwork;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
2D2A285C1D9B044C00D4039D /* Build configuration list for PBXNativeTarget "RCTNetwork-tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2D2A285A1D9B044C00D4039D /* Debug */,
2D2A285B1D9B044C00D4039D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTNetwork" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511ED1A9E6C8500147676 /* Debug */,
58B511EE1A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTNetwork" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511F01A9E6C8500147676 /* Debug */,
58B511F11A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import <React/RCTURLRequestDelegate.h>
#import <React/RCTURLRequestHandler.h>
typedef void (^RCTURLRequestCompletionBlock)(NSURLResponse *response, NSData *data, NSError *error);
typedef void (^RCTURLRequestCancellationBlock)(void);
typedef void (^RCTURLRequestIncrementalDataBlock)(NSData *data, int64_t progress, int64_t total);
typedef void (^RCTURLRequestProgressBlock)(int64_t progress, int64_t total);
typedef void (^RCTURLRequestResponseBlock)(NSURLResponse *response);
typedef NS_ENUM(NSInteger, RCTNetworkTaskStatus) {
RCTNetworkTaskPending = 0,
RCTNetworkTaskInProgress,
RCTNetworkTaskFinished,
};
@interface RCTNetworkTask : NSObject <RCTURLRequestDelegate>
@property (nonatomic, readonly) NSURLRequest *request;
@property (nonatomic, readonly) NSNumber *requestID;
@property (nonatomic, readonly, weak) id requestToken;
@property (nonatomic, readonly) NSURLResponse *response;
@property (nonatomic, copy) RCTURLRequestCompletionBlock completionBlock;
@property (nonatomic, copy) RCTURLRequestProgressBlock downloadProgressBlock;
@property (nonatomic, copy) RCTURLRequestIncrementalDataBlock incrementalDataBlock;
@property (nonatomic, copy) RCTURLRequestResponseBlock responseBlock;
@property (nonatomic, copy) RCTURLRequestProgressBlock uploadProgressBlock;
@property (nonatomic, readonly) RCTNetworkTaskStatus status;
- (instancetype)initWithRequest:(NSURLRequest *)request
handler:(id<RCTURLRequestHandler>)handler
callbackQueue:(dispatch_queue_t)callbackQueue NS_DESIGNATED_INITIALIZER;
- (void)start;
- (void)cancel;
@end

View File

@@ -0,0 +1,200 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTLog.h>
#import <React/RCTNetworkTask.h>
#import <React/RCTUtils.h>
@implementation RCTNetworkTask
{
NSMutableData *_data;
id<RCTURLRequestHandler> _handler;
dispatch_queue_t _callbackQueue;
RCTNetworkTask *_selfReference;
}
- (instancetype)initWithRequest:(NSURLRequest *)request
handler:(id<RCTURLRequestHandler>)handler
callbackQueue:(dispatch_queue_t)callbackQueue
{
RCTAssertParam(request);
RCTAssertParam(handler);
RCTAssertParam(callbackQueue);
static NSUInteger requestID = 0;
if ((self = [super init])) {
_requestID = @(requestID++);
_request = request;
_handler = handler;
_callbackQueue = callbackQueue;
_status = RCTNetworkTaskPending;
dispatch_queue_set_specific(callbackQueue, (__bridge void *)self, (__bridge void *)self, NULL);
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (void)invalidate
{
_selfReference = nil;
_completionBlock = nil;
_downloadProgressBlock = nil;
_incrementalDataBlock = nil;
_responseBlock = nil;
_uploadProgressBlock = nil;
_requestToken = nil;
}
- (void)dispatchCallback:(dispatch_block_t)callback
{
if (dispatch_get_specific((__bridge void *)self) == (__bridge void *)self) {
callback();
} else {
dispatch_async(_callbackQueue, callback);
}
}
- (void)start
{
if (_status != RCTNetworkTaskPending) {
RCTLogError(@"RCTNetworkTask was already started or completed");
return;
}
if (_requestToken == nil) {
id token = [_handler sendRequest:_request withDelegate:self];
if ([self validateRequestToken:token]) {
_selfReference = self;
_status = RCTNetworkTaskInProgress;
}
}
}
- (void)cancel
{
if (_status == RCTNetworkTaskFinished) {
return;
}
_status = RCTNetworkTaskFinished;
id token = _requestToken;
if (token && [_handler respondsToSelector:@selector(cancelRequest:)]) {
[_handler cancelRequest:token];
}
[self invalidate];
}
- (BOOL)validateRequestToken:(id)requestToken
{
BOOL valid = YES;
if (_requestToken == nil) {
if (requestToken == nil) {
if (RCT_DEBUG) {
RCTLogError(@"Missing request token for request: %@", _request);
}
valid = NO;
}
_requestToken = requestToken;
} else if (![requestToken isEqual:_requestToken]) {
if (RCT_DEBUG) {
RCTLogError(@"Unrecognized request token: %@ expected: %@", requestToken, _requestToken);
}
valid = NO;
}
if (!valid) {
_status = RCTNetworkTaskFinished;
if (_completionBlock) {
RCTURLRequestCompletionBlock completionBlock = _completionBlock;
[self dispatchCallback:^{
completionBlock(self->_response, nil, RCTErrorWithMessage(@"Invalid request token."));
}];
}
[self invalidate];
}
return valid;
}
- (void)URLRequest:(id)requestToken didSendDataWithProgress:(int64_t)bytesSent
{
if (![self validateRequestToken:requestToken]) {
return;
}
if (_uploadProgressBlock) {
RCTURLRequestProgressBlock uploadProgressBlock = _uploadProgressBlock;
int64_t length = _request.HTTPBody.length;
[self dispatchCallback:^{
uploadProgressBlock(bytesSent, length);
}];
}
}
- (void)URLRequest:(id)requestToken didReceiveResponse:(NSURLResponse *)response
{
if (![self validateRequestToken:requestToken]) {
return;
}
_response = response;
if (_responseBlock) {
RCTURLRequestResponseBlock responseBlock = _responseBlock;
[self dispatchCallback:^{
responseBlock(response);
}];
}
}
- (void)URLRequest:(id)requestToken didReceiveData:(NSData *)data
{
if (![self validateRequestToken:requestToken]) {
return;
}
if (!_data) {
_data = [NSMutableData new];
}
[_data appendData:data];
int64_t length = _data.length;
int64_t total = _response.expectedContentLength;
if (_incrementalDataBlock) {
RCTURLRequestIncrementalDataBlock incrementalDataBlock = _incrementalDataBlock;
[self dispatchCallback:^{
incrementalDataBlock(data, length, total);
}];
}
if (_downloadProgressBlock && total > 0) {
RCTURLRequestProgressBlock downloadProgressBlock = _downloadProgressBlock;
[self dispatchCallback:^{
downloadProgressBlock(length, total);
}];
}
}
- (void)URLRequest:(id)requestToken didCompleteWithError:(NSError *)error
{
if (![self validateRequestToken:requestToken]) {
return;
}
_status = RCTNetworkTaskFinished;
if (_completionBlock) {
RCTURLRequestCompletionBlock completionBlock = _completionBlock;
[self dispatchCallback:^{
completionBlock(self->_response, self->_data, error);
}];
}
[self invalidate];
}
@end

View File

@@ -0,0 +1,120 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule RCTNetworking
* @flow
*/
'use strict';
// Do not require the native RCTNetworking module directly! Use this wrapper module instead.
// It will add the necessary requestId, so that you don't have to generate it yourself.
const MissingNativeEventEmitterShim = require('MissingNativeEventEmitterShim');
const NativeEventEmitter = require('NativeEventEmitter');
const RCTNetworkingNative = require('NativeModules').Networking;
const convertRequestBody = require('convertRequestBody');
import type {RequestBody} from 'convertRequestBody';
type Header = [string, string];
// Convert FormData headers to arrays, which are easier to consume in
// native on Android.
function convertHeadersMapToArray(headers: Object): Array<Header> {
const headerArray = [];
for (const name in headers) {
headerArray.push([name, headers[name]]);
}
return headerArray;
}
let _requestId = 1;
function generateRequestId(): number {
return _requestId++;
}
/**
* This class is a wrapper around the native RCTNetworking module. It adds a necessary unique
* requestId to each network request that can be used to abort that request later on.
*/
class RCTNetworking extends NativeEventEmitter {
isAvailable: boolean = true;
constructor() {
super(RCTNetworkingNative);
}
sendRequest(
method: string,
trackingName: string,
url: string,
headers: Object,
data: RequestBody,
responseType: 'text' | 'base64',
incrementalUpdates: boolean,
timeout: number,
callback: (requestId: number) => any,
withCredentials: boolean
) {
const body = convertRequestBody(data);
if (body && body.formData) {
body.formData = body.formData.map((part) => ({
...part,
headers: convertHeadersMapToArray(part.headers),
}));
}
const requestId = generateRequestId();
RCTNetworkingNative.sendRequest(
method,
url,
requestId,
convertHeadersMapToArray(headers),
{...body, trackingName},
responseType,
incrementalUpdates,
timeout,
withCredentials
);
callback(requestId);
}
abortRequest(requestId: number) {
RCTNetworkingNative.abortRequest(requestId);
}
clearCookies(callback: (result: boolean) => any) {
RCTNetworkingNative.clearCookies(callback);
}
}
if (__DEV__ && !RCTNetworkingNative) {
class MissingNativeRCTNetworkingShim extends MissingNativeEventEmitterShim {
constructor() {
super('RCTNetworking', 'Networking');
}
sendRequest(...args: Array<any>) {
this.throwMissingNativeModule();
}
abortRequest(...args: Array<any>) {
this.throwMissingNativeModule();
}
clearCookies(...args: Array<any>) {
this.throwMissingNativeModule();
}
}
// This module depends on the native `RCTNetworkingNative` module. If you don't include it,
// `RCTNetworking.isAvailable` will return `false`, and any method calls will throw.
// We reassign the class variable to keep the autodoc generator happy.
RCTNetworking = new MissingNativeRCTNetworkingShim();
} else {
RCTNetworking = new RCTNetworking();
}
module.exports = RCTNetworking;

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTEventEmitter.h>
#import <React/RCTNetworkTask.h>
@protocol RCTNetworkingRequestHandler <NSObject>
// @lint-ignore FBOBJCUNTYPEDCOLLECTION1
- (BOOL)canHandleNetworkingRequest:(NSDictionary *)data;
// @lint-ignore FBOBJCUNTYPEDCOLLECTION1
- (NSDictionary *)handleNetworkingRequest:(NSDictionary *)data;
@end
@protocol RCTNetworkingResponseHandler <NSObject>
- (BOOL)canHandleNetworkingResponse:(NSString *)responseType;
- (id)handleNetworkingResponse:(NSURLResponse *)response data:(NSData *)data;
@end
@interface RCTNetworking : RCTEventEmitter
/**
* Does a handler exist for the specified request?
*/
- (BOOL)canHandleRequest:(NSURLRequest *)request;
/**
* Return an RCTNetworkTask for the specified request. This is useful for
* invoking the React Native networking stack from within native code.
*/
- (RCTNetworkTask *)networkTaskWithRequest:(NSURLRequest *)request
completionBlock:(RCTURLRequestCompletionBlock)completionBlock;
- (void)addRequestHandler:(id<RCTNetworkingRequestHandler>)handler;
- (void)addResponseHandler:(id<RCTNetworkingResponseHandler>)handler;
- (void)removeRequestHandler:(id<RCTNetworkingRequestHandler>)handler;
- (void)removeResponseHandler:(id<RCTNetworkingResponseHandler>)handler;
@end
@interface RCTBridge (RCTNetworking)
@property (nonatomic, readonly) RCTNetworking *networking;
@end

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule RCTNetworking
* @flow
*/
'use strict';
const MissingNativeEventEmitterShim = require('MissingNativeEventEmitterShim');
const NativeEventEmitter = require('NativeEventEmitter');
const RCTNetworkingNative = require('NativeModules').Networking;
const convertRequestBody = require('convertRequestBody');
import type {RequestBody} from 'convertRequestBody';
import type { NativeResponseType } from './XMLHttpRequest';
class RCTNetworking extends NativeEventEmitter {
isAvailable: boolean = true;
constructor() {
super(RCTNetworkingNative);
}
sendRequest(
method: string,
trackingName: string,
url: string,
headers: Object,
data: RequestBody,
responseType: NativeResponseType,
incrementalUpdates: boolean,
timeout: number,
callback: (requestId: number) => any,
withCredentials: boolean
) {
const body = convertRequestBody(data);
RCTNetworkingNative.sendRequest({
method,
url,
data: {...body, trackingName},
headers,
responseType,
incrementalUpdates,
timeout,
withCredentials
}, callback);
}
abortRequest(requestId: number) {
RCTNetworkingNative.abortRequest(requestId);
}
clearCookies(callback: (result: boolean) => any) {
RCTNetworkingNative.clearCookies(callback);
}
}
if (__DEV__ && !RCTNetworkingNative) {
class MissingNativeRCTNetworkingShim extends MissingNativeEventEmitterShim {
constructor() {
super('RCTNetworking', 'Networking');
}
sendRequest(...args: Array<any>) {
this.throwMissingNativeModule();
}
abortRequest(...args: Array<any>) {
this.throwMissingNativeModule();
}
clearCookies(...args: Array<any>) {
this.throwMissingNativeModule();
}
}
// This module depends on the native `RCTNetworkingNative` module. If you don't include it,
// `RCTNetworking.isAvailable` will return `false`, and any method calls will throw.
// We reassign the class variable to keep the autodoc generator happy.
RCTNetworking = new MissingNativeRCTNetworkingShim();
} else {
RCTNetworking = new RCTNetworking();
}
module.exports = RCTNetworking;

View File

@@ -0,0 +1,673 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <mutex>
#import <React/RCTAssert.h>
#import <React/RCTConvert.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTLog.h>
#import <React/RCTNetworkTask.h>
#import <React/RCTNetworking.h>
#import <React/RCTURLRequestHandler.h>
#import <React/RCTUtils.h>
#import "RCTHTTPRequestHandler.h"
typedef RCTURLRequestCancellationBlock (^RCTHTTPQueryResult)(NSError *error, NSDictionary<NSString *, id> *result);
@interface RCTNetworking ()
- (RCTURLRequestCancellationBlock)processDataForHTTPQuery:(NSDictionary<NSString *, id> *)data
callback:(RCTHTTPQueryResult)callback;
@end
/**
* Helper to convert FormData payloads into multipart/formdata requests.
*/
@interface RCTHTTPFormDataHelper : NSObject
@property (nonatomic, weak) RCTNetworking *networker;
@end
@implementation RCTHTTPFormDataHelper
{
NSMutableArray<NSDictionary<NSString *, id> *> *_parts;
NSMutableData *_multipartBody;
RCTHTTPQueryResult _callback;
NSString *_boundary;
}
static NSString *RCTGenerateFormBoundary()
{
const size_t boundaryLength = 70;
const char *boundaryChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.";
char *bytes = (char*)malloc(boundaryLength);
size_t charCount = strlen(boundaryChars);
for (int i = 0; i < boundaryLength; i++) {
bytes[i] = boundaryChars[arc4random_uniform((u_int32_t)charCount)];
}
return [[NSString alloc] initWithBytesNoCopy:bytes length:boundaryLength encoding:NSUTF8StringEncoding freeWhenDone:YES];
}
- (RCTURLRequestCancellationBlock)process:(NSArray<NSDictionary *> *)formData
callback:(RCTHTTPQueryResult)callback
{
RCTAssertThread(_networker.methodQueue, @"process: must be called on method queue");
if (formData.count == 0) {
return callback(nil, nil);
}
_parts = [formData mutableCopy];
_callback = callback;
_multipartBody = [NSMutableData new];
_boundary = RCTGenerateFormBoundary();
return [_networker processDataForHTTPQuery:_parts[0] callback:^(NSError *error, NSDictionary<NSString *, id> *result) {
return [self handleResult:result error:error];
}];
}
- (RCTURLRequestCancellationBlock)handleResult:(NSDictionary<NSString *, id> *)result
error:(NSError *)error
{
RCTAssertThread(_networker.methodQueue, @"handleResult: must be called on method queue");
if (error) {
return _callback(error, nil);
}
// Start with boundary.
[_multipartBody appendData:[[NSString stringWithFormat:@"--%@\r\n", _boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
// Print headers.
NSMutableDictionary<NSString *, NSString *> *headers = [_parts[0][@"headers"] mutableCopy];
NSString *partContentType = result[@"contentType"];
if (partContentType != nil) {
headers[@"content-type"] = partContentType;
}
[headers enumerateKeysAndObjectsUsingBlock:^(NSString *parameterKey, NSString *parameterValue, BOOL *stop) {
[self->_multipartBody appendData:[[NSString stringWithFormat:@"%@: %@\r\n", parameterKey, parameterValue]
dataUsingEncoding:NSUTF8StringEncoding]];
}];
// Add the body.
[_multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[_multipartBody appendData:result[@"body"]];
[_multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[_parts removeObjectAtIndex:0];
if (_parts.count) {
return [_networker processDataForHTTPQuery:_parts[0] callback:^(NSError *err, NSDictionary<NSString *, id> *res) {
return [self handleResult:res error:err];
}];
}
// We've processed the last item. Finish and return.
[_multipartBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", _boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", _boundary];
return _callback(nil, @{@"body": _multipartBody, @"contentType": contentType});
}
@end
/**
* Bridge module that provides the JS interface to the network stack.
*/
@implementation RCTNetworking
{
NSMutableDictionary<NSNumber *, RCTNetworkTask *> *_tasksByRequestID;
std::mutex _handlersLock;
NSArray<id<RCTURLRequestHandler>> *_handlers;
NSMutableArray<id<RCTNetworkingRequestHandler>> *_requestHandlers;
NSMutableArray<id<RCTNetworkingResponseHandler>> *_responseHandlers;
}
@synthesize methodQueue = _methodQueue;
RCT_EXPORT_MODULE()
- (void)invalidate
{
_requestHandlers = nil;
_responseHandlers = nil;
}
- (NSArray<NSString *> *)supportedEvents
{
return @[@"didCompleteNetworkResponse",
@"didReceiveNetworkResponse",
@"didSendNetworkData",
@"didReceiveNetworkIncrementalData",
@"didReceiveNetworkDataProgress",
@"didReceiveNetworkData"];
}
- (id<RCTURLRequestHandler>)handlerForRequest:(NSURLRequest *)request
{
if (!request.URL) {
return nil;
}
{
std::lock_guard<std::mutex> lock(_handlersLock);
if (!_handlers) {
// Get handlers, sorted in reverse priority order (highest priority first)
_handlers = [[self.bridge modulesConformingToProtocol:@protocol(RCTURLRequestHandler)] sortedArrayUsingComparator:^NSComparisonResult(id<RCTURLRequestHandler> a, id<RCTURLRequestHandler> b) {
float priorityA = [a respondsToSelector:@selector(handlerPriority)] ? [a handlerPriority] : 0;
float priorityB = [b respondsToSelector:@selector(handlerPriority)] ? [b handlerPriority] : 0;
if (priorityA > priorityB) {
return NSOrderedAscending;
} else if (priorityA < priorityB) {
return NSOrderedDescending;
} else {
return NSOrderedSame;
}
}];
}
}
if (RCT_DEBUG) {
// Check for handler conflicts
float previousPriority = 0;
id<RCTURLRequestHandler> previousHandler = nil;
for (id<RCTURLRequestHandler> handler in _handlers) {
float priority = [handler respondsToSelector:@selector(handlerPriority)] ? [handler handlerPriority] : 0;
if (previousHandler && priority < previousPriority) {
return previousHandler;
}
if ([handler canHandleRequest:request]) {
if (previousHandler) {
if (priority == previousPriority) {
RCTLogError(@"The RCTURLRequestHandlers %@ and %@ both reported that"
" they can handle the request %@, and have equal priority"
" (%g). This could result in non-deterministic behavior.",
handler, previousHandler, request, priority);
}
} else {
previousHandler = handler;
previousPriority = priority;
}
}
}
return previousHandler;
}
// Normal code path
for (id<RCTURLRequestHandler> handler in _handlers) {
if ([handler canHandleRequest:request]) {
return handler;
}
}
return nil;
}
- (NSDictionary<NSString *, id> *)stripNullsInRequestHeaders:(NSDictionary<NSString *, id> *)headers
{
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:headers.count];
for (NSString *key in headers.allKeys) {
id val = headers[key];
if (val != [NSNull null]) {
result[key] = val;
}
}
return result;
}
- (RCTURLRequestCancellationBlock)buildRequest:(NSDictionary<NSString *, id> *)query
completionBlock:(void (^)(NSURLRequest *request))block
{
RCTAssertThread(_methodQueue, @"buildRequest: must be called on method queue");
NSURL *URL = [RCTConvert NSURL:query[@"url"]]; // this is marked as nullable in JS, but should not be null
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
request.HTTPMethod = [RCTConvert NSString:RCTNilIfNull(query[@"method"])].uppercaseString ?: @"GET";
// Load and set the cookie header.
NSArray<NSHTTPCookie *> *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:URL];
request.allHTTPHeaderFields = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
// Set supplied headers.
NSDictionary *headers = [RCTConvert NSDictionary:query[@"headers"]];
[headers enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
if (value) {
[request addValue:[RCTConvert NSString:value] forHTTPHeaderField:key];
}
}];
request.timeoutInterval = [RCTConvert NSTimeInterval:query[@"timeout"]];
request.HTTPShouldHandleCookies = [RCTConvert BOOL:query[@"withCredentials"]];
NSDictionary<NSString *, id> *data = [RCTConvert NSDictionary:RCTNilIfNull(query[@"data"])];
NSString *trackingName = data[@"trackingName"];
if (trackingName) {
[NSURLProtocol setProperty:trackingName
forKey:@"trackingName"
inRequest:request];
}
return [self processDataForHTTPQuery:data callback:^(NSError *error, NSDictionary<NSString *, id> *result) {
if (error) {
RCTLogError(@"Error processing request body: %@", error);
// Ideally we'd circle back to JS here and notify an error/abort on the request.
return (RCTURLRequestCancellationBlock)nil;
}
request.HTTPBody = result[@"body"];
NSString *dataContentType = result[@"contentType"];
NSString *requestContentType = [request valueForHTTPHeaderField:@"Content-Type"];
BOOL isMultipart = [dataContentType hasPrefix:@"multipart"];
// For multipart requests we need to override caller-specified content type with one
// from the data object, because it contains the boundary string
if (dataContentType && ([requestContentType length] == 0 || isMultipart)) {
[request setValue:dataContentType forHTTPHeaderField:@"Content-Type"];
}
// Gzip the request body
if ([request.allHTTPHeaderFields[@"Content-Encoding"] isEqualToString:@"gzip"]) {
request.HTTPBody = RCTGzipData(request.HTTPBody, -1 /* default */);
[request setValue:(@(request.HTTPBody.length)).description forHTTPHeaderField:@"Content-Length"];
}
dispatch_async(self->_methodQueue, ^{
block(request);
});
return (RCTURLRequestCancellationBlock)nil;
}];
}
- (BOOL)canHandleRequest:(NSURLRequest *)request
{
return [self handlerForRequest:request] != nil;
}
/**
* Process the 'data' part of an HTTP query.
*
* 'data' can be a JSON value of the following forms:
*
* - {"string": "..."}: a simple JS string that will be UTF-8 encoded and sent as the body
*
* - {"uri": "some-uri://..."}: reference to a system resource, e.g. an image in the asset library
*
* - {"formData": [...]}: list of data payloads that will be combined into a multipart/form-data request
*
* - {"blob": {...}}: an object representing a blob
*
* If successful, the callback be called with a result dictionary containing the following (optional) keys:
*
* - @"body" (NSData): the body of the request
*
* - @"contentType" (NSString): the content type header of the request
*
*/
- (RCTURLRequestCancellationBlock)processDataForHTTPQuery:(nullable NSDictionary<NSString *, id> *)query callback:
(RCTURLRequestCancellationBlock (^)(NSError *error, NSDictionary<NSString *, id> *result))callback
{
RCTAssertThread(_methodQueue, @"processDataForHTTPQuery: must be called on method queue");
if (!query) {
return callback(nil, nil);
}
for (id<RCTNetworkingRequestHandler> handler in _requestHandlers) {
if ([handler canHandleNetworkingRequest:query]) {
// @lint-ignore FBOBJCUNTYPEDCOLLECTION1
NSDictionary *body = [handler handleNetworkingRequest:query];
if (body) {
return callback(nil, body);
}
}
}
NSData *body = [RCTConvert NSData:query[@"string"]];
if (body) {
return callback(nil, @{@"body": body});
}
NSString *base64String = [RCTConvert NSString:query[@"base64"]];
if (base64String) {
NSData *data = [[NSData alloc] initWithBase64EncodedString:base64String options:0];
return callback(nil, @{@"body": data});
}
NSURLRequest *request = [RCTConvert NSURLRequest:query[@"uri"]];
if (request) {
__block RCTURLRequestCancellationBlock cancellationBlock = nil;
RCTNetworkTask *task = [self networkTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
dispatch_async(self->_methodQueue, ^{
cancellationBlock = callback(error, data ? @{@"body": data, @"contentType": RCTNullIfNil(response.MIMEType)} : nil);
});
}];
[task start];
__weak RCTNetworkTask *weakTask = task;
return ^{
[weakTask cancel];
if (cancellationBlock) {
cancellationBlock();
}
};
}
NSArray<NSDictionary *> *formData = [RCTConvert NSDictionaryArray:query[@"formData"]];
if (formData) {
RCTHTTPFormDataHelper *formDataHelper = [RCTHTTPFormDataHelper new];
formDataHelper.networker = self;
return [formDataHelper process:formData callback:callback];
}
// Nothing in the data payload, at least nothing we could understand anyway.
// Ignore and treat it as if it were null.
return callback(nil, nil);
}
+ (NSString *)decodeTextData:(NSData *)data fromResponse:(NSURLResponse *)response withCarryData:(NSMutableData *)inputCarryData
{
NSStringEncoding encoding = NSUTF8StringEncoding;
if (response.textEncodingName) {
CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
}
NSMutableData *currentCarryData = inputCarryData ?: [NSMutableData new];
[currentCarryData appendData:data];
// Attempt to decode text
NSString *encodedResponse = [[NSString alloc] initWithData:currentCarryData encoding:encoding];
if (!encodedResponse && data.length > 0) {
if (encoding == NSUTF8StringEncoding && inputCarryData) {
// If decode failed, we attempt to trim broken character bytes from the data.
// At this time, only UTF-8 support is enabled. Multibyte encodings, such as UTF-16 and UTF-32, require a lot of additional work
// to determine wether BOM was included in the first data packet. If so, save it, and attach it to each new data packet. If not,
// an encoding has to be selected with a suitable byte order (for ARM iOS, it would be little endianness).
CFStringEncoding cfEncoding = CFStringConvertNSStringEncodingToEncoding(encoding);
// Taking a single unichar is not good enough, due to Unicode combining character sequences or characters outside the BMP.
// See https://www.objc.io/issues/9-strings/unicode/#common-pitfalls
// We'll attempt with a sequence of two characters, the most common combining character sequence and characters outside the BMP (emojis).
CFIndex maxCharLength = CFStringGetMaximumSizeForEncoding(2, cfEncoding);
NSUInteger removedBytes = 1;
while (removedBytes < maxCharLength) {
encodedResponse = [[NSString alloc] initWithData:[currentCarryData subdataWithRange:NSMakeRange(0, currentCarryData.length - removedBytes)]
encoding:encoding];
if (encodedResponse != nil) {
break;
}
removedBytes += 1;
}
} else {
// We don't have an encoding, or the encoding is incorrect, so now we try to guess
[NSString stringEncodingForData:data
encodingOptions:@{ NSStringEncodingDetectionSuggestedEncodingsKey: @[ @(encoding) ] }
convertedString:&encodedResponse
usedLossyConversion:NULL];
}
}
if (inputCarryData) {
NSUInteger encodedResponseLength = [encodedResponse dataUsingEncoding:encoding].length;
// Ensure a valid subrange exists within currentCarryData
if (currentCarryData.length >= encodedResponseLength) {
NSData *newCarryData = [currentCarryData subdataWithRange:NSMakeRange(encodedResponseLength, currentCarryData.length - encodedResponseLength)];
[inputCarryData setData:newCarryData];
} else {
[inputCarryData setLength:0];
}
}
return encodedResponse;
}
- (void)sendData:(NSData *)data
responseType:(NSString *)responseType
response:(NSURLResponse *)response
forTask:(RCTNetworkTask *)task
{
RCTAssertThread(_methodQueue, @"sendData: must be called on method queue");
id responseData = nil;
for (id<RCTNetworkingResponseHandler> handler in _responseHandlers) {
if ([handler canHandleNetworkingResponse:responseType]) {
responseData = [handler handleNetworkingResponse:response data:data];
break;
}
}
if (!responseData) {
if (data.length == 0) {
return;
}
if ([responseType isEqualToString:@"text"]) {
// No carry storage is required here because the entire data has been loaded.
responseData = [RCTNetworking decodeTextData:data fromResponse:task.response withCarryData:nil];
if (!responseData) {
RCTLogWarn(@"Received data was not a string, or was not a recognised encoding.");
return;
}
} else if ([responseType isEqualToString:@"base64"]) {
responseData = [data base64EncodedStringWithOptions:0];
} else {
RCTLogWarn(@"Invalid responseType: %@", responseType);
return;
}
}
[self sendEventWithName:@"didReceiveNetworkData" body:@[task.requestID, responseData]];
}
- (void)sendRequest:(NSURLRequest *)request
responseType:(NSString *)responseType
incrementalUpdates:(BOOL)incrementalUpdates
responseSender:(RCTResponseSenderBlock)responseSender
{
RCTAssertThread(_methodQueue, @"sendRequest: must be called on method queue");
__weak __typeof(self) weakSelf = self;
__block RCTNetworkTask *task;
RCTURLRequestProgressBlock uploadProgressBlock = ^(int64_t progress, int64_t total) {
NSArray *responseJSON = @[task.requestID, @((double)progress), @((double)total)];
[weakSelf sendEventWithName:@"didSendNetworkData" body:responseJSON];
};
RCTURLRequestResponseBlock responseBlock = ^(NSURLResponse *response) {
NSDictionary<NSString *, NSString *> *headers;
NSInteger status;
if ([response isKindOfClass:[NSHTTPURLResponse class]]) { // Might be a local file request
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
headers = httpResponse.allHeaderFields ?: @{};
status = httpResponse.statusCode;
} else {
headers = response.MIMEType ? @{@"Content-Type": response.MIMEType} : @{};
status = 200;
}
id responseURL = response.URL ? response.URL.absoluteString : [NSNull null];
NSArray<id> *responseJSON = @[task.requestID, @(status), headers, responseURL];
[weakSelf sendEventWithName:@"didReceiveNetworkResponse" body:responseJSON];
};
// XHR does not allow you to peek at xhr.response before the response is
// finished. Only when xhr.responseType is set to ''/'text', consumers may
// peek at xhr.responseText. So unless the requested responseType is 'text',
// we only send progress updates and not incremental data updates to JS here.
RCTURLRequestIncrementalDataBlock incrementalDataBlock = nil;
RCTURLRequestProgressBlock downloadProgressBlock = nil;
if (incrementalUpdates) {
if ([responseType isEqualToString:@"text"]) {
// We need this to carry over bytes, which could not be decoded into text (such as broken UTF-8 characters).
// The incremental data block holds the ownership of this object, and will be released upon release of the block.
NSMutableData *incrementalDataCarry = [NSMutableData new];
incrementalDataBlock = ^(NSData *data, int64_t progress, int64_t total) {
NSUInteger initialCarryLength = incrementalDataCarry.length;
NSString *responseString = [RCTNetworking decodeTextData:data
fromResponse:task.response
withCarryData:incrementalDataCarry];
if (!responseString) {
RCTLogWarn(@"Received data was not a string, or was not a recognised encoding.");
return;
}
// Update progress to include the previous carry length and reduce the current carry length.
NSArray<id> *responseJSON = @[task.requestID,
responseString,
@(progress + initialCarryLength - incrementalDataCarry.length),
@(total)];
[weakSelf sendEventWithName:@"didReceiveNetworkIncrementalData" body:responseJSON];
};
} else {
downloadProgressBlock = ^(int64_t progress, int64_t total) {
NSArray<id> *responseJSON = @[task.requestID, @(progress), @(total)];
[weakSelf sendEventWithName:@"didReceiveNetworkDataProgress" body:responseJSON];
};
}
}
RCTURLRequestCompletionBlock completionBlock =
^(NSURLResponse *response, NSData *data, NSError *error) {
__typeof(self) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
// Unless we were sending incremental (text) chunks to JS, all along, now
// is the time to send the request body to JS.
if (!(incrementalUpdates && [responseType isEqualToString:@"text"])) {
[strongSelf sendData:data
responseType:responseType
response:response
forTask:task];
}
NSArray *responseJSON = @[task.requestID,
RCTNullIfNil(error.localizedDescription),
error.code == kCFURLErrorTimedOut ? @YES : @NO
];
[strongSelf sendEventWithName:@"didCompleteNetworkResponse" body:responseJSON];
[strongSelf->_tasksByRequestID removeObjectForKey:task.requestID];
};
task = [self networkTaskWithRequest:request completionBlock:completionBlock];
task.downloadProgressBlock = downloadProgressBlock;
task.incrementalDataBlock = incrementalDataBlock;
task.responseBlock = responseBlock;
task.uploadProgressBlock = uploadProgressBlock;
if (task.requestID) {
if (!_tasksByRequestID) {
_tasksByRequestID = [NSMutableDictionary new];
}
_tasksByRequestID[task.requestID] = task;
responseSender(@[task.requestID]);
}
[task start];
}
#pragma mark - Public API
- (void)addRequestHandler:(id<RCTNetworkingRequestHandler>)handler
{
if (!_requestHandlers) {
_requestHandlers = [NSMutableArray new];
}
[_requestHandlers addObject:handler];
}
- (void)addResponseHandler:(id<RCTNetworkingResponseHandler>)handler
{
if (!_responseHandlers) {
_responseHandlers = [NSMutableArray new];
}
[_responseHandlers addObject:handler];
}
- (void)removeRequestHandler:(id<RCTNetworkingRequestHandler>)handler
{
[_requestHandlers removeObject:handler];
}
- (void)removeResponseHandler:(id<RCTNetworkingResponseHandler>)handler
{
[_responseHandlers removeObject:handler];
}
- (RCTNetworkTask *)networkTaskWithRequest:(NSURLRequest *)request completionBlock:(RCTURLRequestCompletionBlock)completionBlock
{
id<RCTURLRequestHandler> handler = [self handlerForRequest:request];
if (!handler) {
RCTLogError(@"No suitable URL request handler found for %@", request.URL);
return nil;
}
RCTNetworkTask *task = [[RCTNetworkTask alloc] initWithRequest:request
handler:handler
callbackQueue:_methodQueue];
task.completionBlock = completionBlock;
return task;
}
#pragma mark - JS API
RCT_EXPORT_METHOD(sendRequest:(NSDictionary *)query
responseSender:(RCTResponseSenderBlock)responseSender)
{
// TODO: buildRequest returns a cancellation block, but there's currently
// no way to invoke it, if, for example the request is cancelled while
// loading a large file to build the request body
[self buildRequest:query completionBlock:^(NSURLRequest *request) {
NSString *responseType = [RCTConvert NSString:query[@"responseType"]];
BOOL incrementalUpdates = [RCTConvert BOOL:query[@"incrementalUpdates"]];
[self sendRequest:request
responseType:responseType
incrementalUpdates:incrementalUpdates
responseSender:responseSender];
}];
}
RCT_EXPORT_METHOD(abortRequest:(nonnull NSNumber *)requestID)
{
[_tasksByRequestID[requestID] cancel];
[_tasksByRequestID removeObjectForKey:requestID];
}
RCT_EXPORT_METHOD(clearCookies:(RCTResponseSenderBlock)responseSender)
{
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
if (!storage.cookies.count) {
responseSender(@[@NO]);
return;
}
for (NSHTTPCookie *cookie in storage.cookies) {
[storage deleteCookie:cookie];
}
responseSender(@[@YES]);
}
@end
@implementation RCTBridge (RCTNetworking)
- (RCTNetworking *)networking
{
return [self moduleForClass:[RCTNetworking class]];
}
@end

View File

@@ -0,0 +1,160 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule XHRInterceptor
*/
'use strict';
const XMLHttpRequest = require('XMLHttpRequest');
const originalXHROpen = XMLHttpRequest.prototype.open;
const originalXHRSend = XMLHttpRequest.prototype.send;
const originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
var openCallback;
var sendCallback;
var requestHeaderCallback;
var headerReceivedCallback;
var responseCallback;
var isInterceptorEnabled = false;
/**
* A network interceptor which monkey-patches XMLHttpRequest methods
* to gather all network requests/responses, in order to show their
* information in the React Native inspector development tool.
* This supports interception with XMLHttpRequest API, including Fetch API
* and any other third party libraries that depend on XMLHttpRequest.
*/
const XHRInterceptor = {
/**
* Invoked before XMLHttpRequest.open(...) is called.
*/
setOpenCallback(callback) {
openCallback = callback;
},
/**
* Invoked before XMLHttpRequest.send(...) is called.
*/
setSendCallback(callback) {
sendCallback = callback;
},
/**
* Invoked after xhr's readyState becomes xhr.HEADERS_RECEIVED.
*/
setHeaderReceivedCallback(callback) {
headerReceivedCallback = callback;
},
/**
* Invoked after xhr's readyState becomes xhr.DONE.
*/
setResponseCallback(callback) {
responseCallback = callback;
},
/**
* Invoked before XMLHttpRequest.setRequestHeader(...) is called.
*/
setRequestHeaderCallback(callback) {
requestHeaderCallback = callback;
},
isInterceptorEnabled() {
return isInterceptorEnabled;
},
enableInterception() {
if (isInterceptorEnabled) {
return;
}
// Override `open` method for all XHR requests to intercept the request
// method and url, then pass them through the `openCallback`.
XMLHttpRequest.prototype.open = function(method, url) {
if (openCallback) {
openCallback(method, url, this);
}
originalXHROpen.apply(this, arguments);
};
// Override `setRequestHeader` method for all XHR requests to intercept
// the request headers, then pass them through the `requestHeaderCallback`.
XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
if (requestHeaderCallback) {
requestHeaderCallback(header, value, this);
}
originalXHRSetRequestHeader.apply(this, arguments);
};
// Override `send` method of all XHR requests to intercept the data sent,
// register listeners to intercept the response, and invoke the callbacks.
XMLHttpRequest.prototype.send = function(data) {
if (sendCallback) {
sendCallback(data, this);
}
if (this.addEventListener) {
this.addEventListener('readystatechange', () => {
if (!isInterceptorEnabled) {
return;
}
if (this.readyState === this.HEADERS_RECEIVED) {
const contentTypeString = this.getResponseHeader('Content-Type');
const contentLengthString =
this.getResponseHeader('Content-Length');
let responseContentType, responseSize;
if (contentTypeString) {
responseContentType = contentTypeString.split(';')[0];
}
if (contentLengthString) {
responseSize = parseInt(contentLengthString, 10);
}
if (headerReceivedCallback) {
headerReceivedCallback(
responseContentType,
responseSize,
this.getAllResponseHeaders(),
this,
);
}
}
if (this.readyState === this.DONE) {
if (responseCallback) {
responseCallback(
this.status,
this.timeout,
this.response,
this.responseURL,
this.responseType,
this,
);
}
}
}, false);
}
originalXHRSend.apply(this, arguments);
};
isInterceptorEnabled = true;
},
// Unpatch XMLHttpRequest methods and remove the callbacks.
disableInterception() {
if (!isInterceptorEnabled) {
return;
}
isInterceptorEnabled = false;
XMLHttpRequest.prototype.send = originalXHRSend;
XMLHttpRequest.prototype.open = originalXHROpen;
XMLHttpRequest.prototype.setRequestHeader = originalXHRSetRequestHeader;
responseCallback = null;
openCallback = null;
sendCallback = null;
headerReceivedCallback = null;
requestHeaderCallback = null;
},
};
module.exports = XHRInterceptor;

View File

@@ -0,0 +1,589 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule XMLHttpRequest
* @flow
*/
'use strict';
const EventTarget = require('event-target-shim');
const RCTNetworking = require('RCTNetworking');
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
* found when Flow v0.54 was deployed. To see the error delete this comment and
* run Flow. */
const base64 = require('base64-js');
const invariant = require('fbjs/lib/invariant');
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
* found when Flow v0.54 was deployed. To see the error delete this comment and
* run Flow. */
const warning = require('fbjs/lib/warning');
const BlobManager = require('BlobManager');
export type NativeResponseType = 'base64' | 'blob' | 'text';
export type ResponseType = '' | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text';
export type Response = ?Object | string;
type XHRInterceptor = {
requestSent(
id: number,
url: string,
method: string,
headers: Object
): void,
responseReceived(
id: number,
url: string,
status: number,
headers: Object
): void,
dataReceived(
id: number,
data: string
): void,
loadingFinished(
id: number,
encodedDataLength: number
): void,
loadingFailed(
id: number,
error: string
): void,
};
// The native blob module is optional so inject it here if available.
if (BlobManager.isAvailable) {
BlobManager.addNetworkingHandler();
}
const UNSENT = 0;
const OPENED = 1;
const HEADERS_RECEIVED = 2;
const LOADING = 3;
const DONE = 4;
const SUPPORTED_RESPONSE_TYPES = {
arraybuffer: typeof global.ArrayBuffer === 'function',
blob: typeof global.Blob === 'function',
document: false,
json: true,
text: true,
'': true,
};
const REQUEST_EVENTS = [
'abort',
'error',
'load',
'loadstart',
'progress',
'timeout',
'loadend',
];
const XHR_EVENTS = REQUEST_EVENTS.concat('readystatechange');
class XMLHttpRequestEventTarget extends EventTarget(...REQUEST_EVENTS) {
onload: ?Function;
onloadstart: ?Function;
onprogress: ?Function;
ontimeout: ?Function;
onerror: ?Function;
onabort: ?Function;
onloadend: ?Function;
}
/**
* Shared base for platform-specific XMLHttpRequest implementations.
*/
class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
static UNSENT: number = UNSENT;
static OPENED: number = OPENED;
static HEADERS_RECEIVED: number = HEADERS_RECEIVED;
static LOADING: number = LOADING;
static DONE: number = DONE;
static _interceptor: ?XHRInterceptor = null;
UNSENT: number = UNSENT;
OPENED: number = OPENED;
HEADERS_RECEIVED: number = HEADERS_RECEIVED;
LOADING: number = LOADING;
DONE: number = DONE;
// EventTarget automatically initializes these to `null`.
onload: ?Function;
onloadstart: ?Function;
onprogress: ?Function;
ontimeout: ?Function;
onerror: ?Function;
onabort: ?Function;
onloadend: ?Function;
onreadystatechange: ?Function;
readyState: number = UNSENT;
responseHeaders: ?Object;
status: number = 0;
timeout: number = 0;
responseURL: ?string;
withCredentials: boolean = true
upload: XMLHttpRequestEventTarget = new XMLHttpRequestEventTarget();
_requestId: ?number;
_subscriptions: Array<*>;
_aborted: boolean = false;
_cachedResponse: Response;
_hasError: boolean = false;
_headers: Object;
_lowerCaseResponseHeaders: Object;
_method: ?string = null;
_response: string | ?Object;
_responseType: ResponseType;
_response: string = '';
_sent: boolean;
_url: ?string = null;
_timedOut: boolean = false;
_trackingName: string = 'unknown';
_incrementalEvents: boolean = false;
static setInterceptor(interceptor: ?XHRInterceptor) {
XMLHttpRequest._interceptor = interceptor;
}
constructor() {
super();
this._reset();
}
_reset(): void {
this.readyState = this.UNSENT;
this.responseHeaders = undefined;
this.status = 0;
delete this.responseURL;
this._requestId = null;
this._cachedResponse = undefined;
this._hasError = false;
this._headers = {};
this._response = '';
this._responseType = '';
this._sent = false;
this._lowerCaseResponseHeaders = {};
this._clearSubscriptions();
this._timedOut = false;
}
get responseType(): ResponseType {
return this._responseType;
}
set responseType(responseType: ResponseType): void {
if (this._sent) {
throw new Error(
'Failed to set the \'responseType\' property on \'XMLHttpRequest\': The ' +
'response type cannot be set after the request has been sent.'
);
}
if (!SUPPORTED_RESPONSE_TYPES.hasOwnProperty(responseType)) {
warning(
false,
`The provided value '${responseType}' is not a valid 'responseType'.`
);
return;
}
// redboxes early, e.g. for 'arraybuffer' on ios 7
invariant(
SUPPORTED_RESPONSE_TYPES[responseType] || responseType === 'document',
`The provided value '${responseType}' is unsupported in this environment.`
);
if (responseType === 'blob') {
invariant(BlobManager.isAvailable, 'Native module BlobModule is required for blob support');
}
this._responseType = responseType;
}
get responseText(): string {
if (this._responseType !== '' && this._responseType !== 'text') {
throw new Error(
"The 'responseText' property is only available if 'responseType' " +
`is set to '' or 'text', but it is '${this._responseType}'.`
);
}
if (this.readyState < LOADING) {
return '';
}
return this._response;
}
get response(): Response {
const {responseType} = this;
if (responseType === '' || responseType === 'text') {
return this.readyState < LOADING || this._hasError
? ''
: this._response;
}
if (this.readyState !== DONE) {
return null;
}
if (this._cachedResponse !== undefined) {
return this._cachedResponse;
}
switch (responseType) {
case 'document':
this._cachedResponse = null;
break;
case 'arraybuffer':
this._cachedResponse = base64.toByteArray(this._response).buffer;
break;
case 'blob':
if (typeof this._response === 'object' && this._response) {
this._cachedResponse = BlobManager.createFromOptions(this._response);
} else {
throw new Error(`Invalid response for blob: ${this._response}`);
}
break;
case 'json':
try {
this._cachedResponse = JSON.parse(this._response);
} catch (_) {
this._cachedResponse = null;
}
break;
default:
this._cachedResponse = null;
}
return this._cachedResponse;
}
// exposed for testing
__didCreateRequest(requestId: number): void {
this._requestId = requestId;
XMLHttpRequest._interceptor && XMLHttpRequest._interceptor.requestSent(
requestId,
this._url || '',
this._method || 'GET',
this._headers);
}
// exposed for testing
__didUploadProgress(
requestId: number,
progress: number,
total: number
): void {
if (requestId === this._requestId) {
this.upload.dispatchEvent({
type: 'progress',
lengthComputable: true,
loaded: progress,
total,
});
}
}
__didReceiveResponse(
requestId: number,
status: number,
responseHeaders: ?Object,
responseURL: ?string
): void {
if (requestId === this._requestId) {
this.status = status;
this.setResponseHeaders(responseHeaders);
this.setReadyState(this.HEADERS_RECEIVED);
if (responseURL || responseURL === '') {
this.responseURL = responseURL;
} else {
delete this.responseURL;
}
XMLHttpRequest._interceptor && XMLHttpRequest._interceptor.responseReceived(
requestId,
responseURL || this._url || '',
status,
responseHeaders || {});
}
}
__didReceiveData(requestId: number, response: string): void {
if (requestId !== this._requestId) {
return;
}
this._response = response;
this._cachedResponse = undefined; // force lazy recomputation
this.setReadyState(this.LOADING);
XMLHttpRequest._interceptor && XMLHttpRequest._interceptor.dataReceived(
requestId,
response);
}
__didReceiveIncrementalData(
requestId: number,
responseText: string,
progress: number,
total: number
) {
if (requestId !== this._requestId) {
return;
}
if (!this._response) {
this._response = responseText;
} else {
this._response += responseText;
}
XMLHttpRequest._interceptor && XMLHttpRequest._interceptor.dataReceived(
requestId,
responseText);
this.setReadyState(this.LOADING);
this.__didReceiveDataProgress(requestId, progress, total);
}
__didReceiveDataProgress(
requestId: number,
loaded: number,
total: number
): void {
if (requestId !== this._requestId) {
return;
}
this.dispatchEvent({
type: 'progress',
lengthComputable: total >= 0,
loaded,
total,
});
}
// exposed for testing
__didCompleteResponse(
requestId: number,
error: string,
timeOutError: boolean
): void {
if (requestId === this._requestId) {
if (error) {
if (this._responseType === '' || this._responseType === 'text') {
this._response = error;
}
this._hasError = true;
if (timeOutError) {
this._timedOut = true;
}
}
this._clearSubscriptions();
this._requestId = null;
this.setReadyState(this.DONE);
if (error) {
XMLHttpRequest._interceptor && XMLHttpRequest._interceptor.loadingFailed(
requestId,
error);
} else {
XMLHttpRequest._interceptor && XMLHttpRequest._interceptor.loadingFinished(
requestId,
this._response.length);
}
}
}
_clearSubscriptions(): void {
(this._subscriptions || []).forEach(sub => {
if (sub) {
sub.remove();
}
});
this._subscriptions = [];
}
getAllResponseHeaders(): ?string {
if (!this.responseHeaders) {
// according to the spec, return null if no response has been received
return null;
}
var headers = this.responseHeaders || {};
return Object.keys(headers).map((headerName) => {
return headerName + ': ' + headers[headerName];
}).join('\r\n');
}
getResponseHeader(header: string): ?string {
var value = this._lowerCaseResponseHeaders[header.toLowerCase()];
return value !== undefined ? value : null;
}
setRequestHeader(header: string, value: any): void {
if (this.readyState !== this.OPENED) {
throw new Error('Request has not been opened');
}
this._headers[header.toLowerCase()] = String(value);
}
/**
* Custom extension for tracking origins of request.
*/
setTrackingName(trackingName: string): XMLHttpRequest {
this._trackingName = trackingName;
return this;
}
open(method: string, url: string, async: ?boolean): void {
/* Other optional arguments are not supported yet */
if (this.readyState !== this.UNSENT) {
throw new Error('Cannot open, already sending');
}
if (async !== undefined && !async) {
// async is default
throw new Error('Synchronous http requests are not supported');
}
if (!url) {
throw new Error('Cannot load an empty url');
}
this._method = method.toUpperCase();
this._url = url;
this._aborted = false;
this.setReadyState(this.OPENED);
}
send(data: any): void {
if (this.readyState !== this.OPENED) {
throw new Error('Request has not been opened');
}
if (this._sent) {
throw new Error('Request has already been sent');
}
this._sent = true;
const incrementalEvents = this._incrementalEvents ||
!!this.onreadystatechange ||
!!this.onprogress;
this._subscriptions.push(RCTNetworking.addListener(
'didSendNetworkData',
(args) => this.__didUploadProgress(...args)
));
this._subscriptions.push(RCTNetworking.addListener(
'didReceiveNetworkResponse',
(args) => this.__didReceiveResponse(...args)
));
this._subscriptions.push(RCTNetworking.addListener(
'didReceiveNetworkData',
(args) => this.__didReceiveData(...args)
));
this._subscriptions.push(RCTNetworking.addListener(
'didReceiveNetworkIncrementalData',
(args) => this.__didReceiveIncrementalData(...args)
));
this._subscriptions.push(RCTNetworking.addListener(
'didReceiveNetworkDataProgress',
(args) => this.__didReceiveDataProgress(...args)
));
this._subscriptions.push(RCTNetworking.addListener(
'didCompleteNetworkResponse',
(args) => this.__didCompleteResponse(...args)
));
let nativeResponseType: NativeResponseType = 'text';
if (this._responseType === 'arraybuffer') {
nativeResponseType = 'base64';
}
if (this._responseType === 'blob') {
nativeResponseType = 'blob';
}
invariant(this._method, 'Request method needs to be defined.');
invariant(this._url, 'Request URL needs to be defined.');
RCTNetworking.sendRequest(
this._method,
this._trackingName,
this._url,
this._headers,
data,
nativeResponseType,
incrementalEvents,
this.timeout,
this.__didCreateRequest.bind(this),
this.withCredentials
);
}
abort(): void {
this._aborted = true;
if (this._requestId) {
RCTNetworking.abortRequest(this._requestId);
}
// only call onreadystatechange if there is something to abort,
// below logic is per spec
if (!(this.readyState === this.UNSENT ||
(this.readyState === this.OPENED && !this._sent) ||
this.readyState === this.DONE)) {
this._reset();
this.setReadyState(this.DONE);
}
// Reset again after, in case modified in handler
this._reset();
}
setResponseHeaders(responseHeaders: ?Object): void {
this.responseHeaders = responseHeaders || null;
var headers = responseHeaders || {};
this._lowerCaseResponseHeaders =
Object.keys(headers).reduce((lcaseHeaders, headerName) => {
lcaseHeaders[headerName.toLowerCase()] = headers[headerName];
return lcaseHeaders;
}, {});
}
setReadyState(newState: number): void {
this.readyState = newState;
this.dispatchEvent({type: 'readystatechange'});
if (newState === this.DONE) {
if (this._aborted) {
this.dispatchEvent({type: 'abort'});
} else if (this._hasError) {
if (this._timedOut) {
this.dispatchEvent({type: 'timeout'});
} else {
this.dispatchEvent({type: 'error'});
}
} else {
this.dispatchEvent({type: 'load'});
}
this.dispatchEvent({type: 'loadend'});
}
}
/* global EventListener */
addEventListener(type: string, listener: EventListener): void {
// If we dont' have a 'readystatechange' event handler, we don't
// have to send repeated LOADING events with incremental updates
// to responseText, which will avoid a bunch of native -> JS
// bridge traffic.
if (type === 'readystatechange' || type === 'progress') {
this._incrementalEvents = true;
}
super.addEventListener(type, listener);
}
}
module.exports = XMLHttpRequest;

View File

@@ -0,0 +1,43 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule convertRequestBody
* @flow
* @format
*/
'use strict';
const binaryToBase64 = require('binaryToBase64');
const Blob = require('Blob');
const FormData = require('FormData');
export type RequestBody =
| string
| Blob
| FormData
| {uri: string}
| ArrayBuffer
| $ArrayBufferView;
function convertRequestBody(body: RequestBody): Object {
if (typeof body === 'string') {
return {string: body};
}
if (body instanceof Blob) {
return {blob: body.data};
}
if (body instanceof FormData) {
return {formData: body.getParts()};
}
if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
// $FlowFixMe: no way to assert that 'body' is indeed an ArrayBufferView
return {base64: binaryToBase64(body)};
}
return body;
}
module.exports = convertRequestBody;

21
node_modules/react-native/Libraries/Network/fetch.js generated vendored Normal file
View File

@@ -0,0 +1,21 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule fetch
*
*/
/* globals Headers, Request, Response */
'use strict';
import whatwg from 'whatwg-fetch';
if (whatwg && whatwg.fetch) {
module.exports = whatwg;
} else {
module.exports = {fetch, Headers, Request, Response};
}