diff --git a/Examples/UIExplorer/XHRExample.android.js b/Examples/UIExplorer/XHRExample.android.js
index 151ce5f41..3696a2bfb 100644
--- a/Examples/UIExplorer/XHRExample.android.js
+++ b/Examples/UIExplorer/XHRExample.android.js
@@ -25,6 +25,8 @@ var {
View,
} = React;
+var XHRExampleHeaders = require('./XHRExampleHeaders');
+
// TODO t7093728 This is a simlified XHRExample.ios.js.
// Once we have Camera roll, Toast, Intent (for opening URLs)
// we should make this consistent with iOS.
@@ -259,7 +261,6 @@ class FormUploader extends React.Component {
}
}
-
exports.framework = 'React';
exports.title = 'XMLHttpRequest';
exports.description = 'Example that demonstrates upload and download requests ' +
@@ -274,6 +275,11 @@ exports.examples = [{
render() {
return ;
}
+}, {
+ title: 'Headers',
+ render() {
+ return ;
+ }
}];
var styles = StyleSheet.create({
diff --git a/Examples/UIExplorer/XHRExample.ios.js b/Examples/UIExplorer/XHRExample.ios.js
index 57f7fc31e..cc83ccf58 100644
--- a/Examples/UIExplorer/XHRExample.ios.js
+++ b/Examples/UIExplorer/XHRExample.ios.js
@@ -30,6 +30,8 @@ var {
View,
} = React;
+var XHRExampleHeaders = require('./XHRExampleHeaders');
+
class Downloader extends React.Component {
xhr: XMLHttpRequest;
@@ -368,6 +370,11 @@ exports.examples = [{
render() {
return ;
}
+}, {
+ title: 'Headers',
+ render() {
+ return ;
+ }
}];
var styles = StyleSheet.create({
diff --git a/Examples/UIExplorer/XHRExampleHeaders.js b/Examples/UIExplorer/XHRExampleHeaders.js
new file mode 100644
index 000000000..e9f5e2c4c
--- /dev/null
+++ b/Examples/UIExplorer/XHRExampleHeaders.js
@@ -0,0 +1,116 @@
+/**
+ * The examples provided by Facebook are for non-commercial testing and
+ * evaluation purposes only.
+ *
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+'use strict';
+
+var React = require('react-native');
+var {
+ StyleSheet,
+ Text,
+ TouchableHighlight,
+ View,
+} = React;
+
+class XHRExampleHeaders extends React.Component {
+
+ xhr: XMLHttpRequest;
+ cancelled: boolean;
+
+ constructor(props) {
+ super(props);
+ this.cancelled = false;
+ this.state = {
+ status: '',
+ headers: '',
+ contentSize: 1,
+ downloaded: 0,
+ };
+ }
+
+ download() {
+ this.xhr && this.xhr.abort();
+
+ var xhr = this.xhr || new XMLHttpRequest();
+ xhr.onreadystatechange = () => {
+ if (xhr.readyState === xhr.DONE) {
+ if (this.cancelled) {
+ this.cancelled = false;
+ return;
+ }
+ if (xhr.status === 200) {
+ this.setState({
+ status: 'Download complete!',
+ headers: xhr.getAllResponseHeaders()
+ });
+ } else if (xhr.status !== 0) {
+ this.setState({
+ status: 'Error: Server returned HTTP status of ' + xhr.status + ' ' + xhr.responseText,
+ });
+ } else {
+ this.setState({
+ status: 'Error: ' + xhr.responseText,
+ });
+ }
+ }
+ };
+ xhr.open('GET', 'https://httpbin.org/response-headers?header1=value&header2=value1&header2=value2');
+ xhr.send();
+ this.xhr = xhr;
+
+ this.setState({status: 'Downloading...'});
+ }
+
+ componentWillUnmount() {
+ this.cancelled = true;
+ this.xhr && this.xhr.abort();
+ }
+
+ render() {
+ var button = this.state.status === 'Downloading...' ? (
+
+
+ ...
+
+
+ ) : (
+
+
+ Get headers
+
+
+ );
+
+ return (
+
+ {button}
+ {this.state.headers}
+
+ );
+ }
+}
+
+var styles = StyleSheet.create({
+ wrapper: {
+ borderRadius: 5,
+ marginBottom: 5,
+ },
+ button: {
+ backgroundColor: '#eeeeee',
+ padding: 8,
+ },
+});
+
+module.exports = XHRExampleHeaders;
\ No newline at end of file
diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java
index 432adf90b..3ebf4cd06 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java
@@ -14,6 +14,7 @@ import javax.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;
+import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.GuardedAsyncTask;
import com.facebook.react.bridge.ReactApplicationContext;
@@ -21,7 +22,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.modules.network.OkHttpClientProvider;
+import com.facebook.react.bridge.WritableMap;
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.MediaType;
@@ -184,7 +185,6 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
if (mShuttingDown) {
return;
}
- // TODO(5472580) handle headers properly
String responseBody;
try {
responseBody = response.body().string();
@@ -193,7 +193,22 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
callback.invoke(0, null, e.getMessage());
return;
}
- callback.invoke(response.code(), null, responseBody);
+
+ WritableMap responseHeaders = Arguments.createMap();
+ Headers headers = response.headers();
+ for (int i = 0; i < headers.size(); i++) {
+ String headerName = headers.name(i);
+ // multiple values for the same header
+ if (responseHeaders.hasKey(headerName)) {
+ responseHeaders.putString(
+ headerName,
+ responseHeaders.getString(headerName) + ", " + headers.value(i));
+ } else {
+ responseHeaders.putString(headerName, headers.value(i));
+ }
+ }
+
+ callback.invoke(response.code(), responseHeaders, responseBody);
}
});
}