Merge ""cmd/principal": No third-party code on Macaroon receiving page"
diff --git a/profiles/internal/rpc/server.go b/profiles/internal/rpc/server.go
index da73807..d289516 100644
--- a/profiles/internal/rpc/server.go
+++ b/profiles/internal/rpc/server.go
@@ -51,6 +51,7 @@
 	errFailedToListenForProxy    = reg(".errFailedToListenForProxy", "failed to listen on {3}{:4}")
 	errInternalTypeConversion    = reg(".errInternalTypeConversion", "failed to convert {3} to v.io/x/ref/profiles/internal/naming.Endpoint")
 	errFailedToParseIP           = reg(".errFailedToParseIP", "failed to parse {3} as an IP host")
+	errUnexpectedSuffix          = reg(".errUnexpectedSuffix", "suffix {3} was not expected because either server has the option IsLeaf set to true or it served an object and not a dispatcher")
 )
 
 // state for each requested listen address
@@ -216,6 +217,8 @@
 			s.blessings = opt.Blessings
 		case options.ServesMountTable:
 			s.servesMountTable = bool(opt)
+		case options.IsLeaf:
+			s.isLeaf = bool(opt)
 		case ReservedNameDispatcher:
 			s.dispReserved = opt.Dispatcher
 		case PreferredServerResolveProtocols:
@@ -1177,6 +1180,9 @@
 	disp := fs.disp
 	if naming.IsReserved(suffix) {
 		disp = fs.server.dispReserved
+	} else if fs.server.isLeaf && suffix != "" {
+		innerErr := verror.New(errUnexpectedSuffix, fs.ctx, suffix)
+		return nil, nil, verror.New(verror.ErrUnknownSuffix, fs.ctx, suffix, innerErr)
 	}
 	if disp != nil {
 		obj, auth, err := disp.Lookup(suffix)
diff --git a/profiles/internal/rpc/server_test.go b/profiles/internal/rpc/server_test.go
index a5d1b7d..59d3658 100644
--- a/profiles/internal/rpc/server_test.go
+++ b/profiles/internal/rpc/server_test.go
@@ -16,6 +16,7 @@
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/naming"
+	"v.io/v23/options"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/v23/verror"
@@ -637,7 +638,43 @@
 			t.Fatalf("timed out waiting for changes to take effect")
 		}
 	}
+}
 
+func TestIsLeafServerOption(t *testing.T) {
+	ctx, shutdown := initForTest()
+	defer shutdown()
+	sm := imanager.InternalNew(naming.FixedRoutingID(0x555555555))
+	defer sm.Shutdown()
+	ns := tnaming.NewSimpleNamespace()
+	pclient, pserver := newClientServerPrincipals()
+	server, err := testInternalNewServer(ctx, sm, ns, pserver, options.IsLeaf(true))
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer server.Stop()
+
+	disp := &testServerDisp{&testServer{}}
+
+	if _, err := server.Listen(listenSpec); err != nil {
+		t.Fatalf("Listen failed: %v", err)
+	}
+
+	if err := server.ServeDispatcher("leafserver", disp); err != nil {
+		t.Fatalf("ServeDispatcher failed: %v", err)
+	}
+	client, err := InternalNewClient(sm, ns)
+	if err != nil {
+		t.Fatalf("InternalNewClient failed: %v", err)
+	}
+	ctx, _ = v23.WithPrincipal(ctx, pclient)
+	ctx, _ = context.WithDeadline(ctx, time.Now().Add(10*time.Second))
+	var result string
+	// we have set IsLeaf to true, sending any suffix to leafserver should result
+	// in an suffix was not expected error.
+	callErr := client.Call(ctx, "leafserver/unwantedSuffix", "Echo", []interface{}{"Mirror on the wall"}, []interface{}{&result})
+	if callErr == nil {
+		t.Fatalf("Call should have failed with suffix was not expected error")
+	}
 }
 
 func setLeafEndpoints(eps []naming.Endpoint) {
diff --git a/services/wspr/internal/app/app.go b/services/wspr/internal/app/app.go
index 85ce453..05da65a 100644
--- a/services/wspr/internal/app/app.go
+++ b/services/wspr/internal/app/app.go
@@ -235,6 +235,24 @@
 	return callOpts, nil
 }
 
