Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 60 additions & 19 deletions Examples/UIExplorer/js/WebSocketExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ const {
Text,
TextInput,
TouchableOpacity,
ScrollView,
View,
} = ReactNative;

const DEFAULT_WS_URL = 'ws://localhost:5555/';
const DEFAULT_HTTP_URL = 'http://localhost:5556/';
const WS_EVENTS = [
'close',
'error',
Expand Down Expand Up @@ -95,6 +97,8 @@ function showValue(value) {

type State = {
url: string;
httpUrl: string;
fetchStatus: ?string;
socket: ?WebSocket;
socketState: ?number;
lastSocketEvent: ?string;
Expand All @@ -109,6 +113,8 @@ class WebSocketExample extends React.Component<any, any, State> {

state: State = {
url: DEFAULT_WS_URL,
httpUrl: DEFAULT_HTTP_URL,
fetchStatus: null,
socket: null,
socketState: null,
lastSocketEvent: null,
Expand Down Expand Up @@ -154,6 +160,19 @@ class WebSocketExample extends React.Component<any, any, State> {
this.setState({outgoingMessage: ''});
};

_sendHttp = () => {
this.setState({
fetchStatus: 'fetching',
});
fetch(this.state.httpUrl).then((response) => {
if (response.status >= 200 && response.status < 400) {
this.setState({
fetchStatus: 'OK',
});
}
});
};

_sendBinary = () => {
if (!this.state.socket ||
typeof ArrayBuffer === 'undefined' ||
Expand All @@ -176,16 +195,11 @@ class WebSocketExample extends React.Component<any, any, State> {
this.state.socket.readyState >= WebSocket.CLOSING;
const canSend = !!this.state.socket;
return (
<View style={styles.container}>
<ScrollView style={styles.container}>
<View style={styles.note}>
<Text>Pro tip:</Text>
<Text>To start the WS test server:</Text>
<Text style={styles.monospace}>
node Examples/UIExplorer/websocket_test_server.js
</Text>
<Text>
{' in the '}
<Text style={styles.monospace}>react-native</Text>
{' directory starts a test server.'}
./Examples/UIExplorer/js/websocket_test_server.js
</Text>
</View>
<Row
Expand All @@ -207,16 +221,18 @@ class WebSocketExample extends React.Component<any, any, State> {
onChangeText={(url) => this.setState({url})}
value={this.state.url}
/>
<Button
onPress={this._connect}
label="Connect"
disabled={!canConnect}
/>
<Button
onPress={this._disconnect}
label="Disconnect"
disabled={canConnect}
/>
<View style={styles.buttonRow}>
<Button
onPress={this._connect}
label="Connect"
disabled={!canConnect}
/>
<Button
onPress={this._disconnect}
label="Disconnect"
disabled={canConnect}
/>
</View>
<TextInput
style={styles.textInput}
autoCorrect={false}
Expand All @@ -236,7 +252,32 @@ class WebSocketExample extends React.Component<any, any, State> {
disabled={!canSend}
/>
</View>
</View>
<View style={styles.note}>
<Text>To start the HTTP test server:</Text>
<Text style={styles.monospace}>
./Examples/UIExplorer/http_test_server.js
</Text>
</View>
<TextInput
style={styles.textInput}
autoCorrect={false}
placeholder="HTTP URL..."
onChangeText={(httpUrl) => this.setState({httpUrl})}
value={this.state.httpUrl}
/>
<View style={styles.buttonRow}>
<Button
onPress={this._sendHttp}
label="Send HTTP request to set cookie"
disabled={this.state.fetchStatus === 'fetching'}
/>
</View>
<View style={styles.note}>
<Text>
{this.state.fetchStatus === 'OK' ? 'Done. Check your WS server console to see if the next WS requests include the cookie (should be "wstest=OK")' : '-'}
</Text>
</View>
</ScrollView>
);
}

Expand Down
43 changes: 43 additions & 0 deletions Examples/UIExplorer/js/http_test_server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env node

/**
* 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';

/* eslint-env node */

console.log(`\
Test server for WebSocketExample

This will set a cookie named "wstest" on the response of any incoming request.
`);

const connect = require('connect');
const http = require('http');

const app = connect();

app.use(function(req, res) {
console.log('received request');
const cookieOptions = {
//httpOnly: true, // the cookie is not accessible by the user (javascript,...)
secure: false, // allow HTTP
};
res.cookie('wstest', 'OK', cookieOptions);
res.end('Cookie has been set!\n');
});

http.createServer(app).listen(5556);
5 changes: 4 additions & 1 deletion Examples/UIExplorer/js/websocket_test_server.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#!/usr/bin/env node

/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
Expand Down Expand Up @@ -29,7 +31,7 @@ const WebSocket = require('ws');
console.log(`\
Test server for WebSocketExample

This will send each incoming message right back to the other side.a
This will send each incoming message right back to the other side.
Restart with the '--binary' command line flag to have it respond with an
ArrayBuffer instead of a string.
`);
Expand All @@ -39,6 +41,7 @@ const server = new WebSocket.Server({port: 5555});
server.on('connection', (ws) => {
ws.on('message', (message) => {
console.log('Received message:', message);
console.log('Cookie:', ws.upgradeReq.headers.cookie);
if (respondWithBinary) {
message = new Buffer(message);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ android_library(
react_native_target('java/com/facebook/react/common:common'),
react_native_target('java/com/facebook/react/module/annotations:annotations'),
react_native_target('java/com/facebook/react/modules/core:core'),
react_native_target('java/com/facebook/react/modules/network:network'),
],
visibility = [
'PUBLIC',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.facebook.react.common.ReactConstants;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.modules.network.ForwardingCookieHandler;

import okhttp3.OkHttpClient;
import okhttp3.Request;
Expand All @@ -43,6 +44,7 @@
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.concurrent.TimeUnit;

import okio.Buffer;
Expand All @@ -54,10 +56,12 @@ public class WebSocketModule extends ReactContextBaseJavaModule {
private final Map<Integer, WebSocket> mWebSocketConnections = new HashMap<>();

private ReactContext mReactContext;
private ForwardingCookieHandler mCookieHandler;

public WebSocketModule(ReactApplicationContext context) {
super(context);
mReactContext = context;
mCookieHandler = new ForwardingCookieHandler(context);
}

private void sendEvent(String eventName, WritableMap params) {
Expand Down Expand Up @@ -87,11 +91,16 @@ public void connect(
.tag(id)
.url(url);

String cookie = getCookie(url);
if (cookie != null) {
builder.addHeader("Cookie", cookie);
}

if (headers != null) {
ReadableMapKeySetIterator iterator = headers.keySetIterator();

if (!headers.hasKey("origin")) {
builder.addHeader("origin", setDefaultOrigin(url));
builder.addHeader("origin", getDefaultOrigin(url));
}

while (iterator.hasNextKey()) {
Expand All @@ -105,7 +114,7 @@ public void connect(
}
}
} else {
builder.addHeader("origin", setDefaultOrigin(url));
builder.addHeader("origin", getDefaultOrigin(url));
}

if (protocols != null && protocols.size() > 0) {
Expand Down Expand Up @@ -256,13 +265,13 @@ private void notifyWebSocketFailed(int id, String message) {
}

/**
* Set a default origin
* Get the default HTTP(S) origin for a specific WebSocket URI
*
* @param Websocket connection endpoint
* @return A string of the endpoint converted to HTTP protocol
* @param String uri
* @return A string of the endpoint converted to HTTP protocol (http[s]://host[:port])
*/

private static String setDefaultOrigin(String uri) {
private static String getDefaultOrigin(String uri) {
try {
String defaultOrigin;
String scheme = "";
Expand All @@ -285,8 +294,31 @@ private static String setDefaultOrigin(String uri) {
}

return defaultOrigin;

} catch (URISyntaxException e) {
throw new IllegalArgumentException("Unable to set " + uri + " as default origin header.");
throw new IllegalArgumentException("Unable to set " + uri + " as default origin header");
}
}

/**
* Get the cookie for a specific domain
*
* @param String uri
* @return The cookie header or null if none is set
*/
private String getCookie(String uri) {
try {
URI origin = new URI(getDefaultOrigin(uri));
Map<String, List<String>> cookieMap = mCookieHandler.get(origin, new HashMap());
List<String> cookieList = cookieMap.get("Cookie");

if (cookieList == null || cookieList.isEmpty()) {
return null;
}

return cookieList.get(0);
} catch (URISyntaxException | IOException e) {
throw new IllegalArgumentException("Unable to get cookie from " + uri);
}
}
}