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); } }); }