• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5define("mojo/public/js/router", [
6  "mojo/public/js/codec",
7  "mojo/public/js/core",
8  "mojo/public/js/connector",
9  "mojo/public/js/validator",
10], function(codec, core, connector, validator) {
11
12  var Connector = connector.Connector;
13  var MessageReader = codec.MessageReader;
14  var Validator = validator.Validator;
15
16  function Router(handle, connectorFactory) {
17    if (!core.isHandle(handle))
18      throw new Error("Router constructor: Not a handle");
19    if (connectorFactory === undefined)
20      connectorFactory = Connector;
21    this.connector_ = new connectorFactory(handle);
22    this.incomingReceiver_ = null;
23    this.errorHandler_ = null;
24    this.nextRequestID_ = 0;
25    this.completers_ = new Map();
26    this.payloadValidators_ = [];
27
28    this.connector_.setIncomingReceiver({
29        accept: this.handleIncomingMessage_.bind(this),
30    });
31    this.connector_.setErrorHandler({
32        onError: this.handleConnectionError_.bind(this),
33    });
34  }
35
36  Router.prototype.close = function() {
37    this.completers_.clear();  // Drop any responders.
38    this.connector_.close();
39  };
40
41  Router.prototype.accept = function(message) {
42    this.connector_.accept(message);
43  };
44
45  Router.prototype.reject = function(message) {
46    // TODO(mpcomplete): no way to trasmit errors over a Connection.
47  };
48
49  Router.prototype.acceptAndExpectResponse = function(message) {
50    // Reserve 0 in case we want it to convey special meaning in the future.
51    var requestID = this.nextRequestID_++;
52    if (requestID == 0)
53      requestID = this.nextRequestID_++;
54
55    message.setRequestID(requestID);
56    var result = this.connector_.accept(message);
57    if (!result)
58      return Promise.reject(Error("Connection error"));
59
60    var completer = {};
61    this.completers_.set(requestID, completer);
62    return new Promise(function(resolve, reject) {
63      completer.resolve = resolve;
64      completer.reject = reject;
65    });
66  };
67
68  Router.prototype.setIncomingReceiver = function(receiver) {
69    this.incomingReceiver_ = receiver;
70  };
71
72  Router.prototype.setPayloadValidators = function(payloadValidators) {
73    this.payloadValidators_ = payloadValidators;
74  };
75
76  Router.prototype.setErrorHandler = function(handler) {
77    this.errorHandler_ = handler;
78  };
79
80  Router.prototype.encounteredError = function() {
81    return this.connector_.encounteredError();
82  };
83
84  Router.prototype.handleIncomingMessage_ = function(message) {
85    var noError = validator.validationError.NONE;
86    var messageValidator = new Validator(message);
87    var err = messageValidator.validateMessageHeader();
88    for (var i = 0; err === noError && i < this.payloadValidators_.length; ++i)
89      err = this.payloadValidators_[i](messageValidator);
90
91    if (err == noError)
92      this.handleValidIncomingMessage_(message);
93    else
94      this.handleInvalidIncomingMessage_(message, err);
95  };
96
97  Router.prototype.handleValidIncomingMessage_ = function(message) {
98    if (message.expectsResponse()) {
99      if (this.incomingReceiver_) {
100        this.incomingReceiver_.acceptWithResponder(message, this);
101      } else {
102        // If we receive a request expecting a response when the client is not
103        // listening, then we have no choice but to tear down the pipe.
104        this.close();
105      }
106    } else if (message.isResponse()) {
107      var reader = new MessageReader(message);
108      var requestID = reader.requestID;
109      var completer = this.completers_.get(requestID);
110      this.completers_.delete(requestID);
111      completer.resolve(message);
112    } else {
113      if (this.incomingReceiver_)
114        this.incomingReceiver_.accept(message);
115    }
116  }
117
118  Router.prototype.handleInvalidIncomingMessage_ = function(message, error) {
119    this.close();
120  }
121
122  Router.prototype.handleConnectionError_ = function(result) {
123    this.completers_.forEach(function(value) {
124      value.reject(result);
125    });
126    if (this.errorHandler_)
127      this.errorHandler_();
128    this.close();
129  };
130
131  // The TestRouter subclass is only intended to be used in unit tests.
132  // It defeats valid message handling and delgates invalid message handling.
133
134  function TestRouter(handle, connectorFactory) {
135    Router.call(this, handle, connectorFactory);
136  }
137
138  TestRouter.prototype = Object.create(Router.prototype);
139
140  TestRouter.prototype.handleValidIncomingMessage_ = function() {
141  };
142
143  TestRouter.prototype.handleInvalidIncomingMessage_ =
144      function(message, error) {
145        this.validationErrorHandler(error);
146      };
147
148  var exports = {};
149  exports.Router = Router;
150  exports.TestRouter = TestRouter;
151  return exports;
152});
153