services/wsprd: WSPR component of go/vcl/7026

MultiPart: 2/2
Change-Id: Ie4b2d99ce50cd9733191710ac9df61daff88b0d8
diff --git a/services/wsprd/app/app.go b/services/wsprd/app/app.go
index 323303d..6caddf9 100644
--- a/services/wsprd/app/app.go
+++ b/services/wsprd/app/app.go
@@ -99,8 +99,6 @@
 	// the default implementation.
 	writerCreator func(id int32) lib.ClientWriter
 
-	veyronProxyEP string
-
 	// Store for all the Blessings that javascript has a handle to.
 	blessingsStore *principal.JSBlessingsHandles
 
@@ -111,16 +109,20 @@
 }
 
 // NewController creates a new Controller.  writerCreator will be used to create a new flow for rpcs to
-// javascript server. veyronProxyEP is an endpoint for the veyron proxy to serve through.  It can't be empty.
+// javascript server.
 func NewController(ctx *context.T, writerCreator func(id int32) lib.ClientWriter, listenSpec *ipc.ListenSpec, namespaceRoots []string, p security.Principal) (*Controller, error) {
 	ctx, cancel := context.WithCancel(ctx)
 
-	ctx, _ = vtrace.SetNewTrace(ctx)
-
 	if namespaceRoots != nil {
-		v23.GetNamespace(ctx).SetRoots(namespaceRoots...)
+		var err error
+		ctx, _, err = v23.SetNewNamespace(ctx, namespaceRoots...)
+		if err != nil {
+			return nil, err
+		}
 	}
 
+	ctx, _ = vtrace.SetNewTrace(ctx)
+
 	ctx, err := v23.SetPrincipal(ctx, p)
 	if err != nil {
 		return nil, err
diff --git a/services/wsprd/browspr/browspr.go b/services/wsprd/browspr/browspr.go
index 08144eb..bec1889 100644
--- a/services/wsprd/browspr/browspr.go
+++ b/services/wsprd/browspr/browspr.go
@@ -64,26 +64,44 @@
 	return browspr
 }
 
-func (browspr *Browspr) Shutdown() {
+func (b *Browspr) Shutdown() {
 	// TODO(ataly, bprosnitz): Get rid of this method if possible.
 }
 
+// CreateInstance creates a new pipe and stores it in activeInstances map.
+func (b *Browspr) createInstance(instanceId int32, origin string, namespaceRoots []string, proxy string) (*pipe, error) {
+	b.mu.Lock()
+	defer b.mu.Unlock()
+
+	// Make sure we don't already have an instance.
+	p, ok := b.activeInstances[instanceId]
+	if ok {
+		return nil, fmt.Errorf("InstanceId %v already has an open instance.", instanceId)
+	}
+
+	p = newPipe(b, instanceId, origin, namespaceRoots, proxy)
+	if p == nil {
+		return nil, fmt.Errorf("Could not create pipe for instanceId %v and origin %v", instanceId, origin)
+	}
+	b.activeInstances[instanceId] = p
+	return p, nil
+}
+
 // HandleMessage handles most messages from javascript and forwards them to a
 // Controller.
 func (b *Browspr) HandleMessage(instanceId int32, origin, msg string) error {
 	b.mu.Lock()
-	instance, ok := b.activeInstances[instanceId]
-	if !ok {
-		instance = newPipe(b, instanceId, origin)
-		if instance == nil {
-			b.mu.Unlock()
-			return fmt.Errorf("Could not create pipe for origin %v: origin")
-		}
-		b.activeInstances[instanceId] = instance
-	}
+	p, ok := b.activeInstances[instanceId]
 	b.mu.Unlock()
+	if !ok {
+		return fmt.Errorf("No pipe found for instanceId %v. Must send CreateInstance message first.", instanceId)
+	}
 
-	return instance.handleMessage(msg)
+	if origin != p.origin {
+		return fmt.Errorf("Invalid message origin. InstanceId %v has origin %v, but message is from %v.", instanceId, p.origin, origin)
+	}
+
+	return p.handleMessage(msg)
 }
 
 // HandleCleanupRpc cleans up the specified instance state. (For instance,
@@ -96,11 +114,11 @@
 
 	b.mu.Lock()
 
-	if instance, ok := b.activeInstances[msg.InstanceId]; ok {
+	if pipe, ok := b.activeInstances[msg.InstanceId]; ok {
 		// We must unlock the mutex before calling cleanunp, otherwise
 		// browspr deadlocks.
 		b.mu.Unlock()
-		instance.cleanup()
+		pipe.cleanup()
 
 		b.mu.Lock()
 		delete(b.activeInstances, msg.InstanceId)
@@ -160,3 +178,19 @@
 	res := b.accountManager.OriginHasAccount(msg.Origin)
 	return vdl.ValueFromReflect(reflect.ValueOf(res))
 }
+
+// HandleCreateInstanceRpc sets the namespace root and proxy on the pipe, if
+// any are provided.
+func (b *Browspr) HandleCreateInstanceRpc(val *vdl.Value) (*vdl.Value, error) {
+	var msg CreateInstanceMessage
+	if err := vdl.Convert(&msg, val); err != nil {
+		return nil, fmt.Errorf("HandleCreateInstanceMessage did not receive CreateInstanceMessage, received: %v, %v", val, err)
+	}
+
+	_, err := b.createInstance(msg.InstanceId, msg.Origin, msg.NamespaceRoots, msg.Proxy)
+	if err != nil {
+		return nil, err
+	}
+
+	return nil, nil
+}
diff --git a/services/wsprd/browspr/browspr.vdl b/services/wsprd/browspr/browspr.vdl
index 263b601..66eae99 100644
--- a/services/wsprd/browspr/browspr.vdl
+++ b/services/wsprd/browspr/browspr.vdl
@@ -40,3 +40,10 @@
 }
 
 type GetAccountsMessage struct {}
+
+type CreateInstanceMessage struct {
+  InstanceId     int32
+  Origin         string
+  NamespaceRoots []string
+  Proxy          string
+}
diff --git a/services/wsprd/browspr/browspr.vdl.go b/services/wsprd/browspr/browspr.vdl.go
index c0eeeee..efc80f7 100644
--- a/services/wsprd/browspr/browspr.vdl.go
+++ b/services/wsprd/browspr/browspr.vdl.go
@@ -84,6 +84,18 @@
 }) {
 }
 
