To give you context, our app is a react-native web wrapper (something similar to what Cordova or ionic does).
The app downloads an updated version of the web bundle that is stored locally after the user opens the app, and then the WebView component uses it as a source to show the app to the user.
There are some parts of this web app that uses web sockets, so the issue is that considering its large traffic, the socket/exchange service has a bunch of nodes/replicas and in order to keep connections alive the load balancer uses Set-Cookie headers to make sure that the client is going to the right service node in every request
So, what we're trying is:
- Enable Set-Cookie behavior within a WebView
- Get Set-Cookie response header manually from a request that is happening within a WebView
Our react-native application looks like:
import React from 'react';
import {Platform, StyleSheet, View} from 'react-native';
import {WebView} from 'react-native-webview';
type WrapperProps = {
path: string;
};
function Wrapper({path}: WrapperProps) {
return (
<View style={styles.container}>
<WebView
allowFileAccess
allowUniversalAccessFromFileURLs
allowingReadAccessToURL={path}
javaScriptEnabled
originWhitelist={['*']}
sharedCookiesEnabled
source={{
uri:
Platform.OS === 'ios'
? `file://${path}/index.html`
: `${path}/index.html`,
}}
injectedJavaScript={`(function () {
window.ReactNativeWebView.postMessage(JSON.stringify(document.coookie));
var open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url) {
this.addEventListener("load", function () {
var responseHeaders = this.getAllResponseHeaders();
var setCookies = this.getResponseHeader("Set-Cookie"); // always return null
window.ReactNativeWebView.postMessage(JSON.stringify({ setCookies }));
window.ReactNativeWebView.postMessage(
JSON.stringify({ responseHeaders }) // return all headers except for Set-Cookie
);
});
open.apply(this, arguments);
};
})();
`}
onMessage={e => {
console.log({onMessageEvent: e.nativeEvent.data});
}}
style={styles.webview}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
webview: {
height: '100%',
width: '100%',
},
});
export default React.memo(Wrapper);
Do you have any clue how to solve this issue?
P.S: We already read How to set WKWebView cookie to accept Policy - But it's for swift.
CodePudding user response:
I managed to
Enable Set-Cookie behavior within a WebView
by establishing a socket connection using socket.io-client
, so the web socket requests within the WebView will already include cookies in request headers (which will keep connections alive).
Considering react-native-webview
uses WKWebView
on iOS, there is no way to access cookies in response headers. However, in this particular scenario you can create the socket connection from outside the web view, handle cookies (it seems socket.io-client
already does), and then they should be available in the requests within the WebView.
In order to setup socket io in your react-native app, you will need to
- Install
[email protected]
(I tried different versions but this was the only one that worked) - Assign
window.navigator.userAgent = 'react-native';
(since ES6 modules are hoisted, this assignment must not be done in the same file as react-native and socket io imports) - Establish your socket connection.
So it should look like:
// userAgent.js
window.navigator.userAgent = 'react-native';
// Wrapper.tsx
import React from 'react';
import {Platform, StyleSheet, View} from 'react-native';
import {WebView} from 'react-native-webview';
// user agent MUST be imported before socket io
import './userAgent';
import io from 'socket.io-client';
export const socket = io('https://your.host.com/', {
path: '/path/to/socket.io/',
jsonp: false,
});
type WrapperProps = {
path: string;
};
function Wrapper({path}: WrapperProps) {
return (
<View style={styles.container}>
<WebView
allowFileAccess
allowUniversalAccessFromFileURLs
allowingReadAccessToURL={path}
javaScriptEnabled
originWhitelist={['*']}
sharedCookiesEnabled
source={{
uri:
Platform.OS === 'ios'
? `file://${path}/index.html`
: `${path}/index.html`,
}}
style={styles.webview}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
webview: {
height: '100%',
width: '100%',
},
});
export default React.memo(Wrapper);