diff --git a/framework/android/src/main/java/com/tencent/mtt/hippy/modules/nativemodules/network/NetworkModule.java b/framework/android/src/main/java/com/tencent/mtt/hippy/modules/nativemodules/network/NetworkModule.java index 80ad9057a14..063d09cdb22 100644 --- a/framework/android/src/main/java/com/tencent/mtt/hippy/modules/nativemodules/network/NetworkModule.java +++ b/framework/android/src/main/java/com/tencent/mtt/hippy/modules/nativemodules/network/NetworkModule.java @@ -47,6 +47,7 @@ public class NetworkModule extends HippyNativeModuleBase { private static final String TAG = "NetworkModule"; + private static final String HTTP_RESPONSE_REQUEST_DURATION = "Hippy-Request-Duration"; public NetworkModule(HippyEngineContext context) { super(context); @@ -103,8 +104,9 @@ protected void normalizeRequest(@NonNull HippyMap request, } @NonNull - protected JSObject handleFetchResponse(@NonNull ResourceDataHolder dataHolder) - throws IllegalStateException { + protected JSObject handleFetchResponse(@NonNull ResourceDataHolder dataHolder, + double requestDuration) + throws IllegalStateException { JSObject responseObject = new JSObject(); int statusCode = -1; String responseMessage = null; @@ -129,6 +131,7 @@ protected JSObject handleFetchResponse(@NonNull ResourceDataHolder dataHolder) if (responseMessage == null) { responseMessage = (dataHolder.errorMessage == null) ? "" : dataHolder.errorMessage; } + headerObject.set(HTTP_RESPONSE_REQUEST_DURATION, Double.toString(requestDuration)); responseObject.set(HTTP_RESPONSE_STATUS_CODE, statusCode); responseObject.set("statusLine", responseMessage); responseObject.set("respHeaders", headerObject); @@ -145,10 +148,12 @@ protected JSObject handleFetchResponse(@NonNull ResourceDataHolder dataHolder) return responseObject; } - protected void handleFetchResult(@NonNull ResourceDataHolder dataHolder, final Promise promise) { + protected void handleFetchResult(@NonNull ResourceDataHolder dataHolder, + double requestDuration, + final Promise promise) { try { if (dataHolder.resultCode == ResourceDataHolder.RESOURCE_LOAD_SUCCESS_CODE) { - JSObject responseObject = handleFetchResponse(dataHolder); + JSObject responseObject = handleFetchResponse(dataHolder, requestDuration); promise.resolve(responseObject); } else { String errorMessage = @@ -177,19 +182,25 @@ public void fetch(final HippyMap request, final Promise promise) { promise.reject("Get url parameter failed!"); return; } + + // Record request start time + final long startTime = System.nanoTime(); + vfsManager.fetchResourceAsync(uri, requestHeaders, requestParams, - new FetchResourceCallback() { - @Override - public void onFetchCompleted(@NonNull ResourceDataHolder dataHolder) { - handleFetchResult(dataHolder, promise); - dataHolder.recycle(); - } + new FetchResourceCallback() { + @Override + public void onFetchCompleted(@NonNull ResourceDataHolder dataHolder) { + // Time taken for the request, in milliseconds + double requestDuration = (System.nanoTime() - startTime) / 1_000_000.0; + handleFetchResult(dataHolder, requestDuration, promise); + dataHolder.recycle(); + } - @Override - public void onFetchProgress(long total, long loaded) { - // Nothing need to do here. - } - }); + @Override + public void onFetchProgress(long total, long loaded) { + // Nothing need to do here. + } + }); } @HippyMethod(name = "getCookie") diff --git a/framework/ios/module/network/HippyNetWork.h b/framework/ios/module/network/HippyNetWork.h index 2957ac84a64..82c4d4ce242 100644 --- a/framework/ios/module/network/HippyNetWork.h +++ b/framework/ios/module/network/HippyNetWork.h @@ -21,9 +21,9 @@ */ #import - #import "HippyBridgeModule.h" +/// Hippy Network module @interface HippyNetWork : NSObject @end diff --git a/framework/ios/module/network/HippyNetWork.mm b/framework/ios/module/network/HippyNetWork.mm index 5fb487e19b5..8c1ddc6430c 100644 --- a/framework/ios/module/network/HippyNetWork.mm +++ b/framework/ios/module/network/HippyNetWork.mm @@ -20,15 +20,31 @@ * limitations under the License. */ -#import -#import - +#import "HippyNetWork.h" +#import +#import #import "HippyBridge+VFSLoader.h" #import "HippyDefines.h" -#import "HippyNetWork.h" #import "HippyAssert.h" #import "HippyUtils.h" + +// Request parameter of fetch API +static NSString *const kHippyNetworkRequestParaURL = @"url"; +static NSString *const kHippyNetworkRequestParaMethod = @"method"; +static NSString *const kHippyNetworkRequestParaHeaders = @"headers"; +static NSString *const kHippyNetworkRequestParaBody = @"body"; + +// Response parameter of fetch API +static NSString *const kHippyNetworkResponseStatusCode = @"statusCode"; +static NSString *const kHippyNetworkResponseStatusLine = @"statusLine"; +static NSString *const kHippyNetworkResponseHeaders = @"respHeaders"; +static NSString *const kHippyNetworkResponseBody = @"respBody"; + +// Duration parameter in resp.header of fetch API +static NSString *const kHippyNetworkRequestDuration = @"Hippy-Request-Duration"; + + static NSStringEncoding GetStringEncodingFromURLResponse(NSURLResponse *response) { NSString *textEncoding = [response textEncodingName]; if (!textEncoding) { @@ -45,35 +61,51 @@ @implementation HippyNetWork HIPPY_EXPORT_MODULE(network) -HIPPY_EXPORT_METHOD(fetch:(NSDictionary *)params resolver:(__unused HippyPromiseResolveBlock)resolve rejecter:(__unused HippyPromiseRejectBlock)reject) { +HIPPY_EXPORT_METHOD(fetch:(NSDictionary *)params + resolver:(HippyPromiseResolveBlock)resolve + rejecter:(HippyPromiseRejectBlock)reject) { if (!resolve) { return; } - NSString *method = params[@"method"]; - NSString *url = params[@"url"]; - NSDictionary *header = params[@"headers"]; - NSString *body = params[@"body"]; - - HippyAssertParam(url); - HippyAssertParam(method); + + NSString *url = params[kHippyNetworkRequestParaURL]; + NSString *method = params[kHippyNetworkRequestParaMethod]; + NSDictionary *header = params[kHippyNetworkRequestParaHeaders]; + NSString *body = params[kHippyNetworkRequestParaBody]; + + if (!url) { + HippyAssertParam(url); + if (reject) { + reject(@"invalid_params", @"URL is missing", nil); + } + return; + } NSMutableDictionary *vfsParams = [NSMutableDictionary new]; - [header enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, __unused BOOL *stop) { + [header enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *stop) { NSString *value = nil; - if ([obj isKindOfClass: [NSArray class]]) { + if ([obj isKindOfClass:[NSArray class]]) { value = [[(NSArray *)obj valueForKey:@"description"] componentsJoinedByString:@","]; - } else if ([obj isKindOfClass: [NSString class]]) { + } else if ([obj isKindOfClass:[NSString class]]) { value = obj; } - [vfsParams setValue: value forKey: key]; + if (value) { + [vfsParams setValue:value forKey:key]; + } }]; + NSData *data = nil; if (body) { data = [body dataUsingEncoding:NSUTF8StringEncoding]; } + + // Record request start time + CFTimeInterval startTime = CACurrentMediaTime(); + + // Send Request [self.bridge loadContentsAsynchronouslyFromUrl:url - method:method?:@"Get" + method:method ?: @"GET" params:vfsParams body:data queue:nil @@ -83,19 +115,33 @@ @implementation HippyNetWork NSStringEncoding encoding = GetStringEncodingFromURLResponse(response); NSString *dataStr = [[NSString alloc] initWithData:data encoding:encoding]; NSUInteger statusCode = 0; - NSDictionary *headers = nil; + NSMutableDictionary *headers = [NSMutableDictionary dictionary]; + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { NSHTTPURLResponse *httpRes = (NSHTTPURLResponse *)response; statusCode = [httpRes statusCode]; - headers = [httpRes allHeaderFields]; + [headers addEntriesFromDictionary:[httpRes allHeaderFields]]; } - NSDictionary *result = - @{ @"statusCode": @(statusCode), @"statusLine": @"", @"respHeaders": headers ?: @ {}, @"respBody": dataStr ?: @"" }; + + // Get request duration,in ms. + // and add to resp headers. + CFTimeInterval requestDuration = (CACurrentMediaTime() - startTime) * 1000; + [headers addEntriesFromDictionary:@{ kHippyNetworkRequestDuration : @(requestDuration).stringValue }]; + + NSDictionary *result = @{ + kHippyNetworkResponseStatusCode : @(statusCode), + kHippyNetworkResponseStatusLine : @"", + kHippyNetworkResponseHeaders : headers ?: @{}, + kHippyNetworkResponseBody : dataStr ?: @"" + }; + resolve(result); }]; } -HIPPY_EXPORT_METHOD(getCookie:(NSString *)urlString resolver:(HippyPromiseResolveBlock)resolve rejecter:(__unused HippyPromiseRejectBlock)reject) { +HIPPY_EXPORT_METHOD(getCookie:(NSString *)urlString + resolver:(HippyPromiseResolveBlock)resolve + rejecter:(__unused HippyPromiseRejectBlock)reject) { NSData *uriData = [urlString dataUsingEncoding:NSUTF8StringEncoding]; if (nil == uriData) { resolve(@"");