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

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

Issue 2744963002: Introduce InterfaceEndpointClient(IEC), InterfaceEndpointHandle and (Closed)
Patch Set: Code formatting. Update State close in IEH. Use IEC.closeWithReason in Binding's close/reset. Created 3 years, 9 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", 6 "mojo/public/js/connector",
7 "mojo/public/js/codec",
8 "mojo/public/js/core", 7 "mojo/public/js/core",
9 "mojo/public/js/connector", 8 "mojo/public/js/interface_types",
10 "mojo/public/js/lib/control_message_handler", 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",
11 "mojo/public/js/validator", 12 "mojo/public/js/validator",
12 ], function(console, codec, core, connector, controlMessageHandler, validator) { 13 "timer",
14 ], function(connector, core, types, interfaceEndpointHandle,
15 controlMessageHandler, controlMessageProxy, validator, timer) {
13 16
14 var Connector = connector.Connector; 17 var Connector = connector.Connector;
15 var MessageReader = codec.MessageReader; 18 var PipeControlMessageHandler =
19 controlMessageHandler.PipeControlMessageHandler;
20 var PipeControlMessageProxy = controlMessageProxy.PipeControlMessageProxy;
16 var Validator = validator.Validator; 21 var Validator = validator.Validator;
17 var ControlMessageHandler = controlMessageHandler.ControlMessageHandler; 22 var InterfaceEndpointHandle = interfaceEndpointHandle.InterfaceEndpointHandle;
18 23
19 function Router(handle, interface_version, connectorFactory) { 24 /**
25 * The state of |endpoint|. If both the endpoint and its peer have been
26 * closed, removes it from |endpoints_|.
27 * @enum {string}
28 */
29 var EndpointStateUpdateType = {
30 ENDPOINT_CLOSED: 'endpoint_closed',
31 PEER_ENDPOINT_CLOSED: 'peer_endpoint_closed'
32 };
33
34 function InterfaceEndpoint(router, interfaceId) {
35 this.router_ = router;
36 this.id = interfaceId;
37 this.closed = false;
38 this.peerClosed = false;
39 this.handleCreated = false;
40 this.disconnectReason = null;
41 this.client = null;
42 this.eventSignalled = false;
yzshen1 2017/03/28 00:46:00 This is not needed.
wangjimmy 2017/03/29 17:01:18 Done.
43 }
44
45 InterfaceEndpoint.prototype.sendMessage = function(message) {
46 message.setInterfaceId(this.id);
47 return this.router_.connector_.accept(message);
48 };
49
50 function AssociatedGroupController() {
yzshen1 2017/03/28 00:46:00 Because we don't have multiple implementation of A
wangjimmy 2017/03/29 17:01:18 Done. Merged it with Router.
51 }
52
53 AssociatedGroupController.prototype.attachEndpointClient = function(
54 interfaceEndpointHandle, interfaceEndpointClient) {
55 var endpoint = this.endpoints_.get(interfaceEndpointHandle.id());
56 if (!endpoint) return;
57
58 endpoint.client = interfaceEndpointClient;
59
60 if (endpoint.peerClosed) {
61 timer.createOneShot(0,
62 endpoint.client.notifyError.bind(endpoint.client));
63 }
64
65 return endpoint;
66 };
67
68 AssociatedGroupController.prototype.detachEndpointClient = function(
69 interfaceEndpointHandle) {
70 var endpoint = this.endpoints_.get(interfaceEndpointHandle.id());
71 endpoint.client = null;
72 };
73
74 AssociatedGroupController.prototype.createLocalEndpointHandle = function(
75 interfaceId) {
76 if (!types.isValidInterfaceId(interfaceId)) {
77 return new InterfaceEndpointHandle();
78 }
79
80 var endpoint = this.endpoints_.get(interfaceId);
81
82 if (!endpoint) {
83 endpoint = new InterfaceEndpoint(this, interfaceId);
84 this.endpoints_.set(interfaceId, endpoint);
85
86 if (this.encounteredError_) {
87 this.updateEndpointStateMayRemove(endpoint,
88 EndpointStateUpdateType.PEER_ENDPOINT_CLOSED);
89 }
90 } else {
yzshen1 2017/03/28 00:46:00 Thinking about it some more, I think maybe it make
wangjimmy 2017/03/29 17:01:18 Done. I agree, it would help with debugging. Added
91 // If the endpoint already exist, it is because we have received a
92 // notification that the peer endpoint has closed.
93 if (endpoint.handleCreated) {
94 return new InterfaceEndpointHandle();
95 }
96 }
97
98 endpoint.handleCreated = true;
99 return new InterfaceEndpointHandle(interfaceId, this);
100 };
101
102 function Router(handle, setInterfaceIdNamespaceBit,connectorFactory) {
yzshen1 2017/03/28 00:46:00 Do we still need this "connectorFactory" param? (I
wangjimmy 2017/03/29 17:01:18 Done. Removed it. We don't use this anywhere. It w
20 if (!core.isHandle(handle)) 103 if (!core.isHandle(handle))
21 throw new Error("Router constructor: Not a handle"); 104 throw new Error("Router constructor: Not a handle");
22 if (connectorFactory === undefined) 105 if (connectorFactory === undefined)
23 connectorFactory = Connector; 106 connectorFactory = Connector;
24 this.connector_ = new connectorFactory(handle); 107 this.connector_ = new connectorFactory(handle);
25 this.incomingReceiver_ = null;
26 this.errorHandler_ = null;
27 this.nextRequestID_ = 0;
28 this.completers_ = new Map();
29 this.payloadValidators_ = [];
30 this.testingController_ = null;
31
32 if (interface_version !== undefined) {
33 this.controlMessageHandler_ = new
34 ControlMessageHandler(interface_version);
35 }
36 108
37 this.connector_.setIncomingReceiver({ 109 this.connector_.setIncomingReceiver({
38 accept: this.handleIncomingMessage_.bind(this), 110 accept: this.accept.bind(this),
39 }); 111 });
40 this.connector_.setErrorHandler({ 112 this.connector_.setErrorHandler({
41 onError: this.handleConnectionError_.bind(this), 113 onError: this.onPipeConnectionError.bind(this),
42 }); 114 });
115
116 if (setInterfaceIdNamespaceBit === undefined) {
117 setInterfaceIdNamespaceBit = false;
118 }
119 this.setInterfaceIdNamespaceBit_ = setInterfaceIdNamespaceBit;
120 this.controlMessageHandler_ = new PipeControlMessageHandler(this);
121 this.controlMessageProxy_ = new PipeControlMessageProxy(this.connector_);
122 this.nextInterfaceIdValue = 1;
123 this.encounteredError_ = false;
124 this.endpoints_ = new Map();
43 } 125 }
44 126
45 Router.prototype.close = function() { 127 Router.prototype = Object.create(AssociatedGroupController.prototype);
46 this.completers_.clear(); // Drop any responders.
47 this.connector_.close();
48 this.testingController_ = null;
49 };
50 128
51 Router.prototype.accept = function(message) { 129 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;
101 var messageValidator = new Validator(message); 130 var messageValidator = new Validator(message);
102 var err = messageValidator.validateMessageHeader(); 131 var err = messageValidator.validateMessageHeader();
103 for (var i = 0; err === noError && i < this.payloadValidators_.length; ++i) 132
104 err = this.payloadValidators_[i](messageValidator); 133 var ok = false;
105 134 if (err !== validator.validationError.NONE) {
106 if (err == noError) 135 validator.reportValidationError(err);
107 this.handleValidIncomingMessage_(message); 136 } else if (controlMessageHandler.isPipeControlMessage(message)) {
108 else 137 ok = this.controlMessageHandler_.accept(message);
109 this.handleInvalidIncomingMessage_(message, err);
110 };
111
112 Router.prototype.handleValidIncomingMessage_ = function(message) {
113 if (this.testingController_)
114 return;
115
116 if (message.expectsResponse()) {
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 { 138 } else {
141 if (controlMessageHandler.isControlMessage(message)) { 139 var interfaceId = message.getInterfaceId();
142 if (this.controlMessageHandler_) { 140 var endpoint = this.endpoints_.get(interfaceId);
143 var ok = this.controlMessageHandler_.accept(message); 141 if (!endpoint || endpoint.closed) {
144 if (ok) return; 142 return true;
145 } 143 }
146 this.close(); 144
147 } else if (this.incomingReceiver_) { 145 if (!endpoint.client) {
148 this.incomingReceiver_.accept(message); 146 // We need to wait until a client is attached in order to dispatch
149 } 147 // further messages.
150 } 148 return false;
151 }; 149 }
152 150 ok = endpoint.client.handleIncomingMessage_(message);
153 Router.prototype.handleInvalidIncomingMessage_ = function(message, error) { 151 }
154 if (!this.testingController_) { 152
153 if (!ok) {
154 this.handleInvalidIncomingMessage_();
155 }
156 return ok;
157 };
158
159 Router.prototype.close = function() {
160 this.connector_.close();
161 // Closing the message pipe won't trigger connection error handler.
162 // Explicitly call onPipeConnectionError() so that associated endpoints
163 // will get notified.
164 this.onPipeConnectionError();
165 };
166
167 Router.prototype.waitForNextMessageForTesting = function() {
168 this.connector_.waitForNextMessageForTesting();
169 };
170
171 Router.prototype.handleInvalidIncomingMessage_ = function(message) {
172 if (!validator.isTestingMode()) {
155 // TODO(yzshen): Consider notifying the embedder. 173 // TODO(yzshen): Consider notifying the embedder.
156 // TODO(yzshen): This should also trigger connection error handler. 174 // TODO(yzshen): This should also trigger connection error handler.
157 // Consider making accept() return a boolean and let the connector deal 175 // Consider making accept() return a boolean and let the connector deal
158 // with this, as the C++ code does. 176 // with this, as the C++ code does.
159 console.log("Invalid message: " + validator.validationError[error]);
160
161 this.close(); 177 this.close();
162 return; 178 return;
163 } 179 }
164 180 };
165 this.testingController_.onInvalidIncomingMessage(error); 181
166 }; 182 Router.prototype.onPeerAssociatedEndpointClosed = function(interfaceId,
167 183 reason) {
168 Router.prototype.handleConnectionError_ = function(result) { 184 var endpoint = this.endpoints_.get(interfaceId);
yzshen1 2017/03/28 00:46:00 If the endpoint doesn't exist, we need to insert i
wangjimmy 2017/03/29 17:01:18 Done.
169 this.completers_.forEach(function(value) { 185 if (reason && endpoint) {
170 value.reject(result); 186 endpoint.disconnectReason = reason;
171 }); 187 }
172 if (this.errorHandler_) 188
173 this.errorHandler_(); 189 if (!endpoint.peerClosed && endpoint.client) {
yzshen1 2017/03/28 00:46:00 This is incorrect, even if endpoint.client is not
wangjimmy 2017/03/29 17:01:18 Done.
174 this.close(); 190 timer.createOneShot(0,
175 }; 191 endpoint.client.notifyError.bind(endpoint.client, reason));
176 192 this.updateEndpointStateMayRemove(endpoint,
177 // The RouterTestingController is used in unit tests. It defeats valid message 193 EndpointStateUpdateType.PEER_ENDPOINT_CLOSED);
178 // handling and delgates invalid message handling. 194 }
179 195 return true;
180 function RouterTestingController(connector) { 196 };
181 this.connector_ = connector; 197
182 this.invalidMessageHandler_ = null; 198 Router.prototype.onPipeConnectionError = function() {
183 } 199 this.encounteredError_ = true;
184 200
185 RouterTestingController.prototype.waitForNextMessage = function() { 201 for (var endpoint of this.endpoints_.values()) {
186 this.connector_.waitForNextMessageForTesting(); 202 if (endpoint.client) {
187 }; 203 timer.createOneShot(0,
188 204 endpoint.client.notifyError.bind(endpoint.client,
189 RouterTestingController.prototype.setInvalidIncomingMessageHandler = 205 endpoint.disconnectReason));
190 function(callback) { 206 }
191 this.invalidMessageHandler_ = callback; 207 this.updateEndpointStateMayRemove(endpoint,
192 }; 208 EndpointStateUpdateType.PEER_ENDPOINT_CLOSED);
193 209 }
194 RouterTestingController.prototype.onInvalidIncomingMessage = 210 };
195 function(error) { 211
196 if (this.invalidMessageHandler_) 212 Router.prototype.closeEndpointHandle = function(interfaceId, reason) {
197 this.invalidMessageHandler_(error); 213 if (!types.isValidInterfaceId(interfaceId)) {
214 return;
215 }
216 var endpoint = this.endpoints_.get(interfaceId);
217 this.updateEndpointStateMayRemove(endpoint,
218 EndpointStateUpdateType.ENDPOINT_CLOSED);
219
220 if (!types.isMasterInterfaceId(interfaceId) || reason) {
221 this.controlMessageProxy_.notifyPeerEndpointClosed(interfaceId, reason);
222 }
223 };
224
225 Router.prototype.updateEndpointStateMayRemove = function(endpoint,
226 endpointStateUpdateType) {
227 if (endpointStateUpdateType === EndpointStateUpdateType.ENDPOINT_CLOSED) {
228 endpoint.closed = true;
229 } else {
230 endpoint.peerClosed = true;
231 }
232 if (endpoint.closed && endpoint.peerClosed) {
233 this.endpoints_.delete(endpoint.id);
234 }
198 }; 235 };
199 236
200 var exports = {}; 237 var exports = {};
201 exports.Router = Router; 238 exports.Router = Router;
202 return exports; 239 return exports;
203 }); 240 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698