+type CreateInstanceMessage struct {
+	InstanceId     int32
+	Origin         string
+	NamespaceRoots []string
+	Proxy          string
+}
+
+func (CreateInstanceMessage) __VDLReflect(struct {
+	Name string "v.io/x/ref/services/wsprd/browspr.CreateInstanceMessage"
+}) {
+}
+
 func init() {
 	vdl.Register((*StartMessage)(nil))
 	vdl.Register((*blessingRoot)(nil))
@@ -92,4 +104,5 @@
 	vdl.Register((*CleanupMessage)(nil))
 	vdl.Register((*OriginHasAccountMessage)(nil))
 	vdl.Register((*GetAccountsMessage)(nil))
+	vdl.Register((*CreateInstanceMessage)(nil))
 }
diff --git a/services/wsprd/browspr/browspr_test.go b/services/wsprd/browspr/browspr_test.go
index 55f278b..90b130c 100644
--- a/services/wsprd/browspr/browspr_test.go
+++ b/services/wsprd/browspr/browspr_test.go
@@ -199,6 +199,14 @@
 		t.Fatalf("Failed to marshall app message to json: %v", err)
 	}
 
+	createInstanceMessage := CreateInstanceMessage{
+		InstanceId:     msgInstanceId,
+		Origin:         msgOrigin,
+		NamespaceRoots: nil,
+		Proxy:          "",
+	}
+	_, err = browspr.HandleCreateInstanceRpc(vdl.ValueOf(createInstanceMessage))
+
 	err = browspr.HandleMessage(msgInstanceId, msgOrigin, string(msg))
 	if err != nil {
 		t.Fatalf("Error while handling message: %v", err)
diff --git a/services/wsprd/browspr/main/main_nacl.go b/services/wsprd/browspr/main/main_nacl.go
index b75b5f4..a492cd5 100644
--- a/services/wsprd/browspr/main/main_nacl.go
+++ b/services/wsprd/browspr/main/main_nacl.go
@@ -262,6 +262,7 @@
 	inst.channel.RegisterRequestHandler("auth:associate-account", inst.browspr.HandleAuthAssociateAccountRpc)
 	inst.channel.RegisterRequestHandler("auth:get-accounts", inst.browspr.HandleAuthGetAccountsRpc)
 	inst.channel.RegisterRequestHandler("auth:origin-has-account", inst.browspr.HandleAuthOriginHasAccountRpc)
+	inst.channel.RegisterRequestHandler("create-instance", inst.browspr.HandleCreateInstanceRpc)
 	inst.channel.RegisterRequestHandler("cleanup", inst.browspr.HandleCleanupRpc)
 
 	return nil, nil
diff --git a/services/wsprd/browspr/pipe.go b/services/wsprd/browspr/pipe.go
index 6061380..7fa9bdc 100644
--- a/services/wsprd/browspr/pipe.go
+++ b/services/wsprd/browspr/pipe.go
@@ -17,7 +17,7 @@
 	instanceId int32
 }
 
-func newPipe(b *Browspr, instanceId int32, origin string) *pipe {
+func newPipe(b *Browspr, instanceId int32, origin string, namespaceRoots []string, proxy string) *pipe {
 	pipe := &pipe{
 		browspr:    b,
 		origin:     origin,
@@ -48,7 +48,19 @@
 		}
 	}
 
-	pipe.controller, err = app.NewController(b.ctx, pipe.createWriter, b.listenSpec, b.namespaceRoots, p)
+	// Shallow copy browspr's default listenSpec.  If we have passed in a
+	// proxy, set listenSpec.proxy.
+	listenSpec := *b.listenSpec
+	if proxy != "" {
+		listenSpec.Proxy = proxy
+	}
+
+	// If we have been passed in namespace roots, pass them to NewController, otherwise pass browspr's default.
+	if namespaceRoots == nil {
+		namespaceRoots = b.namespaceRoots
+	}
+
+	pipe.controller, err = app.NewController(b.ctx, pipe.createWriter, &listenSpec, namespaceRoots, p)
 	if err != nil {
 		vlog.Errorf("Could not create controller: %v", err)
 		return nil