Closed ph-teven closed 5 years ago
I am having this same problem.
I think it may have to do with the serializer, but i'm not sure. When I looked at #82 for an example and added
serializer: new JsonSerializer(),
I get this error on crossbar 19.3.5:
"abort": false, "reason": "duplicate protocol 'wamp.2.json' specified in HTTP Sec-WebSocket-Protocol header", "log_time": 1557273573.8720865, "level": "warn", "namespace": "crossbar.router.protocol.WampWebSocketServerProtocol", "text": "dropping connection to peer tcp4:XXX.XXX.XXX.XXX:PPPPP with abort=False: duplicate protocol 'wamp.2.json' specified in HTTP Sec-WebSocket-Protocol header"
When i remove the serializer line it looks like the connection is made and I see the low level pings on the server, but I don't see any exchange. I don't see the onConnect
fired, when I user wampcra
I don't see the onChallenge
fire either.
I've tried with Websocket
and with w3cws
as in the example in #82
So, I got it to work after a lot of debugging. I'm not 100% what is going wrong with Crossbar or with the library, but here is my fix for @zaytsevfuu
The problem I found is that serverProtocol
on the line below is always undefined
https://github.com/KSDaemon/wampy.js/blob/0a962f2130d60a2a7faacf4d49700ab4fbb20878/src/wampy.js#L569
which causes this to always fail
https://github.com/KSDaemon/wampy.js/blob/0a962f2130d60a2a7faacf4d49700ab4fbb20878/src/wampy.js#L582
and it aborts the hello and joins.
My current hack to get the library working is to serverProtocol = 'json'
on line 569
After that change it speaks to crossbar just fine. I'm pretty sure there is a deeper issue at heart here, but I don't understand this library well enough to create a pull.
Hi all, guys! First of all, thanks for trying to figure it out. I'm not a react native expert. But it seems to me, that problem is lying somewhere on websocket communication level. As described in RFC 6455:
The client can request that the server use a specific subprotocol by including the |Sec-WebSocket-Protocol| field in its handshake. If it is specified, the server needs to include the same field and one of the selected subprotocol values in its response for the connection to be established.
Wampy sends Sec-WebSocket-Protocol: wamp.2.json
. Or to be more correct, react native environment websocket object must send Sec-WebSocket-Protocol: wamp.2.json
header. W3cws implementation works right.
We need to inspect network communication between client and crossbar and figure out what is sending in both directions. I'll try to reproduce it.
@zaytsevfuu Can you help me with starting up test you provided? First, you forget about installing android-sdk and missed some other steps:
brew cask install android-sdk
export ANDROID_HOME="/usr/local/share/android-sdk"
sdkmanager --licenses # accept license agreements
After this i've got next errors:
kostik@GrayWolf:~/Projects/wampytest/ {master|…1}!> node node_modules/react-native/local-cli/cli.js run-android [11:35] cmd#6460
info JS server already running.
info Building and installing the app on the device (cd android && ./gradlew app:installDebug)...
> Configure project :app
Checking the license for package Android SDK Build-Tools 28.0.3 in /usr/local/share/android-sdk/licenses
License for package Android SDK Build-Tools 28.0.3 accepted.
Preparing "Install Android SDK Build-Tools 28.0.3 (revision: 28.0.3)".
"Install Android SDK Build-Tools 28.0.3 (revision: 28.0.3)" ready.
Installing Android SDK Build-Tools 28.0.3 in /usr/local/share/android-sdk/build-tools/28.0.3
"Install Android SDK Build-Tools 28.0.3 (revision: 28.0.3)" complete.
"Install Android SDK Build-Tools 28.0.3 (revision: 28.0.3)" finished.
Checking the license for package Android SDK Platform 28 in /usr/local/share/android-sdk/licenses
License for package Android SDK Platform 28 accepted.
Preparing "Install Android SDK Platform 28 (revision: 6)".
"Install Android SDK Platform 28 (revision: 6)" ready.
Installing Android SDK Platform 28 in /usr/local/share/android-sdk/platforms/android-28
"Install Android SDK Platform 28 (revision: 6)" complete.
"Install Android SDK Platform 28 (revision: 6)" finished.
Checking the license for package Android SDK Platform-Tools in /usr/local/share/android-sdk/licenses
License for package Android SDK Platform-Tools accepted.
Preparing "Install Android SDK Platform-Tools (revision: 28.0.3)".
"Install Android SDK Platform-Tools (revision: 28.0.3)" ready.
Installing Android SDK Platform-Tools in /usr/local/share/android-sdk/platform-tools
"Install Android SDK Platform-Tools (revision: 28.0.3)" complete.
"Install Android SDK Platform-Tools (revision: 28.0.3)" finished.
/Users/kostik/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.12.1/dc6d02e4e68514eff5631963e28ca7742ac69efe/okhttp-3.12.1.jar: D8: Type `org.conscrypt.Conscrypt` was not found, it is required for default or static interface methods desugaring of `java.security.Provider okhttp3.internal.platform.ConscryptPlatform.getProvider()`
[adb]: * daemon not running; starting now at tcp:5037
[adb]: * daemon started successfully
> Task :app:installDebug FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:installDebug'.
> com.android.builder.testing.api.DeviceException: No connected devices!
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 1m 16s
26 actionable tasks: 26 executed
error Could not install the app on the device, read the error above for details.
Make sure you have an Android emulator running or a device connected and have
set up your Android development environment:
https://facebook.github.io/react-native/docs/getting-started.html
error Command failed: ./gradlew app:installDebug. Run CLI with --verbose flag for more details.
okhttp-3.12.1.jar: D8: Type org.conscrypt.Conscrypt
was not found, it is required for default or static interface methods desugaring of java.security.Provider okhttp3.internal.platform.ConscryptPlatform.getProvider()
@ephro Can you check what is returned from server after initializing ws connection. I mean this._ws.protocol
. It must contain string wamp.2.json
.
It works as expected with crossbar in node.js/browsers environments.
@ephro Wow amazing. Thanks for digging into this.
@KSDaemon
Hm i have never seen this error before. I recommend opening the android
directory with Android Studio. It is very good at fixing problems by installing missing sdks and build tools.
You can install the apk by running the project from Ancdroid Studio and then starten the JS bundler with npm run start
If you see a white screen on the emulator just re-run the project from Android studio.
Okay, @zaytsevfuu thanks! I'll try.
@KSDaemon The error you are getting is because you don't have an android virtual device created or an android phone connected with USB debugging enabled.
If you have Android Studio installed, these are the instructions to making a device.
https://developer.android.com/studio/run/managing-avds
I'll dig into the other question a little later today and try to get some network dumps to go along with it.
Looking at the wireshark logs I see the app send
origin: http://XXXXXXXXXX:PPPP
Sec-WebSocket-Protocol: wamp.2.json
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: KKKKKKKKKKKKK
Sec-WebSocket-Version: 13
Host: XXXXXXXXXXXX:PPPP
Accept-Encoding: gzip
User-Agent: okhttp/3.12.1
and the server replies with
Server: Crossbar
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Protocol: wamp.2.json
Sec-WebSocket-Accept: KKKKKKKKKKKKK
I redacted the addresses and keys.
In the wampy.js library I added a console dump of this._ws
right before the log line of [wampy] websocket connected
and see the following:
So there is no part of _ws that is getting set. That's what I ran into before and put in the quick hack to just make the library speak json
.
The image above is using ws: WebSocket
in the constructor
When I use w3cws = require('websocket').w3cwebsocket
like in some of your other examples and ws: w3cws
I get the exact same as below
I'm also just using non-encrypted sockets right now as I haven't gotten to the point of getting SSL to work, which seems like a fairly large headache with android.
Let me know if there is anything else I can do to help.
@ephro Oh, great!
Can you paste here or via gist full dump of this._ws
?
I'm trying to look into react-native sources and their ws implementation. And i see only one line in all repository, where server answered Sec-WebSocket-Protocol
is set up:
https://github.com/facebook/react-native/blob/8491cc36dda18589b81d28fe6af220eaf1767b3a/Libraries/WebSocket/RCTSRWebSocket.m#L403-412
https://github.com/facebook/react-native/search?q=_protocol&unscoped_q=_protocol
And i can't see were it is exposed.
Here is this._ws
using util.inspect
since it has circular references
{ CONNECTING: 0,
OPEN: 1,
CLOSING: 2,
CLOSED: 3,
readyState: 1,
_eventEmitter: { _subscriber: { _subscriptionsForType: [Object], _currentSubscription: null } },
_socketId: 1,
_subscriptions:
[ { subscriber: [Object],
emitter: [Object],
listener: [Function],
context: undefined,
eventType: 'websocketMessage',
key: 1 },
{ subscriber: [Object],
emitter: [Object],
listener: [Function],
context: undefined,
eventType: 'websocketOpen',
key: 1 },
{ subscriber: [Object],
emitter: [Object],
listener: [Function],
context: undefined,
eventType: 'websocketClosed',
key: 1 },
{ subscriber: [Object],
emitter: [Object],
listener: [Function],
context: undefined,
eventType: 'websocketFailed',
key: 1 } ] }
and with just a console.log
{CONNECTING: 0, OPEN: 1, CLOSING: 2, CLOSED: 3, readyState: 1, …}CLOSED: 3CLOSING: 2CONNECTING: 0OPEN: 1onclose: (...)onerror: (...)onmessage: (...)onopen: (...)readyState: 1_binaryType: "arraybuffer"_eventEmitter: NativeEventEmitter {_subscriber: EventSubscriptionVendor}_socketId: 1_subscriptions: (4) [EmitterSubscription, EmitterSubscription, EmitterSubscription, EmitterSubscription]binaryType: (...)Symbol(listeners): {open: {…}, close: {…}, message: {…}, error:
I'm not really sure why it's not showing everything i the first as it is in the second.
It's not as easy as one would hope to get the data out of react-native.
btw, I've installed Android Studio and have created emulator device and run it — but no luck. Same if i try to run an example with node node_modules/react-native/local-cli/cli.js run-ios
(other error)
Anyway, i think the problem lies in rn websocket implementation. Time to file a bug there. Here it is: https://github.com/facebook/react-native/issues/24796
Please, @zaytsevfuu @ephro Have a look there, so you can provide more info if required. Thanks!
@KSDaemon
Would you accept a change to the library where
https://github.com/KSDaemon/wampy.js/blob/0a962f2130d60a2a7faacf4d49700ab4fbb20878/src/wampy.js#L582
would be changed to
if (serverProtocol === 'json' || typeof this._ws.protocol === 'undefined') {
I know it's a little hacky, but it would fix our problems until the websocket library gets hopefully patched.
@ephro I was thinking about your proposal... at first glance, i thought it is bad in some cases, but going deeper, i think this should not break the current workflow (i hope) Can we somehow detect, that we are running in RN env? If it's possible that we can make a hack just for this case. I've found this https://github.com/facebook/react-native/pull/2120 seems what is needed.
@KSDaemon I did some more digging and I think the problem is just with react-native and android because of the OkHttp code. I don't have mac handy to test on IOS right now, however this test should only check for the undefined protocol in RN envs, while not affecting other ones. The code below would replace 582 from the post above.
if (serverProtocol === 'json'
|| (typeof navigator != 'undefined'
&& navigator.product == 'ReactNative'
&& typeof this._ws.protocol === 'undefined')) {
Apologies for the multi-line here, but I wanted to to fit in the post more easily.
I'm thinking...
I'm afraid this solution won't work if you use any other serializer, besides json.
For example, if you initialize wampy with msgpack serializer, then connect to crossbar (which supports different serializers), crossbar will accept wamp.2.msgpack, but wampy still will not be able to distinguish this answer and will choose json.
Well, anyway, this is still better than the original code and it doesn't break current behavior. So, i'll implement this.
I'll publish new release now, so you can test.
@KSDaemon I agree, it does break it if you use another serializer, but at least it connects. Without the protocols being exposed I'm not sure of any other way to handle it though. At least it connects with this change as ugly as it is.
I was trying to find out what the OkHttp android library does, but it's not well documented. I think it may do the negotiation for you, but i'm not really sure how to figure it out.
I will try to do some testing with the new release and sniff the packets to try to figure out what is happening and get back to you.
@ephro great! You're welcome!
Just letting you guys know. I sent a fix for react-native. I hope it can help you guys. https://github.com/facebook/react-native/pull/25273
@cabelitos Thats great! Looking forward for next release with ur MR included :)
Describe the bug Wampy is not connecting properly in react-native environment.
The
onConnect
callback is not called.Killing the crossbar service will trigger the
onClose
andonError
callbacks. As mentioned also here: https://github.com/KSDaemon/wampy.js/issues/82#issuecomment-405012228I also checked https://github.com/KSDaemon/wampy.js/issues/82 but could not find a working solution. Also the examples use
w3cws
as ws client.I made a stripped down version of crossbar.io without authentication and bundled it into a repo to reproduce.
To Reproduce Clone https://github.com/zaytsevfuu/wampytest
yarn install
node node_modules/react-native/local-cli/cli.js run-android
yarn run start
cd crossbar && ./start_wamp.sh
Expected behavior
onConnect
callback is calledScreenshots
Environment (please complete the following information):