Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(285)

Side by Side Diff: mojo/public/js/router.js

Issue 2788403002: Revert of Introduce InterfaceEndpointClient(IEC), InterfaceEndpointHandle and (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 define("mojo/public/js/router", [ 5 define("mojo/public/js/router", [
6 "console",
7 "mojo/public/js/codec",
8 "mojo/public/js/core",
6 "mojo/public/js/connector", 9 "mojo/public/js/connector",
7 "mojo/public/js/core", 10 "mojo/public/js/lib/control_message_handler",
8 "mojo/public/js/interface_types",
9 "mojo/public/js/lib/interface_endpoint_handle",
10 "mojo/public/js/lib/pipe_control_message_handler",
11 "mojo/public/js/lib/pipe_control_message_proxy",
12 "mojo/public/js/validator", 11 "mojo/public/js/validator",
13 "timer", 12 ], function(console, codec, core, connector, controlMessageHandler, validator) {
14 ], function(connector, core, types, interfaceEndpointHandle,
15 controlMessageHandler, controlMessageProxy, validator, timer) {
16 13
17 var Connector = connector.Connector; 14 var Connector = connector.Connector;
18 var PipeControlMessageHandler = 15 var MessageReader = codec.MessageReader;
19 controlMessageHandler.PipeControlMessageHandler;
20 var PipeControlMessageProxy = controlMessageProxy.PipeControlMessageProxy;
21 var Validator = validator.Validator; 16 var Validator = validator.Validator;
22 var InterfaceEndpointHandle = interfaceEndpointHandle.InterfaceEndpointHandle; 17 var ControlMessageHandler = controlMessageHandler.ControlMessageHandler;
23 18
24 /** 19 function Router(handle, interface_version, connectorFactory) {
25 * The state of |endpoint|. If both the endpoint and its peer have been 20 if (!core.isHandle(handle))
26 * closed, removes it from |endpoints_|. 21 throw new Error("Router constructor: Not a handle");
27 * @enum {string} 22 if (connectorFactory === undefined)
28 */ 23 connectorFactory = Connector;
29 var EndpointStateUpdateType = { 24 this.connector_ = new connectorFactory(handle);
30 ENDPOINT_CLOSED: 'endpoint_closed', 25 this.incomingReceiver_ = null;
31 PEER_ENDPOINT_CLOSED: 'peer_endpoint_closed' 26 this.errorHandler_ = null;
32 }; 27 this.nextRequestID_ = 0;
28 this.completers_ = new Map();
29 this.payloadValidators_ = [];
30 this.testingController_ = null;
33 31
34 function check(condition, output) { 32 if (interface_version !== undefined) {
35 if (!condition) { 33 this.controlMessageHandler_ = new
36 // testharness.js does not rethrow errors so the error stack needs to be 34 ControlMessageHandler(interface_version);
37 // included as a string in the error we throw for debugging layout tests.
38 throw new Error((new Error()).stack);
39 } 35 }
36
37 this.connector_.setIncomingReceiver({
38 accept: this.handleIncomingMessage_.bind(this),
39 });
40 this.connector_.setErrorHandler({
41 onError: this.handleConnectionError_.bind(this),
42 });
40 } 43 }
41 44
42 function InterfaceEndpoint(router, interfaceId) { 45 Router.prototype.close = function() {
43 this.router_ = router; 46 this.completers_.clear(); // Drop any responders.
44 this.id = interfaceId; 47 this.connector_.close();
45 this.closed = false; 48 this.testingController_ = null;
46 this.peerClosed = false;
47 this.handleCreated = false;
48 this.disconnectReason = null;
49 this.client = null;
50 }
51
52 InterfaceEndpoint.prototype.sendMessage = function(message) {
53 message.setInterfaceId(this.id);
54 return this.router_.connector_.accept(message);
55 };
56
57 function Router(handle, setInterfaceIdNamespaceBit) {
58 if (!core.isHandle(handle)) {
59 throw new Error("Router constructor: Not a handle");
60 }
61 if (setInterfaceIdNamespaceBit === undefined) {
62 setInterfaceIdNamespaceBit = false;
63 }
64
65 this.connector_ = new Connector(handle);
66
67 this.connector_.setIncomingReceiver({
68 accept: this.accept.bind(this),
69 });
70 this.connector_.setErrorHandler({
71 onError: this.onPipeConnectionError.bind(this),
72 });
73
74 this.setInterfaceIdNamespaceBit_ = setInterfaceIdNamespaceBit;
75 this.controlMessageHandler_ = new PipeControlMessageHandler(this);
76 this.controlMessageProxy_ = new PipeControlMessageProxy(this.connector_);
77 this.nextInterfaceIdValue = 1;
78 this.encounteredError_ = false;
79 this.endpoints_ = new Map();
80 }
81
82 Router.prototype.attachEndpointClient = function(
83 interfaceEndpointHandle, interfaceEndpointClient) {
84 check(types.isValidInterfaceId(interfaceEndpointHandle.id()));
85 check(interfaceEndpointClient);
86
87 var endpoint = this.endpoints_.get(interfaceEndpointHandle.id());
88 check(endpoint);
89 check(!endpoint.client);
90 check(!endpoint.closed);
91 endpoint.client = interfaceEndpointClient;
92
93 if (endpoint.peerClosed) {
94 timer.createOneShot(0,
95 endpoint.client.notifyError.bind(endpoint.client));
96 }
97
98 return endpoint;
99 };
100
101 Router.prototype.detachEndpointClient = function(
102 interfaceEndpointHandle) {
103 check(types.isValidInterfaceId(interfaceEndpointHandle.id()));
104 var endpoint = this.endpoints_.get(interfaceEndpointHandle.id());
105 check(endpoint);
106 check(endpoint.client);
107 check(!endpoint.closed);
108
109 endpoint.client = null;
110 };
111
112 Router.prototype.createLocalEndpointHandle = function(
113 interfaceId) {
114 if (!types.isValidInterfaceId(interfaceId)) {
115 return new InterfaceEndpointHandle();
116 }
117
118 var endpoint = this.endpoints_.get(interfaceId);
119
120 if (!endpoint) {
121 endpoint = new InterfaceEndpoint(this, interfaceId);
122 this.endpoints_.set(interfaceId, endpoint);
123
124 check(!endpoint.handleCreated);
125
126 if (this.encounteredError_) {
127 this.updateEndpointStateMayRemove(endpoint,
128 EndpointStateUpdateType.PEER_ENDPOINT_CLOSED);
129 }
130 } else {
131 // If the endpoint already exist, it is because we have received a
132 // notification that the peer endpoint has closed.
133 check(!endpoint.closed);
134 check(endpoint.peerClosed);
135
136 if (endpoint.handleCreated) {
137 return new InterfaceEndpointHandle();
138 }
139 }
140
141 endpoint.handleCreated = true;
142 return new InterfaceEndpointHandle(interfaceId, this);
143 }; 49 };
144 50
145 Router.prototype.accept = function(message) { 51 Router.prototype.accept = function(message) {
52 this.connector_.accept(message);
53 };
54
55 Router.prototype.reject = function(message) {
56 // TODO(mpcomplete): no way to trasmit errors over a Connection.
57 };
58
59 Router.prototype.acceptAndExpectResponse = function(message) {
60 // Reserve 0 in case we want it to convey special meaning in the future.
61 var requestID = this.nextRequestID_++;
62 if (requestID == 0)
63 requestID = this.nextRequestID_++;
64
65 message.setRequestID(requestID);
66 var result = this.connector_.accept(message);
67 if (!result)
68 return Promise.reject(Error("Connection error"));
69
70 var completer = {};
71 this.completers_.set(requestID, completer);
72 return new Promise(function(resolve, reject) {
73 completer.resolve = resolve;
74 completer.reject = reject;
75 });
76 };
77
78 Router.prototype.setIncomingReceiver = function(receiver) {
79 this.incomingReceiver_ = receiver;
80 };
81
82 Router.prototype.setPayloadValidators = function(payloadValidators) {
83 this.payloadValidators_ = payloadValidators;
84 };
85
86 Router.prototype.setErrorHandler = function(handler) {
87 this.errorHandler_ = handler;
88 };
89
90 Router.prototype.encounteredError = function() {
91 return this.connector_.encounteredError();
92 };
93
94 Router.prototype.enableTestingMode = function() {
95 this.testingController_ = new RouterTestingController(this.connector_);
96 return this.testingController_;
97 };
98
99 Router.prototype.handleIncomingMessage_ = function(message) {
100 var noError = validator.validationError.NONE;
146 var messageValidator = new Validator(message); 101 var messageValidator = new Validator(message);
147 var err = messageValidator.validateMessageHeader(); 102 var err = messageValidator.validateMessageHeader();
103 for (var i = 0; err === noError && i < this.payloadValidators_.length; ++i)
104 err = this.payloadValidators_[i](messageValidator);
148 105
149 var ok = false; 106 if (err == noError)
150 if (err !== validator.validationError.NONE) { 107 this.handleValidIncomingMessage_(message);
151 validator.reportValidationError(err); 108 else
152 } else if (controlMessageHandler.isPipeControlMessage(message)) { 109 this.handleInvalidIncomingMessage_(message, err);
153 ok = this.controlMessageHandler_.accept(message);
154 } else {
155 var interfaceId = message.getInterfaceId();
156 var endpoint = this.endpoints_.get(interfaceId);
157 if (!endpoint || endpoint.closed) {
158 return true;
159 }
160
161 if (!endpoint.client) {
162 // We need to wait until a client is attached in order to dispatch
163 // further messages.
164 return false;
165 }
166 ok = endpoint.client.handleIncomingMessage_(message);
167 }
168
169 if (!ok) {
170 this.handleInvalidIncomingMessage_();
171 }
172 return ok;
173 }; 110 };
174 111
175 Router.prototype.close = function() { 112 Router.prototype.handleValidIncomingMessage_ = function(message) {
176 this.connector_.close(); 113 if (this.testingController_)
177 // Closing the message pipe won't trigger connection error handler. 114 return;
178 // Explicitly call onPipeConnectionError() so that associated endpoints 115
179 // will get notified. 116 if (message.expectsResponse()) {
180 this.onPipeConnectionError(); 117 if (controlMessageHandler.isControlMessage(message)) {
118 if (this.controlMessageHandler_) {
119 this.controlMessageHandler_.acceptWithResponder(message, this);
120 } else {
121 this.close();
122 }
123 } else if (this.incomingReceiver_) {
124 this.incomingReceiver_.acceptWithResponder(message, this);
125 } else {
126 // If we receive a request expecting a response when the client is not
127 // listening, then we have no choice but to tear down the pipe.
128 this.close();
129 }
130 } else if (message.isResponse()) {
131 var reader = new MessageReader(message);
132 var requestID = reader.requestID;
133 var completer = this.completers_.get(requestID);
134 if (completer) {
135 this.completers_.delete(requestID);
136 completer.resolve(message);
137 } else {
138 console.log("Unexpected response with request ID: " + requestID);
139 }
140 } else {
141 if (controlMessageHandler.isControlMessage(message)) {
142 if (this.controlMessageHandler_) {
143 var ok = this.controlMessageHandler_.accept(message);
144 if (ok) return;
145 }
146 this.close();
147 } else if (this.incomingReceiver_) {
148 this.incomingReceiver_.accept(message);
149 }
150 }
181 }; 151 };
182 152
183 Router.prototype.waitForNextMessageForTesting = function() { 153 Router.prototype.handleInvalidIncomingMessage_ = function(message, error) {
184 this.connector_.waitForNextMessageForTesting(); 154 if (!this.testingController_) {
185 };
186
187 Router.prototype.handleInvalidIncomingMessage_ = function(message) {
188 if (!validator.isTestingMode()) {
189 // TODO(yzshen): Consider notifying the embedder. 155 // TODO(yzshen): Consider notifying the embedder.
190 // TODO(yzshen): This should also trigger connection error handler. 156 // TODO(yzshen): This should also trigger connection error handler.
191 // Consider making accept() return a boolean and let the connector deal 157 // Consider making accept() return a boolean and let the connector deal
192 // with this, as the C++ code does. 158 // with this, as the C++ code does.
159 console.log("Invalid message: " + validator.validationError[error]);
160
193 this.close(); 161 this.close();
194 return; 162 return;
195 } 163 }
164
165 this.testingController_.onInvalidIncomingMessage(error);
196 }; 166 };
197 167
198 Router.prototype.onPeerAssociatedEndpointClosed = function(interfaceId, 168 Router.prototype.handleConnectionError_ = function(result) {
199 reason) { 169 this.completers_.forEach(function(value) {
200 check(!types.isMasterInterfaceId(interfaceId) || reason); 170 value.reject(result);
201 171 });
202 var endpoint = this.endpoints_.get(interfaceId); 172 if (this.errorHandler_)
203 if (!endpoint) { 173 this.errorHandler_();
204 endpoint = new InterfaceEndpoint(this, interfaceId); 174 this.close();
205 this.endpoints_.set(interfaceId, endpoint);
206 }
207
208 if (reason) {
209 endpoint.disconnectReason = reason;
210 }
211
212 if (!endpoint.peerClosed) {
213 if (endpoint.client) {
214 timer.createOneShot(0,
215 endpoint.client.notifyError.bind(endpoint.client, reason));
216 }
217 this.updateEndpointStateMayRemove(endpoint,
218 EndpointStateUpdateType.PEER_ENDPOINT_CLOSED);
219 }
220 return true;
221 }; 175 };
222 176
223 Router.prototype.onPipeConnectionError = function() { 177 // The RouterTestingController is used in unit tests. It defeats valid message
224 this.encounteredError_ = true; 178 // handling and delgates invalid message handling.
225 179
226 for (var endpoint of this.endpoints_.values()) { 180 function RouterTestingController(connector) {
227 if (endpoint.client) { 181 this.connector_ = connector;
228 timer.createOneShot(0, 182 this.invalidMessageHandler_ = null;
229 endpoint.client.notifyError.bind(endpoint.client, 183 }
230 endpoint.disconnectReason)); 184
231 } 185 RouterTestingController.prototype.waitForNextMessage = function() {
232 this.updateEndpointStateMayRemove(endpoint, 186 this.connector_.waitForNextMessageForTesting();
233 EndpointStateUpdateType.PEER_ENDPOINT_CLOSED);
234 }
235 }; 187 };
236 188
237 Router.prototype.closeEndpointHandle = function(interfaceId, reason) { 189 RouterTestingController.prototype.setInvalidIncomingMessageHandler =
238 if (!types.isValidInterfaceId(interfaceId)) { 190 function(callback) {
239 return; 191 this.invalidMessageHandler_ = callback;
240 }
241 var endpoint = this.endpoints_.get(interfaceId);
242 check(endpoint);
243 check(!endpoint.client);
244 check(!endpoint.closed);
245
246 this.updateEndpointStateMayRemove(endpoint,
247 EndpointStateUpdateType.ENDPOINT_CLOSED);
248
249 if (!types.isMasterInterfaceId(interfaceId) || reason) {
250 this.controlMessageProxy_.notifyPeerEndpointClosed(interfaceId, reason);
251 }
252 }; 192 };
253 193
254 Router.prototype.updateEndpointStateMayRemove = function(endpoint, 194 RouterTestingController.prototype.onInvalidIncomingMessage =
255 endpointStateUpdateType) { 195 function(error) {
256 if (endpointStateUpdateType === EndpointStateUpdateType.ENDPOINT_CLOSED) { 196 if (this.invalidMessageHandler_)
257 endpoint.closed = true; 197 this.invalidMessageHandler_(error);
258 } else {
259 endpoint.peerClosed = true;
260 }
261 if (endpoint.closed && endpoint.peerClosed) {
262 this.endpoints_.delete(endpoint.id);
263 }
264 }; 198 };
265 199
266 var exports = {}; 200 var exports = {};
267 exports.Router = Router; 201 exports.Router = Router;
268 return exports; 202 return exports;
269 }); 203 });
OLDNEW
« no previous file with comments | « mojo/public/js/lib/pipe_control_message_proxy.js ('k') | mojo/public/js/tests/validation_unittest.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698