UNPKG

4.84 kBJavaScriptView Raw
1/**
2 * Kettle Test Utilities for WebSockets
3 *
4 * Contains facilities for issuing WebSocketsrequests encoded declaratively as Infusion components
5 * Copyright 2013-2015 Raising the Floor (International)
6 * Copyright 2013 OCAD University
7 *
8 * Licensed under the New BSD license. You may not use this file except in
9 * compliance with this License.
10 *
11 * You may obtain a copy of the License at
12 * https://github.com/gpii/universal/LICENSE.txt
13 */
14
15"use strict";
16
17var fluid = require("infusion"),
18 kettle = fluid.registerNamespace("kettle");
19
20fluid.require("ws", require, "kettle.npm.ws");
21
22fluid.defaults("kettle.test.request.ws", {
23 gradeNames: ["kettle.test.request"],
24 receiveJSON: true, // deserialize all received data as JSON
25 sendJSON: true, // serialize all sent data as JSON
26 // forwarded to "protocols" argument of new ws.WebSocket() - ws 2.x requires this to be [] rather than null
27 webSocketsProtocols: [],
28 invokers: {
29 connect: {
30 funcName: "kettle.test.request.ws.connect",
31 args: ["{that}", "{cookieJar}", "{arguments}.0"]
32 },
33 send: {
34 funcName: "kettle.test.request.ws.send",
35 args: [
36 "{that}",
37 "{arguments}.0", // model
38 "{arguments}.1" // send options: https://github.com/websockets/ws/blob/master/doc/ws.md#websocketsenddata-options-callback
39 ]
40 },
41 disconnect: {
42 funcName: "kettle.test.request.ws.disconnect",
43 args: "{that}.ws"
44 }
45 },
46 events: {
47 onConnect: null, // fired when we receive "open" for initial connection
48 onReceiveMessage: null,
49 onError: null // fires if connect fails
50 },
51 listeners: {
52 onDestroy: "{that}.disconnect"
53 }
54});
55
56// A variety of WebSockets request that retrieve cookies from a "jar" higher in the component tree
57fluid.defaults("kettle.test.request.wsCookie", {
58 gradeNames: ["kettle.test.request.ws"],
59 storeCookies: true
60});
61
62// permitted options taken from https://github.com/websockets/ws/blob/master/doc/ws.md#new-wswebsocketaddress-protocols-options
63kettle.test.request.ws.requestOptions = ["protocol", "agent", "headers", "protocolVersion", "hostname", "port", "path", "termMap"];
64
65kettle.test.request.ws.connect = function (that, cookieJar, directOptions) {
66 if (that.ws) {
67 fluid.fail("You cannot reuse a kettle.test.request.ws object once it has connected - please construct a fresh component for this request");
68 }
69 var requestOptions = kettle.dataSource.URL.prepareRequestOptions(that.options, cookieJar, directOptions, kettle.test.request.ws.requestOptions, that.resolveUrl);
70
71 var url = "ws://" + requestOptions.hostname + ":" + requestOptions.port + requestOptions.path;
72 fluid.log("connecting ws.WebSocket to: " + url + " with request options ", requestOptions);
73
74 that.ws = new kettle.npm.ws(url, that.options.webSocketsProtocols, requestOptions);
75 that.ws.on("open", function () {
76 that.events.onConnect.fire(that);
77 });
78 that.ws.on("unexpected-response", function (req, res) { // mined out of the source code of ws WebSockets.js to get extra detail on native HTTP response
79 that.nativeResponse = { // to enable parallel processing of errors as for kettle.test.request.http
80 statusCode: res.statusCode
81 };
82 that.events.onError.fire(// Fabricate a response body as if from HTTP response which we can't read
83 JSON.stringify({statusCode: res.statusCode,
84 isError: true,
85 message: "Unexpected HTTP response where WebSockets response was expected"}), that, res);
86 });
87 that.ws.on("error", function (err) {
88 fluid.log("kettle.test.request.ws.connect client error on connect ", err);
89 that.events.onError.fire(that, err);
90 });
91 that.ws.on("message", function (data) {
92 fluid.log("kettle.test.request.ws.connect client message ", data);
93 that.events.onReceiveMessage.fire(that.options.receiveJSON ? kettle.JSON.parse(data) : data, that);
94 });
95};
96
97kettle.test.request.ws.disconnect = function (ws) {
98 if (ws) {
99 ws.terminate();
100 }
101};
102
103kettle.test.request.ws.send = function (that, model, directOptions) {
104 if (!that.ws) {
105 fluid.fail("Error in kettle.test.request.ws.send - you must first call connect() on this request object before calling send()");
106 }
107 var togo = fluid.promise();
108 var text = that.options.sendJSON ? JSON.stringify(model) : model;
109 that.ws.send(text, directOptions, function (err) {
110 if (err) {
111 that.events.onError.fire(that, err); // TODO: somehow advertise to users that they should not double-handle
112 togo.reject(err);
113 } else {
114 togo.resolve();
115 }
116 });
117 return togo;
118};