Use case: Android devices cannot contact Ruby On Rails server directly (it is behind firewall) and I cannot have a Internet server to receive requests directly. I need to have internal Rails server to retrieve messages from Android devices and instead of a pooling strategy, I've seen that GCM + XMPP would fit perfectly.
But, I could not find any info on how to code such scenario in Ruby/Rails.
How can I not only send push-notifications from my Ruby On Rails server, but also receive Upstream Messages from GCM (Google Cloud Messaging) connecting my Ruby On Rails server to Google CCS Servers?
for the Upstream, messages are originated from Android device, that is not the scope of the question
I know that I must use XMPP for that.
I know there are ruby gems for sending push-notifications from rails servers to Android devices, but all of them are HTTP based (at least, all that I've seen) and cannot do what I need
I know that Rails server would keep a connection opened (XMPP) to Google's CCS Servers (GCM Cloud Connection Server) to receive Upstream messages
I am aware of XMPP4r gem but cannot find code examples enough to integrate it with Rails
Any help is appreciated.
Log (or what I have done so far...)
17/12/15:
Trying to use this post as a starting point. I have created a project on google console and added a server API key, also enabled API Google Cloud Messaging for Android. I have tested with this code (based on this blog post) and, so far, it executes with no errors:
require 'stella_gcm_xmpp'
id = '[project_number]#gcm.googleapis.com'
password = [API_KEY]
gcm = StellaGcmXmpp.new(id, password, true, true)
gcm.connect
gcm.callback
Upstream Message test
Client Side (out of the scope of the question, for completeness sake):
$ ionic start gcm-test
$ ionic platform add android
$ ionic plugin install cordova-plugin-chrome-apps-gcm
$ ionic run android
open Chrome-Dev-Tools console:
> chrome.gcm.register( ['2195xxxxx718'], function(regId) { console.log('regId:' + regId); } )
> regId:APA91bG_5QIpVrBvuooVp7xO...KiVt3ozcf2HKIkHq_42UAPAU4w
> chrome.gcm.send( {destinationId: '2195xxxxx718#gcm.googleapis.com', messageId: '111', timeToLive: 10, data: {my: 'my message'} }, function(messageId) { console.log(messageId); } )
> 8
Server Side
check receive of upstream message on Ruby (irb) console:
D, [2015-12-18T10:09:05.664007 #4019] DEBUG -- : RECEIVED:
<message from='devices#gcm.googleapis.com' to='2195xxxxx718#gcm.googleapis.com' type='normal'><gcm xmlns='google:mobile:data'>{"data":{"my":"my message"},"time_to_live":86400,"from":"APA91bG_5QIpVrBvuooVp7xOos_EYzA4XNH0CeGzVudbJXxW4avE4NpZO84Q3mC2I-FKAGMTfFdGumSGmkUYViZVwp5gbbC38NDS4GWyaIsABJfhZd3J5KMJBLKgah6lC4LwkbLHKiVt3ozcf2HKIkHq_42UAPAU4w","message_id":"8","category":"com.ionicframework.gcmtest908063"}</gcm></message>
D, [2015-12-18T10:09:05.665623 #4019] DEBUG -- : PROCESSING:
<message from='devices#gcm.googleapis.com' to='2195xxxxx718#gcm.googleapis.com' type='normal' xmlns='jabber:client'><gcm xmlns='google:mobile:data'>{"data":{"my":"my message"},"time_to_live":86400,"from":"APA91bG_5QIpVrBvuooVp7xOos_EYzA4XNH0CeGzVudbJXxW4avE4NpZO84Q3mC2I-FKAGMTfFdGumSGmkUYViZVwp5gbbC38NDS4GWyaIsABJfhZd3J5KMJBLKgah6lC4LwkbLHKiVt3ozcf2HKIkHq_42UAPAU4w","message_id":"8","category":"com.ionicframework.gcmtest908063"}</gcm></message> (Jabber::Message)
D, [2015-12-18T10:09:05.665760 #4019] DEBUG -- : TRYING stanzacbs...
D, [2015-12-18T10:09:05.665859 #4019] DEBUG -- : TRYING message/iq/presence/cbs...
[2015-12-18 10:09:05] GCM send Failed id: 8 error:
*The 'GCM send Failed id: 8 error:' occurs not because of an error, but because message-type is empty*
Downstream Message test (out of the scope of the question, for completeness sake)
Client Side
chrome.gcm.onMessage.addListener(function(msg) { console.log('msg: ' + JSON.stringify(msg)) } )
Server Side
gcm.send 'APA91bG_5QIpVrBvuooVp7x...kHq_42UAPAU4w', '999', { msg: 'teste' }
Client Side
msg: {"data":{"msg":"teste"}}
CCS (GCM's XMPP server) would sit in between your Android clients and your ruby server. Once your ruby server can establish an XMPP connection to CCS then there is nothing special that has to occur to deliver messages from client to server and vice versa. The ruby server would be responsible for sending and receiving messages to and from CCS.
Related
Background and Setup
I have a Google Cloud Platform (GCP) Pub/Sub topic called "test", for which I've published a few messages from the GCP Cloud Console. I'm using a Ruby on Rails app to subscribe to this topic. From the Receiving Messages section in their OVERVIEW.md documentation, I've placed that code snippet (reproduced here with brevity) in an initializer file config/initializers/pubsub.rb
require "google/cloud/pubsub"
pubsub = Google::Cloud::PubSub.new
sub = pubsub.subscription "test"
subscriber = sub.listen threads: { callback: 16 } do |received_message|
puts "Data: #{received_message.message.data}, published at #{received_message.message.published_at}"
received_message.acknowledge!
end
subscriber.start
sleep
To test that the app can receive messages, I start Rails Console and confirm that the message has arrived. Eureka!
Data: test message I typed from GCP Cloud Console, published at 2021-04-30 16:36:42 -0400
The Problem
Rails Console cannot receive any input. By starting the subscription listener in an initializer, I've effectively rendered Rails Console inoperable.
How can I start the subscription listener in the app without breaking the rest of the app (what is the Rails way for this scenario)?
We are trying out Kurento 6.0 + Java Spring Client. The Examples works well (one2one call + one2one-recording). We are trying to implement the same functionality on an IOS app so that we can do Peer (IOS) -> Peer (Web) calls. But unfortunately - the documentation is not very clear.
The Kurento Server and Java Spring Boot application are deployed to an AWS ec2 instance and stun servers are configured.
We are using the call https://kurento-IP:8443/call with json to register:
var message = {
id : 'register',
name : name
};
ws.send(message)
And it works!
Question:
How can we now initiate a call in IOS after that?
Should the iOS be communicating to the Spring App (https://kurento-IP:8443/call) or directly to ws://kurento-ip:8888/kurento (We guess should be both?)
On the Web the JS does the following to make a call:
webRtcPeer = new kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(options,
function(error) {
if (error) {
return console.error(error);
}
this.generateOffer(onOfferCall);
});
onOfferCall - calls directly the Web!
We were looking into the Kurento IOS documentation, but couldn't yet figure out. How can we convert this into IOS?
Any help would be highly appreciated!
Your iOS app should be sending the messages to the signaling server. I'd suggest you read this small introduction from the documentation, and spend some time understanding where your application architecture fits in this diagram
Hint: It's not the first one ;-)
I'm trying to send an Apple push notification from a Go appengine site. I'm using the apns2 library as follows:
cert, err := certificate.FromPemFile(pemFile, "")
if err != nil {
log.Fatalf("cert error: %v", err)
}
client := apns2.NewClient(cert).Development()
n := &apns2.Notification{...}
if res, err := client.Push(n); err != nil { ... }
On a local development server, it works fine; but in production I'm seeing:
Post https://api.development.push.apple.com/3/device/995aa87050518ca346f7254f3484d7d5c731ee93c35e3c359db9ddf95d035003:
dial tcp: lookup api.development.push.apple.com on [::1]:53: dial udp [::1]:53: socket: operation not permitted
It looks like appengine expects you to use its own urlfetch library when sending outbound requests, so I tried setting the underlying HTTPClient to use that:
client.HTTPClient = urlfetch.Client(ctx)
However, the response from the Apple server is now
##?HTTP/2 client preface string missing or corrupt. Hex dump for received bytes: 504f5354202f332f6465766963652f393935616138373035
I believe the problem is that Apple push notifications require HTTP/2, but urlfetch only implements HTTP/1.1.
How do I solve this problem? Is there a way for an appengine app to send an HTTP/2 request?
This would require going through the App Engine Sockets API. The documentation states:
Libraries that can accept a net.Conn should work without modification.
You can get a net.Conn from the appengine/socket package and pass it to a lib that will accept one, but in the case of apns2 it doesn't allow you to do this. However another user has submitted a pull request to the apns2 project that adds a distinct GAEClient which can use App Engine sockets.
As of right now it looks like the commits still have not been pulled into the master branch, however you could still merge these updates manually into your own source tree as a workaround for now.
I dont know much about go appengine, but whatever it looks from the code, your client := apns2.NewClient(cert).Development() line seems to be defective, i think for production, you dont need development cert, you need to have distribution cert. So check that is there any option available for it. Also, is certificates from apple's dev site are generated by you or by go appengine. If you have manually created that, then you have to create 2 types of certificates, one for developement and one for distribution/production, and when app is running in production mode, you need to use that certificates.
so i'm building an iOS app and i've started building a REST API w/ nodejs, express, & mongodb. I'm currently adding instant messaging and notifications to my app so i've been reading up on websockets(socket.io). After tons of reading, I honestly cannot wrap my head around the concept and how to integrate into my API.
For example, I have this API route:
// create new message
app.post('/newmessage', function (req, res, next) {
if (!req.body.message ) {
res.json({success: false, msg: 'You must type a message.'});
console.log('message: ' + req.body.message);
} else {
var newMessage = new Message({
fromUser: ObjectID(req.params.id),
toUser: ObjectID(req.params.id),
message: String,
});
// save new message
newMessage.save(function(err) {
if (err) {
res.json({success: false, msg: 'message was unsuccessful.'});
} else {
res.json({success: true, msg: 'message sent!'});
console.log(newMessage.createdAt);
console.log(newMessage.updatedAt);
}
});
}
});
How would I integrate socket.io into this specific call? Would I create a Socket.js file and export from there? Backend isn't my thing at all, so I apologize if this is a poor question. Thanks!
The general architecture for using a webSocket or socket.io connection for instant messaging or server-push notifications is as follows:
Each client makes a webSocket or socket.io connection to the server.
The server is listening for those incoming connections and uses some sort of cookie on the initial connection to identify which user is connecting.
The server then holds those connections open for the duration of the user session and listens for incoming messages on them.
At any time, if the server wishes to send a notification to the client, it can find the connection belonging to the desired client and send a message on that connection.
The client will be listening for incoming messages and will receive that message and can then process it.
For instant messaging, a client would send a message to the server that essentially says "send this message to Bob" (where Bob is some user ID for some other user on the system). The server would receive that message from the other client and would then find Bob's connection and send the message to Bob on that connection.
I would recommend using socket.io as it offers a number of useful features on top of webSockets and there should be socket.io libraries for all platforms you would be using. The socket.io documentation includes a demo app that does chat which will give you some idea how things work with socket.io.
I added a chat functionality in my existing RoR app with the websocket-rails (https://github.com/websocket-rails/websocket-rails) gem and I'm testing it in production mode on Heroku.
It works fine in localhost, but when I sent it to Heroku it is not receiving any further messages from the Server after the connection.
The connection works:
var WEBSOCKET_URL = 'myapponheroku.herokuapp.com/websocket'
var dispatcher = new WebSocketRails(WEBSOCKET_URL);
dispatcher.on_open = function(data) {
console.log('Connection has been established: ', data);
};
And I see the feedback message.
But the first task is to ask the server for active rooms in my chat. I noticed nothing is returning. I put some feedback messages in the server's action that should do the first tasks and none of them are reached. The feedback I implemented is a simple pair of functions that just returns a object and prints it to the console:
Javascript
dispatcher.bind('feedback',function(data){
console.log(data);
});
Ruby
def feedback data
send_message :feedback, {data: data}
end
It is never triggered. Any ideas? Is the a special configuration I must do on Heroku to allow it to work?
#update
Same error on my live server hosted on AWS.
Connection is established, but nothing is returned from server to client.
#update2
I wrote some code so the app would write some files when connection is established and when it asks for open rooms, right at the beginning, and the files were not created. The actions are not even being called.