OLD | NEW |
| (Empty) |
1 // Copyright 2015 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 | |
5 package application | |
6 | |
7 import ( | |
8 "log" | |
9 | |
10 "mojo/public/go/bindings" | |
11 "mojo/public/go/system" | |
12 | |
13 sp "mojo/public/interfaces/application/service_provider" | |
14 "mojo/public/interfaces/bindings/service_describer" | |
15 ) | |
16 | |
17 type connectionInfo struct { | |
18 requestorURL string | |
19 connectionURL string | |
20 } | |
21 | |
22 // RequestorURL returns the URL of application that established the connection. | |
23 func (c *connectionInfo) RequestorURL() string { | |
24 return c.requestorURL | |
25 } | |
26 | |
27 // ConnectionURL returns the URL that was used by the source application to | |
28 // establish a connection to the destination application. | |
29 func (c *connectionInfo) ConnectionURL() string { | |
30 return c.connectionURL | |
31 } | |
32 | |
33 // ServiceRequest is an interface request for a specified mojo service. | |
34 type ServiceRequest interface { | |
35 // Name returns the name of requested mojo service. | |
36 Name() string | |
37 | |
38 // ServiceDescription returns a service description, which can be querie
d to | |
39 // examine the type information of the service associated with this | |
40 // ServiceRequest. | |
41 // Note: In some implementations, the ServiceDescription returned will n
ot | |
42 // provide type information. Methods called may return nil or an error. | |
43 ServiceDescription() service_describer.ServiceDescription | |
44 | |
45 // PassMessagePipe passes ownership of the underlying message pipe | |
46 // handle to the newly created handle object, invalidating the | |
47 // underlying handle object in the process. | |
48 PassMessagePipe() system.MessagePipeHandle | |
49 } | |
50 | |
51 // ServiceFactory provides implementation of a mojo service. | |
52 type ServiceFactory interface { | |
53 // Name returns the name of provided mojo service. | |
54 Name() string | |
55 | |
56 // ServiceDescription returns a service description, which can be querie
d to | |
57 // examine the type information of the mojo service associated with this | |
58 // ServiceFactory. | |
59 // Note: In some implementations, the ServiceDescription returned will n
ot | |
60 // provide type information. Methods called may return nil or an error. | |
61 ServiceDescription() service_describer.ServiceDescription | |
62 | |
63 // Create binds an implementation of mojo service to the provided | |
64 // message pipe and runs it. | |
65 Create(pipe system.MessagePipeHandle) | |
66 } | |
67 | |
68 // Connection represents a connection to another application. An instance of | |
69 // this struct is passed to Delegate's AcceptConnection() function each time a | |
70 // connection is made to this application. | |
71 // TODO(vtl): This is largely overkill now that we no longer have "wrong way" | |
72 // service providers (a.k.a. "exposed services"). Things should be simplified. | |
73 // https://github.com/domokit/mojo/issues/762 | |
74 type Connection struct { | |
75 connectionInfo | |
76 // Request for local services. Is valid until ProvideServices is called. | |
77 servicesRequest *sp.ServiceProvider_Request | |
78 // Indicates that ProvideServices function was already called. | |
79 servicesProvided bool | |
80 localServices *bindings.Stub | |
81 outgoingConnection *OutgoingConnection | |
82 isClosed bool | |
83 // Is set if ProvideServicesWithDescriber was called. | |
84 // Note: When DescribeServices is invoked, some implementations may retu
rn | |
85 // incomplete ServiceDescriptions. For example, if type information was
not | |
86 // generated, then the methods called may return nil or an error. | |
87 describer *ServiceDescriberFactory | |
88 } | |
89 | |
90 func newConnection(requestorURL string, services sp.ServiceProvider_Request, res
olvedURL string) *Connection { | |
91 info := connectionInfo{ | |
92 requestorURL, | |
93 resolvedURL, | |
94 } | |
95 return &Connection{ | |
96 connectionInfo: info, | |
97 servicesRequest: &services, | |
98 outgoingConnection: &OutgoingConnection{ | |
99 info, | |
100 nil, | |
101 }, | |
102 } | |
103 } | |
104 | |
105 // ProvideServices starts a service provider on a separate goroutine that | |
106 // provides given services to the remote application. Returns a pointer to | |
107 // outgoing connection that can be used to connect to services provided by | |
108 // remote application. | |
109 // Panics if called more than once. | |
110 func (c *Connection) ProvideServices(services ...ServiceFactory) *OutgoingConnec
tion { | |
111 if c.servicesProvided { | |
112 panic("ProvideServices or ProvideServicesWithDescriber can be ca
lled only once") | |
113 } | |
114 c.servicesProvided = true | |
115 if c.servicesRequest == nil { | |
116 return c.outgoingConnection | |
117 } | |
118 if len(services) == 0 { | |
119 c.servicesRequest.PassMessagePipe().Close() | |
120 return c.outgoingConnection | |
121 } | |
122 | |
123 provider := &serviceProviderImpl{ | |
124 make(map[string]ServiceFactory), | |
125 } | |
126 for _, service := range services { | |
127 provider.AddService(service) | |
128 } | |
129 c.localServices = sp.NewServiceProviderStub(*c.servicesRequest, provider
, bindings.GetAsyncWaiter()) | |
130 go func() { | |
131 for { | |
132 if err := c.localServices.ServeRequest(); err != nil { | |
133 connectionError, ok := err.(*bindings.Connection
Error) | |
134 if !ok || !connectionError.Closed() { | |
135 log.Println(err) | |
136 } | |
137 break | |
138 } | |
139 } | |
140 }() | |
141 return c.outgoingConnection | |
142 } | |
143 | |
144 // ProvideServicesWithDescriber is an alternative to ProvideServices that, in | |
145 // addition to providing the given services, also provides type descriptions of | |
146 // the given services. See ProvideServices for a description of what it does. | |
147 // This method will invoke ProvideServices after appending the ServiceDescriber | |
148 // service to |services|. See service_describer.mojom for a description of the | |
149 // ServiceDescriber interface. Client Mojo applications can choose to connect | |
150 // to this ServiceDescriber interface, which describes the other services listed | |
151 // in |services|. | |
152 // Note that the implementation of ServiceDescriber will make the optional | |
153 // DeclarationData available on all types, and in particular, the names used in | |
154 // .mojom files will be exposed to client applications. | |
155 func (c *Connection) ProvideServicesWithDescriber(services ...ServiceFactory) *O
utgoingConnection { | |
156 if c.servicesProvided { | |
157 panic("ProvideServices or ProvideServicesWithDescriber can be ca
lled only once") | |
158 } | |
159 mapping := make(map[string]service_describer.ServiceDescription) | |
160 for _, service := range services { | |
161 mapping[service.Name()] = service.ServiceDescription() | |
162 } | |
163 c.describer = newServiceDescriberFactory(mapping) | |
164 servicesWithDescriber := append(services, &service_describer.ServiceDesc
riber_ServiceFactory{c.describer}) | |
165 | |
166 return c.ProvideServices(servicesWithDescriber...) | |
167 } | |
168 | |
169 // Close closes both incoming and outgoing parts of the connection. | |
170 func (c *Connection) Close() { | |
171 if c.servicesRequest != nil { | |
172 c.servicesRequest.Close() | |
173 } | |
174 if c.localServices != nil { | |
175 c.localServices.Close() | |
176 } | |
177 if c.describer != nil { | |
178 c.describer.Close() | |
179 } | |
180 if c.outgoingConnection.remoteServices != nil { | |
181 c.outgoingConnection.remoteServices.Close_Proxy() | |
182 } | |
183 c.isClosed = true | |
184 } | |
185 | |
186 // OutgoingConnection represents outgoing part of connection to another | |
187 // application. In order to close it close the |Connection| object that returned | |
188 // this |OutgoingConnection|. | |
189 type OutgoingConnection struct { | |
190 connectionInfo | |
191 remoteServices *sp.ServiceProvider_Proxy | |
192 } | |
193 | |
194 // ConnectToService asks remote application to provide a service through the | |
195 // message pipe endpoint supplied by the caller. | |
196 func (c *OutgoingConnection) ConnectToService(request ServiceRequest) { | |
197 pipe := request.PassMessagePipe() | |
198 if c.remoteServices == nil { | |
199 pipe.Close() | |
200 return | |
201 } | |
202 c.remoteServices.ConnectToService(request.Name(), pipe) | |
203 } | |
204 | |
205 // serviceProviderImpl is an implementation of mojo ServiceProvider interface. | |
206 type serviceProviderImpl struct { | |
207 factories map[string]ServiceFactory | |
208 } | |
209 | |
210 // Mojo ServiceProvider implementation. | |
211 func (sp *serviceProviderImpl) ConnectToService(name string, messagePipe system.
MessagePipeHandle) error { | |
212 factory, ok := sp.factories[name] | |
213 if !ok { | |
214 messagePipe.Close() | |
215 return nil | |
216 } | |
217 factory.Create(messagePipe) | |
218 return nil | |
219 } | |
220 | |
221 func (sp *serviceProviderImpl) AddService(factory ServiceFactory) { | |
222 sp.factories[factory.Name()] = factory | |
223 } | |
OLD | NEW |