Add support for ontimeout and onerror handler when using XMLHttpRequest for Android and iOS

Summary:Currently React-Native does not have `ontimeout` and `onerror` handlers for [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest). This is an extension to [No timeout on XMLHttpRequest](https://github.com/facebook/react-native/issues/4648).

With addition to two handlers, both Android and iOS can now handle `ontimeout` if request times out and `onerror` when there is general network error.

**Test plan**

Code has been tested on both Android and iOS with [Charles](https://www.charlesproxy.com/) by setting a breakpoint on the request which fires `ontimeout` when the request waits beyond `timeout` time and `onerror` when there is network error.

**Usage**

JavaScript -

```
var request = new XMLHttpRequest();

function onLoad() {
    console.log(request.status);
};

function onTimeout() {
    console.log('Timeout');
};

function onError() {
    console.log('General network error');
};

request.onload = onLoad;
request.ontimeout = onTimeout;
request.onerr
Closes https://github.com/facebook/react-native/pull/6841

Differential Revision: D3178859

Pulled By: lexs

fb-gh-sync-id: 30674570653e92ab5f7e74bd925dd5640fc862b6
fbshipit-source-id: 30674570653e92ab5f7e74bd925dd5640fc862b6
This commit is contained in:
grgmo
2016-04-15 05:16:53 -07:00
committed by Facebook Github Bot 9
parent 967dbd0cbe
commit d09cd62011
8 changed files with 248 additions and 20 deletions

View File

@@ -15,6 +15,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.SocketTimeoutException;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -172,7 +174,7 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
Headers requestHeaders = extractHeaders(headers, data);
if (requestHeaders == null) {
onRequestError(executorToken, requestId, "Unrecognized headers format");
onRequestError(executorToken, requestId, "Unrecognized headers format", null);
return;
}
String contentType = requestHeaders.get(CONTENT_TYPE_HEADER_NAME);
@@ -186,7 +188,8 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
onRequestError(
executorToken,
requestId,
"Payload is set but no content-type header specified");
"Payload is set but no content-type header specified",
null);
return;
}
String body = data.getString(REQUEST_BODY_KEY_STRING);
@@ -194,7 +197,7 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
if (RequestBodyUtil.isGzipEncoding(contentEncoding)) {
RequestBody requestBody = RequestBodyUtil.createGzip(contentMediaType, body);
if (requestBody == null) {
onRequestError(executorToken, requestId, "Failed to gzip request body");
onRequestError(executorToken, requestId, "Failed to gzip request body", null);
return;
}
requestBuilder.method(method, requestBody);
@@ -206,14 +209,15 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
onRequestError(
executorToken,
requestId,
"Payload is set but no content-type header specified");
"Payload is set but no content-type header specified",
null);
return;
}
String uri = data.getString(REQUEST_BODY_KEY_URI);
InputStream fileInputStream =
RequestBodyUtil.getFileInputStream(getReactApplicationContext(), uri);
if (fileInputStream == null) {
onRequestError(executorToken, requestId, "Could not retrieve file for uri " + uri);
onRequestError(executorToken, requestId, "Could not retrieve file for uri " + uri, null);
return;
}
requestBuilder.method(
@@ -242,7 +246,7 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
if (mShuttingDown) {
return;
}
onRequestError(executorToken, requestId, e.getMessage());
onRequestError(executorToken, requestId, e.getMessage(), e);
}
@Override
@@ -264,7 +268,7 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
onRequestSuccess(executorToken, requestId);
}
} catch (IOException e) {
onRequestError(executorToken, requestId, e.getMessage());
onRequestError(executorToken, requestId, e.getMessage(), e);
}
}
});
@@ -294,11 +298,15 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
getEventEmitter(ExecutorToken).emit("didReceiveNetworkData", args);
}
private void onRequestError(ExecutorToken ExecutorToken, int requestId, String error) {
private void onRequestError(ExecutorToken ExecutorToken, int requestId, String error, IOException e) {
WritableArray args = Arguments.createArray();
args.pushInt(requestId);
args.pushString(error);
if ((e != null) && (e.getClass() == SocketTimeoutException.class)) {
args.pushBoolean(true); // last argument is a time out boolean
}
getEventEmitter(ExecutorToken).emit("didCompleteNetworkResponse", args);
}
@@ -385,7 +393,8 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
onRequestError(
ExecutorToken,
requestId,
"Missing or invalid header format for FormData part.");
"Missing or invalid header format for FormData part.",
null);
return null;
}
MediaType partContentType = null;
@@ -405,7 +414,8 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
onRequestError(
ExecutorToken,
requestId,
"Binary FormData part needs a content-type header.");
"Binary FormData part needs a content-type header.",
null);
return null;
}
String fileContentUriStr = bodyPart.getString(REQUEST_BODY_KEY_URI);
@@ -415,12 +425,13 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
onRequestError(
ExecutorToken,
requestId,
"Could not retrieve file for uri " + fileContentUriStr);
"Could not retrieve file for uri " + fileContentUriStr,
null);
return null;
}
multipartBuilder.addPart(headers, RequestBodyUtil.create(partContentType, fileInputStream));
} else {
onRequestError(ExecutorToken, requestId, "Unrecognized FormData part.");
onRequestError(ExecutorToken, requestId, "Unrecognized FormData part.", null);
}
}
return multipartBuilder;
@@ -464,4 +475,4 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
return getReactApplicationContext()
.getJSModule(ExecutorToken, DeviceEventManagerModule.RCTDeviceEventEmitter.class);
}
}
}