From 3b562029420c7e484facc948ce6a2a5af92ad2e0 Mon Sep 17 00:00:00 2001 From: Pieter Hintjens Date: Wed, 10 Feb 2016 13:37:56 +0100 Subject: [PATCH] Problem: want fuller Zyre example for node.js Solution: add ZyreEvent class and write zpinger.js This tool works exactly the same as the new zpinger in C, and shows identical output. The C code is the same size and uses a very similar flow. CLASS makes an elegant match with JavaScript. --- bindings/nodejs/binding.cc | 291 ++++++++++++++++++++++++++++++----- bindings/nodejs/build.sh | 2 +- bindings/nodejs/package.json | 2 - bindings/nodejs/zpinger.js | 109 +++++++++++-- include/zyre.h | 6 - 5 files changed, 350 insertions(+), 60 deletions(-) diff --git a/bindings/nodejs/binding.cc b/bindings/nodejs/binding.cc index 53cfb6afe..9f855b6ad 100644 --- a/bindings/nodejs/binding.cc +++ b/bindings/nodejs/binding.cc @@ -2,6 +2,7 @@ #include "nan.h" using namespace v8; +using namespace Nan; class Zyre: public Nan::ObjectWrap { public: @@ -15,6 +16,7 @@ class Zyre: public Nan::ObjectWrap { // Prototypes Nan::SetPrototypeMethod (tpl, "destroy", destroy); + Nan::SetPrototypeMethod (tpl, "defined", defined); Nan::SetPrototypeMethod (tpl, "uuid", uuid); Nan::SetPrototypeMethod (tpl, "name", name); Nan::SetPrototypeMethod (tpl, "start", start); @@ -24,8 +26,8 @@ class Zyre: public Nan::ObjectWrap { Nan::SetPrototypeMethod (tpl, "join", join); Nan::SetPrototypeMethod (tpl, "leave", leave); Nan::SetPrototypeMethod (tpl, "print", print); - Nan::SetPrototypeMethod (tpl, "whispers", whispers); - Nan::SetPrototypeMethod (tpl, "shouts", shouts); + Nan::SetPrototypeMethod (tpl, "whisper", whisper); + Nan::SetPrototypeMethod (tpl, "shout", shout); Nan::SetPrototypeMethod (tpl, "recv", recv); constructor ().Reset (Nan::GetFunction (tpl).ToLocalChecked ()); @@ -35,61 +37,68 @@ class Zyre: public Nan::ObjectWrap { private: explicit Zyre (char *name = NULL) { self = zyre_new (name); - assert (self); } ~Zyre () { } static NAN_METHOD (New) { assert (info.IsConstructCall ()); - Zyre *obj; + Zyre *zyre; if (info [0]->IsString ()) { Nan::Utf8String name (info [0].As()); - obj = new Zyre (*name); + zyre = new Zyre (*name); } else - obj = new Zyre (); - obj->Wrap (info.This ()); - info.GetReturnValue ().Set (info.This ()); + zyre = new Zyre (); + + if (zyre) { + zyre->Wrap (info.This ()); + info.GetReturnValue ().Set (info.This ()); + } } static NAN_METHOD (destroy) { - Zyre *obj = Nan::ObjectWrap::Unwrap (info.Holder ()); - zyre_destroy (&obj->self); + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); + zyre_destroy (&zyre->self); + } + + static NAN_METHOD (defined) { + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); + info.GetReturnValue ().Set (Nan::New (zyre->self != NULL)); } static NAN_METHOD (uuid) { - Zyre *obj = Nan::ObjectWrap::Unwrap (info.Holder ()); - info.GetReturnValue ().Set (Nan::New (zyre_uuid (obj->self)).ToLocalChecked ()); + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); + info.GetReturnValue ().Set (Nan::New (zyre_uuid (zyre->self)).ToLocalChecked ()); } static NAN_METHOD (name) { - Zyre *obj = Nan::ObjectWrap::Unwrap (info.Holder ()); - info.GetReturnValue ().Set (Nan::New (zyre_name (obj->self)).ToLocalChecked ()); + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); + info.GetReturnValue ().Set (Nan::New (zyre_name (zyre->self)).ToLocalChecked ()); } static NAN_METHOD (start) { - Zyre *obj = Nan::ObjectWrap::Unwrap (info.Holder ()); - zyre_start (obj->self); + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); + zyre_start (zyre->self); } static NAN_METHOD (stop) { - Zyre *obj = Nan::ObjectWrap::Unwrap (info.Holder ()); - zyre_stop (obj->self); + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); + zyre_stop (zyre->self); } static NAN_METHOD (set_verbose) { - Zyre *obj = Nan::ObjectWrap::Unwrap (info.Holder ()); - zyre_set_verbose (obj->self); + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); + zyre_set_verbose (zyre->self); } static NAN_METHOD (set_header) { if (info [0]->IsString () && info [1]->IsString ()) { - Zyre *obj = Nan::ObjectWrap::Unwrap (info.Holder ()); + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); Nan::Utf8String name (info [0].As()); Nan::Utf8String value (info [0].As()); - zyre_set_header (obj->self, *name, "%s", *value); + zyre_set_header (zyre->self, *name, "%s", *value); } else return Nan::ThrowTypeError (".set_header() expects name and value as strings"); @@ -97,9 +106,9 @@ class Zyre: public Nan::ObjectWrap { static NAN_METHOD (join) { if (info [0]->IsString ()) { - Zyre *obj = Nan::ObjectWrap::Unwrap (info.Holder ()); + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); Nan::Utf8String group (info [0].As()); - zyre_join (obj->self, *group); + zyre_join (zyre->self, *group); } else return Nan::ThrowTypeError (".join() expects group as string"); @@ -107,41 +116,45 @@ class Zyre: public Nan::ObjectWrap { static NAN_METHOD (leave) { if (info [0]->IsString ()) { - Zyre *obj = Nan::ObjectWrap::Unwrap (info.Holder ()); + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); Nan::Utf8String group (info [0].As()); - zyre_leave (obj->self, *group); + zyre_leave (zyre->self, *group); } else return Nan::ThrowTypeError (".leave() expects group as string"); } - static NAN_METHOD (whispers) { + static NAN_METHOD (whisper) { if (info [0]->IsString () && info [1]->IsString ()) { - Zyre *obj = Nan::ObjectWrap::Unwrap (info.Holder ()); + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); Nan::Utf8String peer (info [0].As()); - Nan::Utf8String message (info [0].As()); - zyre_shouts (obj->self, *peer, "%s", *message); + Nan::Utf8String string (info [0].As()); + zmsg_t *msg = zmsg_new (); + zmsg_pushstr (msg, *string); + zyre_whisper (zyre->self, *peer, &msg); } else - return Nan::ThrowTypeError (".set_header() expects peer and message as strings"); + return Nan::ThrowTypeError (".whisper() expects peer and message as strings"); } - static NAN_METHOD (shouts) { + static NAN_METHOD (shout) { if (info [0]->IsString () && info [1]->IsString ()) { - Zyre *obj = Nan::ObjectWrap::Unwrap (info.Holder ()); + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); Nan::Utf8String group (info [0].As()); - Nan::Utf8String message (info [0].As()); - zyre_shouts (obj->self, *group, "%s", *message); + Nan::Utf8String string (info [0].As()); + zmsg_t *msg = zmsg_new (); + zmsg_pushstr (msg, *string); + zyre_shout (zyre->self, *group, &msg); } else - return Nan::ThrowTypeError (".set_header() expects group and message as strings"); + return Nan::ThrowTypeError (".shout() expects group and message as strings"); } static NAN_METHOD (recv) { - Zyre *obj = Nan::ObjectWrap::Unwrap (info.Holder ()); - zmsg_t *msg = zyre_recv (obj->self); + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); + zmsg_t *msg = zyre_recv (zyre->self); if (msg) { char *string = zmsg_popstr (msg); info.GetReturnValue ().Set (Nan::New (string).ToLocalChecked ()); @@ -150,8 +163,8 @@ class Zyre: public Nan::ObjectWrap { } static NAN_METHOD (print) { - Zyre *obj = Nan::ObjectWrap::Unwrap (info.Holder ()); - zyre_print (obj->self); + Zyre *zyre = Nan::ObjectWrap::Unwrap (info.Holder ()); + zyre_print (zyre->self); } static Nan::Persistent & constructor () { @@ -160,6 +173,202 @@ class Zyre: public Nan::ObjectWrap { } zyre_t *self; + public: + zyre_t *get_self () { + return self; + } }; -NODE_MODULE (zyre, Zyre::Init) +class ZyreEvent: public Nan::ObjectWrap { + public: + static NAN_MODULE_INIT (Init) { + Nan::HandleScope scope; + + // Prepare constructor template + Local tpl = Nan::New (New); + tpl->SetClassName (Nan::New ("ZyreEvent").ToLocalChecked ()); + tpl->InstanceTemplate ()->SetInternalFieldCount (1); + + // Prototypes + Nan::SetPrototypeMethod (tpl, "destroy", destroy); + Nan::SetPrototypeMethod (tpl, "defined", defined); + Nan::SetPrototypeMethod (tpl, "type", type); + Nan::SetPrototypeMethod (tpl, "type_name", type_name); + Nan::SetPrototypeMethod (tpl, "peer_id", peer_id); + Nan::SetPrototypeMethod (tpl, "peer_name", peer_name); + Nan::SetPrototypeMethod (tpl, "peer_addr", peer_addr); + Nan::SetPrototypeMethod (tpl, "header", header); + Nan::SetPrototypeMethod (tpl, "group", group); + Nan::SetPrototypeMethod (tpl, "msg", msg); + Nan::SetPrototypeMethod (tpl, "print", print); + + constructor ().Reset (Nan::GetFunction (tpl).ToLocalChecked ()); + Nan::Set (target, Nan::New ("ZyreEvent").ToLocalChecked (), + Nan::GetFunction (tpl).ToLocalChecked ()); + } + private: + explicit ZyreEvent (Zyre *zyre) { + self = zyre_event_new (zyre->get_self ()); + } + ~ZyreEvent () { + } + + static NAN_METHOD (New) { + assert (info.IsConstructCall ()); + if (info [0]->IsObject ()) { + Zyre *zyre = Nan::ObjectWrap::Unwrap (info [0].As()); + ZyreEvent *zyre_event = new ZyreEvent (zyre); + zyre_event->Wrap (info.This ()); + info.GetReturnValue ().Set (info.This ()); + } + else + return Nan::ThrowTypeError ("New ZyreEvent expects Zyre argument"); + } + + static NAN_METHOD (destroy) { + ZyreEvent *zyre_event = Nan::ObjectWrap::Unwrap (info.Holder ()); + zyre_event_destroy (&zyre_event->self); + } + + static NAN_METHOD (defined) { + ZyreEvent *zyre_event = Nan::ObjectWrap::Unwrap (info.Holder ()); + info.GetReturnValue ().Set (Nan::New (zyre_event->self != NULL)); + } + + static NAN_METHOD (type) { + ZyreEvent *zyre_event = Nan::ObjectWrap::Unwrap (info.Holder ()); + info.GetReturnValue ().Set (Nan::New (zyre_event_type (zyre_event->self))); + } + + static NAN_METHOD (type_name) { + ZyreEvent *zyre_event = Nan::ObjectWrap::Unwrap (info.Holder ()); + int type = zyre_event_type (zyre_event->self); + const char *type_name = ""; + switch (type) { + case 1: type_name = "ENTER"; break; + case 2: type_name = "JOIN"; break; + case 3: type_name = "LEAVE"; break; + case 4: type_name = "EXIT"; break; + case 5: type_name = "WHISPER"; break; + case 6: type_name = "SHOUT"; break; + case 7: type_name = "STOP"; break; + case 8: type_name = "EVASIVE"; break; + } + info.GetReturnValue ().Set (Nan::New (type_name).ToLocalChecked ()); + } + + static NAN_METHOD (peer_id) { + ZyreEvent *zyre_event = Nan::ObjectWrap::Unwrap (info.Holder ()); + info.GetReturnValue ().Set (Nan::New (zyre_event_peer_id (zyre_event->self)).ToLocalChecked ()); + } + + static NAN_METHOD (peer_name) { + ZyreEvent *zyre_event = Nan::ObjectWrap::Unwrap (info.Holder ()); + info.GetReturnValue ().Set (Nan::New (zyre_event_peer_name (zyre_event->self)).ToLocalChecked ()); + } + + static NAN_METHOD (peer_addr) { + ZyreEvent *zyre_event = Nan::ObjectWrap::Unwrap (info.Holder ()); + info.GetReturnValue ().Set (Nan::New (zyre_event_peer_addr (zyre_event->self)).ToLocalChecked ()); + } + + static NAN_METHOD (header) { + if (info [0]->IsString ()) { + ZyreEvent *zyre_event = Nan::ObjectWrap::Unwrap (info.Holder ()); + Nan::Utf8String name (info [0].As()); + info.GetReturnValue ().Set (Nan::New (zyre_event_header (zyre_event->self, *name)).ToLocalChecked ()); + } + else + return Nan::ThrowTypeError (".header() expects name as string"); + } + + static NAN_METHOD (group) { + ZyreEvent *zyre_event = Nan::ObjectWrap::Unwrap (info.Holder ()); + info.GetReturnValue ().Set (Nan::New (zyre_event_group (zyre_event->self)).ToLocalChecked ()); + } + + static NAN_METHOD (msg) { + ZyreEvent *zyre_event = Nan::ObjectWrap::Unwrap (info.Holder ()); + info.GetReturnValue ().Set (Nan::New (zyre_event_group (zyre_event->self)).ToLocalChecked ()); + zmsg_t *msg = zmsg_dup (zyre_event_msg (zyre_event->self)); + if (msg) { + char *string = zmsg_popstr (msg); + info.GetReturnValue ().Set (Nan::New (string).ToLocalChecked ()); + free (string); + zmsg_destroy (&msg); + } + } + + static NAN_METHOD (print) { + ZyreEvent *zyre_event = Nan::ObjectWrap::Unwrap (info.Holder ()); + zyre_event_print (zyre_event->self); + } + + static Nan::Persistent & constructor () { + static Nan::Persistent my_constructor; + return my_constructor; + } + + zyre_event_t *self; + public: + zyre_event_t *get_self () { + return self; + } +}; + + +// +// +// Parsing Zyre messages +// +// +// +// +// +// +// +// +// +// +// +// +// +// Returns value of a header from the message headers +// obtained by ENTER. Return NULL if no value was found. +// +// +// +// +// +// Returns the group name that a SHOUT event was sent to +// +// +// +// +// Returns the incoming message payload (currently one frame) +// +// +// +// +// Print event to zsys log +// +// + + +extern "C" NAN_MODULE_INIT (zyre_initialize) +{ + Zyre::Init (target); + ZyreEvent::Init (target); +} + + +NODE_MODULE (zyre, zyre_initialize) diff --git a/bindings/nodejs/build.sh b/bindings/nodejs/build.sh index f0fc241c0..39f4e908a 100755 --- a/bindings/nodejs/build.sh +++ b/bindings/nodejs/build.sh @@ -122,7 +122,7 @@ echo "I: resolving dependencies for Zyre:" echo "I: building Node.js binding:" cd $BUILD_ROOT test ! -d node_modules/nan && npm install nan@latest --save -test ! -d node_modules/bindings && npm install bindings --save +test ! -d node_modules/bindings && npm install bindings --save-dev # Still not sure of this if [ $ELECTRON -eq 1 ]; then diff --git a/bindings/nodejs/package.json b/bindings/nodejs/package.json index c2927f6b7..b8bd11266 100644 --- a/bindings/nodejs/package.json +++ b/bindings/nodejs/package.json @@ -15,8 +15,6 @@ "url": "git://github.com/zeromq/zyre.git" }, "dependencies": { - "bindings": "^1.2.1", "nan": "^2.2.0", - "sleep": "^3.0.1" } } diff --git a/bindings/nodejs/zpinger.js b/bindings/nodejs/zpinger.js index 4872cb8e8..f877bca44 100644 --- a/bindings/nodejs/zpinger.js +++ b/bindings/nodejs/zpinger.js @@ -1,17 +1,106 @@ -// Minimal sanity test +/* ========================================================================= + zpinger.js - ping other peers in a ZRE network + + ------------------------------------------------------------------------- + Copyright (c) the Contributors as noted in the AUTHORS file. + + This file is part of Zyre, an open-source framework for proximity-based + peer-to-peer applications -- See http://zyre.org. + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + ========================================================================= +*/ + +/* +@header +zpinger tells you what other Zyre nodes are running. Use this to debug +network issues. Run with -v option to get more detail on Zyre's internal +flow for each event. +@discuss +Note that this will detect and speak to Zyre nodes on the entire local +network. +@end +*/ + +// Implementation of zpinger in Node.js +// When you run this, it shows activity on the Zyre network + +Date.prototype.DateTime = function () { + return ( + + (this.getYear () - 100) + "-" + + ((this.getMonth () + 1 < 10)? "0" :"") + (this.getMonth () + 1) + "-" + + ((this.getDate () < 10)? "0" :"") + this.getDate () + " " + + ((this.getHours () < 10)? "0": "") + this.getHours () + ":" + + ((this.getMinutes () < 10)? "0": "") + this.getMinutes () + ":" + + ((this.getSeconds () < 10)? "0": "") + this.getSeconds () + ); +} + +// Print informational message prefixed by date/time +function info (string) +{ + var date = new Date (); + console.log ("I: " + date.DateTime () + " " + string); +} + +var verbose = false; + +process.argv.forEach (function (value, index, array) { + if (value == "-h" || value == "--help") { + console.log ("zpinger.js [options] ..."); + console.log (" --verbose / -v verbose test output"); + console.log (" --help / -h this help"); + process.exit (); + } + else + if (value == "-v" || value == "--verbose") { + verbose = true; + console.log (process.versions); + } +}); var ZyreBinding = require ('bindings')('zyre'); -var zyre = new ZyreBinding.Zyre ("World"); -zyre.setVerbose (); +var zyre = new ZyreBinding.Zyre (); +info ("Create Zyre node, uuid=" + zyre.uuid () + " name=" + zyre.name ()); + +if (verbose) { + zyre.setVerbose (); +} zyre.start (); zyre.join ("GLOBAL"); +if (verbose) { + zyre.print (); +} -console.log ('Hello: ' + zyre.name ()); -console.log ('Waiting for 2 seconds, then exiting'); - -var sleep = require ('sleep'); -sleep.sleep (2); - -zyre.print (); +while (true) { + var event = new ZyreBinding.ZyreEvent (zyre); + if (!event.defined ()) + break; // Interrupted + if (verbose) { + event.print (); + } + if (event.type_name () == "ENTER") { + // If new peer, say hello to it and wait for it to answer us + info ("[" + event.peer_name () + "] peer entered"); + zyre.whisper (event.peer_id (), "Hello"); + } + else + if (event.type_name () == "EXIT") { + info ("[" + event.peer_name () + "] peer exited"); + } + else + if (event.type_name () == "WHISPER") { + info ("[" + event.peer_name () + "] received ping (WHISPER)"); + zyre.shout ("GLOBAL", "Hello"); + } + else + if (event.type_name () == "SHOUT") { + info ("[" + event.peer_name () + "](" + + event.group () + ") received ping (SHOUT)"); + } + event.destroy (); +} zyre.stop (); zyre.destroy (); diff --git a/include/zyre.h b/include/zyre.h index 440bff013..972263e96 100644 --- a/include/zyre.h +++ b/include/zyre.h @@ -233,12 +233,6 @@ int zyre_shouts (zyre_t *self, const char *group, const char *format, ...) CHECK_PRINTF (3); // @end - - - - - - #define zyre_dump(z) zyre_print((z)) #ifdef __cplusplus