React Native WS

 

Real-time chat with Websockets

 

Zak Burki

23-03-2017

The Product

Real Time Chat for Networking at Conferences. Join conference channels. View people. Then 1-to-1 chat to them.

http://near.pro

The Brief

  • Component UI Polish and Smoothness
  • Real-Time Messaging (1-to-1 initially)
  • Robust enough to support say approx 1,000 simultaneous chats. 
  • As Cheap as Possible. (Ruddy Clients)

 

Real-Time Options

XMPP Bosh

Websockets

MQTT

CoAP

 

Protocols

React Native

Node.js WS

Options

sockets.io

sails.js (node.js framework)

nodejs-websocket

websocket

express-ws

ws

 

General Design Thought

User

A

User

B

ChatRoom.

Conversation

ID

If User A sends a message in the chat room, and User B isn't connected - then send a Push notification instead.

Websocket Connection

Websocket Connection

Open a new websocket connection based on entry into conversation (chat-room) only.

Websocket Endpoint

ws endpoints instead of http

For example - ws://api.product.com/:conversationId?accessToken=MA_73XP87WG^oH[*bW12TpRXU;t$JD

var wsServer = require('./ws_routes/chat')(app,server,cookieParser,client);

config.ws + config.host + '/' + this.state.chatroomId + '?accessToken=' + this.state.token

RN Code:

Node Code:

Text

RN Websocket Client Code

this.ws.onmessage = (e) => {
	// a message was received
	console.log('Received: ' + e.data);
	if (e.data != 'ping') {
		this.setState({
			messages: this.state.messages.concat([JSON.parse(e.data)])
		});
		this.updateListChats();
	}
};

updateListChats: function() {
...
	// get current chatlist
	var chatList = this.props.tabBar.mountedComponents["chats"].state.chats;
	var found = false;
	// map through the collections
	chatList.map(function(conversation) {
		// if the current iterated conversation is the one you're in, update its last message with the newly               sent/received one
		if (conversation._id === $this.state.chatroomId) {
			found = true;
			conversation.lastMessage = $this.state.messages[$this.state.messages.length - 1];
		}
		return conversation;
	});
...
}

Websocket Server Code

ws.on('message', function incoming(message) {
      try {
        var data = JSON.parse(message);
        data['senderId'] = userData.userId;
        console.log('Received Message: ', data); // JUST LOG THE MESSAGE ON THE SERVER
        if (!data.senderId) {
          console.log('Websocket Sending Error : no data.senderId');
          return ws.send(JSON.stringify({
            err: 'Sender is required'
          }), function ack(wsError) {
            if (wsError) console.log('Websocket Sending Error : no data.senderId', wsError);
          });
        }
        var messageData = {
          sender: data.senderId, // SHOULD BE FROM THE SESSION
          message: data.message
        } ...

Push Notifications

if (!isOnChatroom) {
  // PUSH NOTIF HERE ON RECEIVER ONLY
  userModel.getDeviceToken(receiverId, function(err, res) {
    if (err) console.log('Cannot Find User');
    if (!res) console.log('Cannot Find User');
    //console.log('Receiver Token: ', results);
    if (res.token) {
      apnService.pushMessage(res.token, userData.name, 
        chatroomId, results, userData, function() {
        console.log('Notification Sent');
      });
    }
  });
}

Check to see if other person (recipient of broadcast) is in the room and has a ws connection...if not..send them a push notification.

Security Issues

Websocket Protocol is not as mature as HTTP and the security measures are not as robust.

  • WSS (SSL)
  • Avoid Tunnelling
  • Validate Client/Server Input
  • Authentication/Authorization
  • Origin Header

Check out Heroku's Websocket Security Blog for the implementation details.

Next?

Extending it for Group Chat.

exports.leaveGroup = function(req,res,next){
  var userId = JSON.parse(req.session.userSession).userId;
  var input = req.body;
  if(!input.groupId){
    return res.status(400).send('Group id is required');
  }
  Group.update({_id:input.groupId},{$pull:{"members" : {"id": userId}}},function(err,g){
    if(err) return res.status(400).send(err);
    res.sendStatus(200);
  });
}

More work required on the chat broadcasting logic within a group, but keep simple!

The End

Twitter: @zakburki

React Native Websockets

By Zak Burki

React Native Websockets

Code Share Mar 2017. Kennedy Town, Hong Kong.

  • 3,516