blob: f3be55c9a691443630e02d33e3d7bbfe3ffe38db [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package application
import (
"log"
"mojo/public/go/bindings"
"mojo/public/go/system"
sp "mojo/public/interfaces/application/service_provider"
)
type connectionInfo struct {
requestorURL string
connectionURL string
}
// RequestorURL returns the URL of application that established the connection.
func (c *connectionInfo) RequestorURL() string {
return c.requestorURL
}
// ConnectionURL returns the URL that was used by the source application to
// establish a connection to the destination application.
func (c *connectionInfo) ConnectionURL() string {
return c.connectionURL
}
// ServiceRequest is an interface request for a specified mojo service.
type ServiceRequest interface {
// Name returns the name of requested mojo service.
Name() string
// PassMessagePipe passes ownership of the underlying message pipe
// handle to the newly created handle object, invalidating the
// underlying handle object in the process.
PassMessagePipe() system.MessagePipeHandle
}
// ServiceFactory provides implementation of a mojo service.
type ServiceFactory interface {
// Name returns the name of provided mojo service.
Name() string
// Create binds an implementation of mojo service to the provided
// message pipe and runs it.
Create(pipe system.MessagePipeHandle)
}
// Connection represents a connection to another application. An instance of
// this struct is passed to Delegate's AcceptConnection() function each time a
// connection is made to this application.
type Connection struct {
connectionInfo
// Request for local services. Is valid until ProvideServices is called.
servicesRequest *sp.ServiceProvider_Request
// Indicates that ProvideServices function was already called.
servicesProvided bool
localServices *bindings.Stub
outgoingConnection *OutgoingConnection
isClosed bool
}
func newConnection(requestorURL string, services *sp.ServiceProvider_Request, exposedServices *sp.ServiceProvider_Pointer, resolvedURL string) *Connection {
info := connectionInfo{
requestorURL,
resolvedURL,
}
var remoteServices *sp.ServiceProvider_Proxy
if exposedServices != nil {
remoteServices = sp.NewServiceProviderProxy(*exposedServices, bindings.GetAsyncWaiter())
}
return &Connection{
connectionInfo: info,
servicesRequest: services,
outgoingConnection: &OutgoingConnection{
info,
remoteServices,
},
}
}
// ProvideServices starts a service provider on a separate goroutine that
// provides given services to the remote application. Returns a pointer to
// outgoing connection that can be used to connect to services provided by
// remote application.
// Panics if called more than once.
func (c *Connection) ProvideServices(services ...ServiceFactory) *OutgoingConnection {
if c.servicesProvided {
panic("ProvideServices can be called only once")
}
c.servicesProvided = true
if c.servicesRequest == nil {
return c.outgoingConnection
}
if len(services) == 0 {
c.servicesRequest.PassMessagePipe().Close()
return c.outgoingConnection
}
provider := &serviceProviderImpl{
make(map[string]ServiceFactory),
}
for _, service := range services {
provider.AddService(service)
}
c.localServices = sp.NewServiceProviderStub(*c.servicesRequest, provider, bindings.GetAsyncWaiter())
go func() {
for {
if err := c.localServices.ServeRequest(); err != nil {
connectionError, ok := err.(*bindings.ConnectionError)
if !ok || !connectionError.Closed() {
log.Println(err)
}
break
}
}
}()
return c.outgoingConnection
}
// Close closes both incoming and outgoing parts of the connection.
func (c *Connection) Close() {
if c.servicesRequest != nil {
c.servicesRequest.Close()
}
if c.localServices != nil {
c.localServices.Close()
}
if c.outgoingConnection.remoteServices != nil {
c.outgoingConnection.remoteServices.Close_Proxy()
}
c.isClosed = true
}
// OutgoingConnection represents outgoing part of connection to another
// application. In order to close it close the |Connection| object that returned
// this |OutgoingConnection|.
type OutgoingConnection struct {
connectionInfo
remoteServices *sp.ServiceProvider_Proxy
}
// ConnectToService asks remote application to provide a service through the
// message pipe endpoint supplied by the caller.
func (c *OutgoingConnection) ConnectToService(request ServiceRequest) {
pipe := request.PassMessagePipe()
if c.remoteServices == nil {
pipe.Close()
return
}
c.remoteServices.ConnectToService(request.Name(), pipe)
}
// serviceProviderImpl is an implementation of mojo ServiceProvider interface.
type serviceProviderImpl struct {
factories map[string]ServiceFactory
}
// Mojo ServiceProvider implementation.
func (sp *serviceProviderImpl) ConnectToService(name string, messagePipe system.MessagePipeHandle) error {
factory, ok := sp.factories[name]
if !ok {
messagePipe.Close()
return nil
}
factory.Create(messagePipe)
return nil
}
func (sp *serviceProviderImpl) AddService(factory ServiceFactory) {
sp.factories[factory.Name()] = factory
}