Skip to content

Commit

Permalink
Get response url from XMLHttpRequest
Browse files Browse the repository at this point in the history
Summary:
An HTTP request may be redirected to another URL, sometimes we need to know the URL where the  response comes from.

If the server is in control, we can add an HTTP header X-Request-URL for the redirect URL. However there will be cases that 3rd party services are used.

This PR retrieves the response URL from native networking module and passes to it XMLHttpRequest. The fetch API built on XMLHttpRequest also benefits from this feature.
Closes #4981

Reviewed By: svcscm

Differential Revision: D2811392

Pulled By: lexs

fb-gh-sync-id: 3ec356fb92f8011b6a243d6879172877a3dc498a
  • Loading branch information
realaboo authored and facebook-github-bot-3 committed Jan 7, 2016
1 parent 473f9bc commit b659d7f
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 67 deletions.
6 changes: 6 additions & 0 deletions Examples/UIExplorer/XHRExample.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var {

var XHRExampleHeaders = require('./XHRExampleHeaders');
var XHRExampleCookies = require('./XHRExampleCookies');
var XHRExampleFetch = require('./XHRExampleFetch');


// TODO t7093728 This is a simplified XHRExample.ios.js.
Expand Down Expand Up @@ -281,6 +282,11 @@ exports.examples = [{
render() {
return <FormUploader/>;
}
}, {
title: 'Fetch Test',
render() {
return <XHRExampleFetch/>;
}
}, {
title: 'Headers',
render() {
Expand Down
68 changes: 3 additions & 65 deletions Examples/UIExplorer/XHRExample.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var {
} = React;

var XHRExampleHeaders = require('./XHRExampleHeaders');
var XHRExampleFetch = require('./XHRExampleFetch');

class Downloader extends React.Component {

Expand Down Expand Up @@ -304,54 +305,6 @@ class FormUploader extends React.Component {
}
}

class FetchTest extends React.Component {

constructor(props) {
super(props);
this.state = {
responseText: null,
};
}

submit(uri: String) {
fetch(uri).then((response) => {
return response.text();
}).then((body) => {
this.setState({responseText: body});
});
}

render() {

var response = this.state.responseText ? (
<View style={{marginTop: 10}}>
<Text style={styles.label}>Server response:</Text>
<TextInput
editable={false}
multiline={true}
defaultValue={this.state.responseText}
style={styles.textOutput}
/>
</View>
) : null;

return (
<View>
<Text style={styles.label}>Edit URL to submit:</Text>
<TextInput
returnKeyType="go"
defaultValue="http://www.posttestserver.com/post.php"
onSubmitEditing={(event)=> {
this.submit(event.nativeEvent.text);
}}
style={styles.textInput}
/>
{response}
</View>
);
}
}

exports.framework = 'React';
exports.title = 'XMLHttpRequest';
exports.description = 'XMLHttpRequest';
Expand All @@ -366,9 +319,9 @@ exports.examples = [{
return <FormUploader/>;
}
}, {
title: 'fetch test',
title: 'Fetch Test',
render() {
return <FetchTest/>;
return <XHRExampleFetch/>;
}
}, {
title: 'Headers',
Expand Down Expand Up @@ -432,19 +385,4 @@ var styles = StyleSheet.create({
fontSize: 16,
fontWeight: '500',
},
label: {
flex: 1,
color: '#aaa',
fontWeight: '500',
height: 20,
},
textOutput: {
flex: 1,
fontSize: 17,
borderRadius: 3,
borderColor: 'grey',
borderWidth: 1,
height: 200,
paddingLeft: 8,
},
});
112 changes: 112 additions & 0 deletions Examples/UIExplorer/XHRExampleFetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* 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.
*
* @flow
*/
'use strict';

var React = require('react-native');
var {
StyleSheet,
Text,
TextInput,
View,
Platform,
} = React;


class XHRExampleFetch extends React.Component {

constructor(props: any) {
super(props);
this.state = {
responseText: null,
};
this.responseURL = null;
}

submit(uri: String) {
fetch(uri).then((response) => {
this.responseURL = response.url;
return response.text();
}).then((body) => {
this.setState({responseText: body});
});
}

render() {

var responseURL = this.responseURL ? (
<View style={{marginTop: 10}}>
<Text style={styles.label}>Server response URL:</Text>
<Text>{this.responseURL}</Text>
</View>
) : null;

var response = this.state.responseText ? (
<View style={{marginTop: 10}}>
<Text style={styles.label}>Server response:</Text>
<TextInput
editable={false}
multiline={true}
defaultValue={this.state.responseText}
style={styles.textOutput}
/>
</View>
) : null;

return (
<View>
<Text style={styles.label}>Edit URL to submit:</Text>
<TextInput
returnKeyType="go"
defaultValue="http://www.posttestserver.com/post.php"
onSubmitEditing={(event)=> {
this.submit(event.nativeEvent.text);
}}
style={styles.textInput}
/>
{responseURL}
{response}
</View>
);
}
}

var styles = StyleSheet.create({
textInput: {
flex: 1,
borderRadius: 3,
borderColor: 'grey',
borderWidth: 1,
height: Platform.OS === 'android' ? 44 : 30,
paddingLeft: 8,
},
label: {
flex: 1,
color: '#aaa',
fontWeight: '500',
height: 20,
},
textOutput: {
flex: 1,
fontSize: 17,
borderRadius: 3,
borderColor: 'grey',
borderWidth: 1,
height: 200,
paddingLeft: 8,
},
});

module.exports = XHRExampleFetch;
3 changes: 2 additions & 1 deletion Libraries/Network/RCTNetworking.m
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,8 @@ - (void)sendRequest:(NSURLRequest *)request
headers = response.MIMEType ? @{@"Content-Type": response.MIMEType} : @{};
status = 200;
}
NSArray<id> *responseJSON = @[task.requestID, @(status), headers];
id responseURL = response.URL ? response.URL.absoluteString : [NSNull null];
NSArray<id> *responseJSON = @[task.requestID, @(status), headers, responseURL];
[_bridge.eventDispatcher sendDeviceEventWithName:@"didReceiveNetworkResponse"
body:responseJSON];
});
Expand Down
9 changes: 8 additions & 1 deletion Libraries/Network/XMLHttpRequestBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class XMLHttpRequestBase {
responseHeaders: ?Object;
responseText: ?string;
status: number;
responseURL: ?string;

upload: ?{
onprogress?: (event: Object) => void;
Expand Down Expand Up @@ -69,6 +70,7 @@ class XMLHttpRequestBase {
this.responseHeaders = undefined;
this.responseText = '';
this.status = 0;
delete this.responseURL;

this._requestId = null;

Expand Down Expand Up @@ -110,11 +112,16 @@ class XMLHttpRequestBase {
}
}

_didReceiveResponse(requestId: number, status: number, responseHeaders: ?Object): void {
_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;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ private void onResponseReceived(int requestId, Response response) {
args.pushInt(requestId);
args.pushInt(response.code());
args.pushMap(headers);
args.pushString(response.request().urlString());

getEventEmitter().emit("didReceiveNetworkResponse", args);
}
Expand Down

0 comments on commit b659d7f

Please sign in to comment.