I'm thinking of scenarios where I might want to perform cross-cutting/AOP or other functions at the server for my Meteor Js project when a Meteor client (or DDP client) invokes a server-side method over a DDP connection.
This link here gives a really nice example of how to perform AOP on objects, but I wanted to know if there was a way to listen for inbound client requests over the DDP connection much like express-interceptor or action filters for asp.net web api but, of course, for websocket/ddp rpc implementations.
The Meteor Js Api describes only one event "onConnection" at the server. And this SO response mentions a connection._send on the client to perform certain AOP functions...but didn't find a whole lot of official documentation beyond that.
I basically want to know if there is a way to listen at the server for all DDP method calls from all client sessions to the server as described in the DDP spec here
Thanks.
We'll there are a lot of undocumented things in Meteor you'll find. I don't really see it as a problem. Here are some ways to intercept WebSocket traffic in Meteor:
Server
Here's some stuff you can do from the server:
Server -> Client
It's a bit tedious to intercept messages from the server to the client, but this works. You'd probably want to write some code to pin logs to clients.
(function () {
var timeout = 3000
var streamServer = Meteor.server.stream_server
var standardConnect = streamServer.server._events.connection
streamServer.server._events.connection = function (socket) {
var write = socket.write
socket.write = function () {
console.log(arguments)
write.apply(this, args)
}
standardConnect.apply(this, arguments)
}
})()
Client -> Server
To intercept calls from the client on the server you can do this:
Meteor.server.stream_server.server.addListener('connection', function (socket) {
var old = socket._events.data
socket._events.data = function () {
console.log(arguments)
old.apply(this, arguments)
}
})
The above sipped can't be used with the first one. It's not hard to fix dough. If you use this snipped, Meteor.server.stream_server.server._events.connection will simply be an array of functions instead of a function.
Client
Server - > Client
To Listen to calls from server to the client on the client you can do this:
Meteor.connection._stream.on('message', console.log.bind(console))
You can also intercept them using something like this
(function () {
var cb = Meteor.connection._stream.eventCallbacks.message[0]
Meteor.connection._stream.eventCallbacks.message[0] = function () {
console.log(arguments)
cb.apply(this, arguments)
}
})()
Not entirely sure how solid that one is. But it works, so what the heck.
To test it out you can simply do
Meteor.subscribe('test')
Client -> Server
As you pointed out, you can also do similar things with outgoing messages from the client.
Meteor.connection._send = function () {
console.log(arguments)
this.__proto__._send.apply(this, arguments)
}
Related
I have a CasperJS Script duolingo.js and I run the script using a MeteorJS App using a Meteor Method like this:
// define server methods so that the clients will have access to server components
Meteor.methods({
runCasperJS: function() {
// This method call won't return immediately, it will wait for the
// asynchronous code to finish, so we call unblock to allow this client
// to queue other method calls (see Meteor docs)
this.unblock();
// run synchonous system command
var result = process_exec_sync('casperjs duolingo.js --engine=slimerjs --disk-cache=no --proxy=178.166.41.225:80 --proxy-type=HTTP');
// check for error
if (result.error) {
throw new Meteor.Error("exec-fail", "Error running CasperJS: " + result.error.message);
}
// success
return true;
}
});
I need to run the CasperJS Instance using a proxy IP V6. But I don't have any idea, the official documentations of slimerjs and casperjs did not say anything.
Here is the exact part of the code where I use an IP V4 Proxy.
var result = process_exec_sync('casperjs duolingo.js
--engine=slimerjs --disk-cache=no --proxy=178.166.41.225:80
--proxy-type=HTTP');
Thank you for your time and help.
Currently, I use the built-in meteor http method (see http://docs.meteor.com/#http) for issuing http calls, on both my client and my server.
However, I'm experiencing two issues:
is it possible to cancel a request?
is it possible to have multiple query parameters which share the same key?
Are these just Meteor limitations, or are there ways to get both to work using Meteor?
I know I could you jquery on the clientside, and there must be a server-side solution which supports both as wel, but I'd prefer sticking with meteor code here.
"is it possible to cancel a request?"
HTTP.call() does not appear to return an object on which we could call something like a stop() method. Perhaps a solution would be to prevent execution of your callback based on a Session variable?
HTTP.call("GET", url, function(error, result) {
if (!Session.get("stopHTTP")) {
// Callback code here
}
});
Then when you reach a point where you want to cancel the request, do this:
Session.set("stopHTTP", true);
On the server, instead of Session perhaps you could use an environment variable?
Note that the HTTP.call() options object does accept a timeout key, so if you're just worried about the request never timing out, you can set this to whatever millisecond integer you want.
"is it possible to have multiple query parameters which share the same key?"
Yes, this appears to be possible. Here's a simple test I used:
Meteor code:
HTTP.call("GET", "http://localhost:1337", {
query: "id=foo&id=bar"
}, function(error, result) {
// ...
});
Separate Node.js server: (just the basic example on the Node.js homepage, with a console.log line to output the request URL with query string)
var http = require('http');
http.createServer(function(req, res) {
console.log(req.url); // Here I log the request URL, with the query string
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
When the Meteor server is run, the Node.js server logged:
/?id=foo&id=bar
Of course, this is only for GET URL query parameters. If you need to do this for POST params, perhaps you could store the separate values as a serialized array string with EJSON.stringify?
Is there a way to stop a Client calling a Server Method from the browser console?
I gather from the Unofficial Meteor FAQ that there isn't. I just wanted to check if that's definitely the case - the FAQ isn't really specific. I mean are there no 'private' methods?
In meteor the 'methods' described by Meteor.methods can all be called from the client. In this sense there aren't private methods because the purpose of the RPC call is for the client to make the call.
If you want a 'private' method you could use an ordinary JavaScript method. If you define the method with var, it would only be accessible within the file, and cannot be called from the client.
var yourmethod = function() {
...
}
which is equivalent to:
function yourmethod() {
...
}
Or you can define it so any of your server script can use it:
yourmethod = function() {
....
}
If you mean you want a RPC method call that is accessible only from the javascript code, but not from the javascript console in chrome this isn't possible. This is because the idea behind meteor is all RPCs from the client are not trusted & there is no way to distinguish whether it came from the console or not. You can use meteor user authentication or Collection.allow or Collection.deny methods to prevent any unauthorized changes this way.
I made a private method by checking this.connection to be null.
Ref: http://docs.meteor.com/#/full/method_connection
Ex.
Meteor.methods({
'serverCallOnlyFunc': function() {
if (this.connection === null) {
//do something
} else {
throw(new Meteor.Error(500, 'Permission denied!'));
}
}
});
I heard SignalR is a good messaging library. I got some code for SignalR but I am not able to understand how it works.
JS
var hooking;
$(function() {
hooking = $.connection.hooking;
hooking.removeLead = function(ref) {
$("lead" + ref).remove();
};
$.connection.hub.start();
});
C#
// Hooking.cs (placed in application root)
public class Hooking : Hub
{
public void Submit(string jsonString)
{
var serializer = new JavaScriptSerializer();
var json = serializer.Deserialize<HookingLeadResult>(jsonString);
Clients.removeLead(json.Ref); // Remove lead from client hooking windows
// update lead gen
}
}
I have questions about the above code.
What does hooking mean ins $.connection.hooking;
Where is removeLead in hooking.removeLead
What will this do $.connection.hub.start(); ? What does it start? Which method it will invoke at the server side?
Who & how Submit method will be called at the server side? how to pass data from client side to server side. If possible please give me a url for good start for SignalR library.
The Javascript function hooking.removeLead will be invoked whenever you call Clients.removeLead(). All the bindings are done dynamically, between Javascript to C# and between C# and Javascript.
$.connection.hub.start() is actually the connect function. It will connect your client to the server. No messages can be sent or received until you do. The start function allows you to define a callback to be called when it's done connecting.
The Submit method at the server will be called whenever you do a hooking.submit(json) call on your client. For instance, as a result of the user filling in some form and clicking a button.
I recommend starting with the SignalR official wiki: http://www.asp.net/signalr
I'm wondering if I can make a RPC to meteor server from a client?
The context is that I like to get a list of files in server, something like the following:
// in server
var listdir = function() {
}
// in client
files = SERVER.listdir();
Example code for calling server method: https://gist.github.com/2964422
Demo : http://servercall.meteor.com/
Meteor Documentation: http://docs.meteor.com/#methods_header