Commits

Greg Taylor committed 3ad42f8

Working WAMP client.

Comments (0)

Files changed (6)

lib/src/netcomm/netcomm.dart

 library netcomm;
 
-class WampClient {
 
-  static String SERVER_URL = 'ws://localhost:9000';
 
-  WampClient() {
+import "package:hexmap/wamp_client/wamp_client.dart";
 
-  }
-
-  void onConnect(var session) {
-    print("Connected!: ${session}");
-    //session.subscribe("http://example.com/myEvent1",
-    //    js.context.onEventCallback);
-  }
+class ChatClient extends WampClientProtocol {
+  ChatClient(socket) : super(socket);
 
-  void onConnectionLost(var code, var reason) {
-    print("Connection lost: ${code} ${reason}");
+  onOpenSession() {
+    subscribe('http://example.com/myEvent1');
   }
 
-  void onEvent(var topic, var event) {
-    print("topic: ${topic}");
-    print("event: ${event}");
+  onEvent(topicUri, event) {
+    print("Event received");
   }
 }
+

lib/wamp_client/src/curie.dart

+part of wamp_client;
+
+class CurieCodec {
+  Map<String, String> map = new Map();
+
+  CurieCodec();
+
+  void addPrefix(String prefix, String expansion) {
+    map[prefix] = expansion;
+  }
+
+  /**
+   * Encode the given URI to CURIE. If no appropriate prefix mapping
+   * is available, return original URI.
+   */
+  String encode(String uri) {
+    return uri; // TODO
+  }
+
+  /**
+   * Decode given CURIE to full URI. Returns the original CURIE if it cannot be
+   * decoded.
+   */
+  String decode(String curie) {
+    return curie; // TODO
+  }
+}

lib/wamp_client/src/protocol.dart

+part of wamp_client;
+
+const int PROTOCOL_VERSION = 1;
+
+abstract class WampProtocol {
+  static const String URI_WAMP_BASE = "http://api.wamp.ws/";
+  static const String URI_WAMP_PROCEDURE = "http://api.wamp.ws/procedure#";
+}
+
+/**
+ * WAMP defines the message types which are used in the communication between
+ * two WebSocket endpoints, the client and the server, and describes associated
+ * semantics.
+ */
+abstract class MessageType {
+  // Auxiliary messages.
+  static const int WELCOME = 0;
+  static const int PREFIX = 1;
+
+  // Remote procedure calls.
+  static const int CALL = 2;
+  static const int CALL_RESULT = 3;
+  static const int CALL_ERROR = 4;
+
+  // Publish & subscribe.
+  static const int SUBSCRIBE = 5;
+  static const int UNSUBSCRIBE = 6;
+  static const int PUBLISH = 7;
+  static const int EVENT = 8;
+}

lib/wamp_client/wamp_client.dart

-library blarty;
+library wamp_client;
+
+import 'dart:html';
+import 'dart:convert' show JSON;
+import 'dart:math';
+import 'dart:async';
+import "package:crypto/crypto.dart";
+
+part 'src/protocol.dart';
+part 'src/curie.dart';
+
+// TODO: add reconnect functionality.
+
+/**
+ * WAMP client.
+ */
+class WampClientProtocol {
+  WebSocket _socket;
+
+  String sessionId;
+  Map<String, String> prefixes = new Map();
+  Map<String, Completer> callCompleters = new Map();
+
+  WampClientProtocol(this._socket) {
+    _socket.onMessage.listen((e) => onMessage(JSON.decode(e.data)));
+  }
+
+  void onMessage(msg) {
+    print("Messsage: ${msg}");
+    switch (msg[0]) {
+      case MessageType.WELCOME:
+        sessionId = msg[1];
+        onOpenSession();
+        break;
+
+      case MessageType.CALL_RESULT:
+        var completer = callCompleters.remove(msg[1]);
+
+        if (completer != null) {
+          completer.complete(msg[2]);
+        } else {
+          // TODO: handle unknown callId error.
+        }
+        break;
+
+      case MessageType.CALL_ERROR:
+        // TODO: implement me!
+        break;
+
+      case MessageType.EVENT:
+        onEvent(msg[1], msg[2]);
+        break;
+    }
+  }
+
+  void send(msg) {
+    _socket.send(JSON.encode(msg));
+  }
+
+  void onOpenSession() {
+    // Override me!
+  }
+
+  void onEvent(String topicUri, event) {
+    // Override me!
+  }
+
+  /**
+   * Set a CURIE prefix.
+   */
+  void prefix(String prefix, String uri) {
+    prefixes[prefix] = uri;
+    send([MessageType.PREFIX, prefix, uri]);
+  }
+
+  /**
+   * A remote procedure call.
+   */
+  Future call(uri, [args]) {
+    var rnd = new Random(), // TODO: Extract this!
+        callId = rnd.nextInt(99999).toString(); // TODO: use some kind of hash.
+
+    var completer = new Completer();
+
+    callCompleters[callId] = completer;
+
+    var msg = [MessageType.CALL, callId, uri];
+    if (args is List) {
+      msg.addAll(args);
+    } else if (args != null) {
+      msg.add(args);
+    }
+    send(msg);
+
+    return completer.future;
+  }
+
+  /**
+   * Subscribe to the given topic.
+   */
+  void subscribe(topicUri) {
+    send([MessageType.SUBSCRIBE, topicUri]);
+  }
+
+  /**
+   * Unsubscribe from the given topic.
+   */
+  void unsubscribe(topicUri) {
+    send([MessageType.UNSUBSCRIBE, topicUri]);
+  }
+
+  /**
+   * Publish an event to the given topic.
+   */
+  void publish(String topicUri, event, [exclude, eligible]) { // TODO: convert to named parameters.
+    send([MessageType.PUBLISH, topicUri, event]); //, exclude, eligible]);
+  }
+}
+
+class WampCraClientProtocol extends WampClientProtocol {
+
+  WampCraClientProtocol(socket) : super(socket);
+
+  /*
+   * Authenticate the WAMP session to server.
+   */
+  Future authenticate({authKey: "", authExtra: "", authSecret: ""}) {
+    Future authreq = call(WampProtocol.URI_WAMP_PROCEDURE + "authreq", authKey);
+    return authreq.then((challenge) {
+      var sig = authSignature(challenge, authSecret);
+      return call(WampProtocol.URI_WAMP_PROCEDURE + "auth", sig);
+    });
+  }
+
+  /*
+   * Compute the authentication signature from an authentication challenge and a secret.
+   */
+  String authSignature(authChallenge, authSecret, [authExtra]) {
+    HMAC hash = new HMAC(new SHA256(), authSecret.codeUnits);
+    hash.add(authChallenge.codeUnits);
+    return CryptoUtils.bytesToBase64(hash.digest);
+  }
 
-void yay() {
-  print("HOORAY");
 }
 description: A sample web application
 dependencies:
   browser: any
-  js: any
+  crypto: any
   json_object: any
   stagexl: any
 import 'dart:html' as html;
 import 'dart:async' as async;
 import 'package:stagexl/stagexl.dart';
-//import "package:hexmap/src/netcomm/netcomm.dart";
+import "package:hexmap/src/netcomm/netcomm.dart";
 import "package:hexmap/src/map_widget/map_widget.dart";
 
 html.Element stageElement = html.querySelector('#stage');
   RenderLoop renderLoop = new RenderLoop()
     ..addStage(stage);
 
+  var socket = new html.WebSocket('ws://127.0.0.1:9000', ["wamp"]);
+  socket.onError.listen((error) => print("Error received: ${error.toString()}"));
+  socket.onMessage.listen((html.MessageEvent e) {
+    print('Received message: ${e.data}');
+  });
+  var client = new ChatClient(socket);
+
   MapContainer mapContainer = new MapContainer(stage);
   mapContainer.setup()
     .then((resourceManager) {
       stageElement.focus();
     });
 
-}
+}