+// serverOpts turns a slice of type []RpcServerOptions object into an array of rpc.ServerOpt.
+func (c *Controller) serverOpts(opts []RpcServerOption) ([]rpc.ServerOpt, error) {
+	var serverOpts []rpc.ServerOpt
+
+	for _, opt := range opts {
+		switch v := opt.(type) {
+		case RpcServerOptionIsLeaf:
+			serverOpts = append(serverOpts, options.IsLeaf(v.Value))
+		case RpcServerOptionServesMountTable:
+			serverOpts = append(serverOpts, options.ServesMountTable(v.Value))
+		default:
+			return nil, fmt.Errorf("Unknown RpcServerOption type %T", v)
+		}
+	}
+
+	return serverOpts, nil
+}
+
 func (c *Controller) startCall(ctx *context.T, w lib.ClientWriter, msg *RpcRequest, inArgs []interface{}) (rpc.ClientCall, error) {
 	methodName := lib.UppercaseFirstCharacter(msg.Method)
 	callOpts, err := c.callOpts(msg.CallOptions)
@@ -574,13 +592,13 @@
 	vlog.Errorf("close called on non-existent call: %v", id)
 }
 
-func (c *Controller) maybeCreateServer(serverId uint32) (*server.Server, error) {
+func (c *Controller) maybeCreateServer(serverId uint32, opts ...rpc.ServerOpt) (*server.Server, error) {
 	c.Lock()
 	defer c.Unlock()
 	if server, ok := c.servers[serverId]; ok {
 		return server, nil
 	}
-	server, err := server.NewServer(serverId, c.listenSpec, c)
+	server, err := server.NewServer(serverId, c.listenSpec, c, opts...)
 	if err != nil {
 		return nil, err
 	}
@@ -620,8 +638,13 @@
 
 // Serve instructs WSPR to start listening for calls on behalf
 // of a javascript server.
-func (c *Controller) Serve(_ *context.T, _ rpc.ServerCall, name string, serverId uint32) error {
-	server, err := c.maybeCreateServer(serverId)
+func (c *Controller) Serve(_ *context.T, _ rpc.ServerCall, name string, serverId uint32, rpcServerOpts []RpcServerOption) error {
+
+	opts, err := c.serverOpts(rpcServerOpts)
+	if err != nil {
+		return verror.Convert(verror.ErrInternal, nil, err)
+	}
+	server, err := c.maybeCreateServer(serverId, opts...)
 	if err != nil {
 		return verror.Convert(verror.ErrInternal, nil, err)
 	}
diff --git a/services/wspr/internal/app/app.vdl b/services/wspr/internal/app/app.vdl
index 0c06832..c62b9e4 100644
--- a/services/wspr/internal/app/app.vdl
+++ b/services/wspr/internal/app/app.vdl
@@ -27,8 +27,8 @@
 	CallOptions  []RpcCallOption
 }
 
-// TODO(nlacasse): It would be nice if RpcCallOption were a struct with
-// optional types, like:
+// TODO(nlacasse): It would be nice if RpcCallOption and RpcServerOption
+// were a struct with optional types, like:
 //
 //   type RpcCallOptions struct {
 //     AllowedServersPolicy ?[]security.BlessingPattern
@@ -38,6 +38,7 @@
 // Unfortunately, optional types are currently only supported for structs, not
 // slices or primitive types.  Once optional types are better supported, switch
 // this to a struct with optional types.
+// Waiting for v.io/i/213
 
 type RpcCallOption union {
 	AllowedServersPolicy []security.BlessingPattern
@@ -45,6 +46,11 @@
 	Granter GranterHandle
 }
 
+type RpcServerOption union {
+  IsLeaf bool
+  ServesMountTable bool
+}
+
 type RpcResponse struct {
 	OutArgs       []any
 	TraceResponse vtrace.Response
diff --git a/services/wspr/internal/app/app.vdl.go b/services/wspr/internal/app/app.vdl.go
index bb46e83..b3dce5d 100644
--- a/services/wspr/internal/app/app.vdl.go
+++ b/services/wspr/internal/app/app.vdl.go
@@ -84,6 +84,43 @@
 func (x RpcCallOptionGranter) Name() string                        { return "Granter" }
 func (x RpcCallOptionGranter) __VDLReflect(__RpcCallOptionReflect) {}
 
+type (
+	// RpcServerOption represents any single field of the RpcServerOption union type.
+	RpcServerOption interface {
+		// Index returns the field index.
+		Index() int
+		// Interface returns the field value as an interface.
+		Interface() interface{}
+		// Name returns the field name.
+		Name() string
+		// __VDLReflect describes the RpcServerOption union type.
+		__VDLReflect(__RpcServerOptionReflect)
+	}
+	// RpcServerOptionIsLeaf represents field IsLeaf of the RpcServerOption union type.
+	RpcServerOptionIsLeaf struct{ Value bool }
+	// RpcServerOptionServesMountTable represents field ServesMountTable of the RpcServerOption union type.
+	RpcServerOptionServesMountTable struct{ Value bool }
+	// __RpcServerOptionReflect describes the RpcServerOption union type.
+	__RpcServerOptionReflect struct {
+		Name  string "v.io/x/ref/services/wspr/internal/app.RpcServerOption"
+		Type  RpcServerOption
+		Union struct {
+			IsLeaf           RpcServerOptionIsLeaf
+			ServesMountTable RpcServerOptionServesMountTable
+		}
+	}
+)
+
+func (x RpcServerOptionIsLeaf) Index() int                            { return 0 }
+func (x RpcServerOptionIsLeaf) Interface() interface{}                { return x.Value }
+func (x RpcServerOptionIsLeaf) Name() string                          { return "IsLeaf" }
+func (x RpcServerOptionIsLeaf) __VDLReflect(__RpcServerOptionReflect) {}
+
+func (x RpcServerOptionServesMountTable) Index() int                            { return 1 }
+func (x RpcServerOptionServesMountTable) Interface() interface{}                { return x.Value }
+func (x RpcServerOptionServesMountTable) Name() string                          { return "ServesMountTable" }
+func (x RpcServerOptionServesMountTable) __VDLReflect(__RpcServerOptionReflect) {}
+
 type RpcResponse struct {
 	OutArgs       []*vdl.Value
 	TraceResponse vtrace.Response
@@ -124,6 +161,7 @@
 func init() {
 	vdl.Register((*RpcRequest)(nil))
 	vdl.Register((*RpcCallOption)(nil))
+	vdl.Register((*RpcServerOption)(nil))
 	vdl.Register((*RpcResponse)(nil))
 	vdl.Register((*GranterHandle)(nil))
 	vdl.Register((*GranterRequest)(nil))
diff --git a/services/wspr/internal/app/app_test.go b/services/wspr/internal/app/app_test.go
index 9821146..05c02cf 100644
--- a/services/wspr/internal/app/app_test.go
+++ b/services/wspr/internal/app/app_test.go
@@ -337,10 +337,10 @@
 	req, err := makeRequest(RpcRequest{
 		Name:       "__controller",
 		Method:     "Serve",
-		NumInArgs:  2,
+		NumInArgs:  3,
 		NumOutArgs: 1,
 		Deadline:   vdltime.Deadline{},
-	}, "adder", 0)
+	}, "adder", 0, []RpcServerOption{})
 	controller.HandleVeyronRequest(ctx, 0, req, writer)
 
 	testWriter, _ := writer.(*testwriter.Writer)
diff --git a/services/wspr/internal/app/controller.vdl b/services/wspr/internal/app/controller.vdl
index 68376f0..95daf5b 100644
--- a/services/wspr/internal/app/controller.vdl
+++ b/services/wspr/internal/app/controller.vdl
@@ -14,7 +14,7 @@
 type Controller interface {
 	// Serve instructs WSPR to start listening for calls on behalf
 	// of a javascript server.
-	Serve(name string, serverId uint32) error
+	Serve(name string, serverId uint32, serverOpts []RpcServerOption) error
 	// Stop instructs WSPR to stop listening for calls for the
 	// given javascript server.
 	Stop(serverId uint32) error
diff --git a/services/wspr/internal/app/controller.vdl.go b/services/wspr/internal/app/controller.vdl.go
index f8b6c0d..4ffd6c6 100644
--- a/services/wspr/internal/app/controller.vdl.go
+++ b/services/wspr/internal/app/controller.vdl.go
@@ -24,7 +24,7 @@
 type ControllerClientMethods interface {
 	// Serve instructs WSPR to start listening for calls on behalf
 	// of a javascript server.
-	Serve(ctx *context.T, name string, serverId uint32, opts ...rpc.CallOpt) error
+	Serve(ctx *context.T, name string, serverId uint32, serverOpts []RpcServerOption, opts ...rpc.CallOpt) error
 	// Stop instructs WSPR to stop listening for calls for the
 	// given javascript server.
 	Stop(ctx *context.T, serverId uint32, opts ...rpc.CallOpt) error
@@ -68,8 +68,8 @@
 	name string
 }
 
-func (c implControllerClientStub) Serve(ctx *context.T, i0 string, i1 uint32, opts ...rpc.CallOpt) (err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "Serve", []interface{}{i0, i1}, nil, opts...)
+func (c implControllerClientStub) Serve(ctx *context.T, i0 string, i1 uint32, i2 []RpcServerOption, opts ...rpc.CallOpt) (err error) {
+	err = v23.GetClient(ctx).Call(ctx, c.name, "Serve", []interface{}{i0, i1, i2}, nil, opts...)
 	return
 }
 
@@ -138,7 +138,7 @@
 type ControllerServerMethods interface {
 	// Serve instructs WSPR to start listening for calls on behalf
 	// of a javascript server.
-	Serve(ctx *context.T, call rpc.ServerCall, name string, serverId uint32) error
+	Serve(ctx *context.T, call rpc.ServerCall, name string, serverId uint32, serverOpts []RpcServerOption) error
 	// Stop instructs WSPR to stop listening for calls for the
 	// given javascript server.
 	Stop(ctx *context.T, call rpc.ServerCall, serverId uint32) error
@@ -202,8 +202,8 @@
 	gs   *rpc.GlobState
 }
 
-func (s implControllerServerStub) Serve(ctx *context.T, call rpc.ServerCall, i0 string, i1 uint32) error {
-	return s.impl.Serve(ctx, call, i0, i1)
+func (s implControllerServerStub) Serve(ctx *context.T, call rpc.ServerCall, i0 string, i1 uint32, i2 []RpcServerOption) error {
+	return s.impl.Serve(ctx, call, i0, i1, i2)
 }
 
 func (s implControllerServerStub) Stop(ctx *context.T, call rpc.ServerCall, i0 uint32) error {
@@ -274,8 +274,9 @@
 			Name: "Serve",
 			Doc:  "// Serve instructs WSPR to start listening for calls on behalf\n// of a javascript server.",
 			InArgs: []rpc.ArgDesc{
-				{"name", ``},     // string
-				{"serverId", ``}, // uint32
+				{"name", ``},       // string
+				{"serverId", ``},   // uint32
+				{"serverOpts", ``}, // []RpcServerOption
 			},
 		},
 		{
diff --git a/services/wspr/internal/rpc/server/server.go b/services/wspr/internal/rpc/server/server.go
index ac7d2b5..9ce5ac9 100644
--- a/services/wspr/internal/rpc/server/server.go
+++ b/services/wspr/internal/rpc/server/server.go
@@ -102,7 +102,7 @@
 
 type serverContextKey struct{}
 
-func NewServer(id uint32, listenSpec *rpc.ListenSpec, helper ServerHelper) (*Server, error) {
+func NewServer(id uint32, listenSpec *rpc.ListenSpec, helper ServerHelper, opts ...rpc.ServerOpt) (*Server, error) {
 	server := &Server{
 		id:                            id,
 		helper:                        helper,
@@ -114,7 +114,7 @@
 	var err error
 	ctx := helper.Context()
 	ctx = context.WithValue(ctx, serverContextKey{}, server)
-	if server.server, err = v23.NewServer(ctx); err != nil {
+	if server.server, err = v23.NewServer(ctx, opts...); err != nil {
 		return nil, err
 	}
 	return server, nil