(runtime|services): Implement new AllGlobberX and ChildrenGlobberX
This change adds support for the new AllGlobberX and ChildrenGlobberX to
the rpc Server, and convers most of the code to use them.
I'm not super happy with the wspr change, but it works. Added TODO to
revisit.
Depends on v.io/c/13589
Partly addresses v.io/i/532 and v.io/i/533
MultiPart: 2/3
Change-Id: I541587e5d376a65f2b5ba4a3aab8f4b7063ad042
diff --git a/cmd/mounttable/impl_test.go b/cmd/mounttable/impl_test.go
index 1aaf18f..09875f3 100644
--- a/cmd/mounttable/impl_test.go
+++ b/cmd/mounttable/impl_test.go
@@ -15,6 +15,7 @@
"v.io/v23"
"v.io/v23/context"
+ "v.io/v23/glob"
"v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/security"
@@ -42,13 +43,12 @@
suffix string
}
-func (s *server) Glob__(ctx *context.T, _ rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
- ctx.VI(2).Infof("Glob() was called. suffix=%v pattern=%q", s.suffix, pattern)
- ch := make(chan naming.GlobReply, 2)
- ch <- naming.GlobReplyEntry{naming.MountEntry{"name1", []naming.MountedServer{{"server1", deadline(1)}}, false, false}}
- ch <- naming.GlobReplyEntry{naming.MountEntry{"name2", []naming.MountedServer{{"server2", deadline(2)}, {"server3", deadline(3)}}, false, false}}
- close(ch)
- return ch, nil
+func (s *server) Glob__(ctx *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
+ ctx.VI(2).Infof("Glob() was called. suffix=%v pattern=%q", s.suffix, g.String())
+ sender := call.SendStream()
+ sender.Send(naming.GlobReplyEntry{naming.MountEntry{"name1", []naming.MountedServer{{"server1", deadline(1)}}, false, false}})
+ sender.Send(naming.GlobReplyEntry{naming.MountEntry{"name2", []naming.MountedServer{{"server2", deadline(2)}, {"server3", deadline(3)}}, false, false}})
+ return nil
}
func (s *server) Mount(ctx *context.T, _ rpc.ServerCall, server string, ttl uint32, flags naming.MountFlag) error {
diff --git a/runtime/internal/naming/namespace/all_test.go b/runtime/internal/naming/namespace/all_test.go
index 89e3552..0844c4f 100644
--- a/runtime/internal/naming/namespace/all_test.go
+++ b/runtime/internal/naming/namespace/all_test.go
@@ -14,6 +14,7 @@
"v.io/v23"
"v.io/v23/context"
+ "v.io/v23/glob"
"v.io/v23/namespace"
"v.io/v23/naming"
"v.io/v23/options"
@@ -132,18 +133,20 @@
// testServer has the following namespace:
// "" -> {level1} -> {level2}
-func (t *testServer) GlobChildren__(*context.T, rpc.ServerCall) (<-chan string, error) {
- ch := make(chan string, 1)
+func (t *testServer) GlobChildren__(_ *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
switch t.suffix {
case "":
- ch <- "level1"
+ if n := "level1"; m.Match(n) {
+ call.SendStream().Send(naming.GlobChildrenReplyName{n})
+ }
case "level1":
- ch <- "level2"
+ if n := "level2"; m.Match(n) {
+ call.SendStream().Send(naming.GlobChildrenReplyName{n})
+ }
default:
- return nil, nil
+ return nil
}
- close(ch)
- return ch, nil
+ return nil
}
type dispatcher struct{}
@@ -496,11 +499,11 @@
mu sync.Mutex
}
-func (g *GlobbableServer) Glob__(*context.T, rpc.ServerCall, string) (<-chan naming.GlobReply, error) {
+func (g *GlobbableServer) Glob__(*context.T, rpc.GlobServerCall, *glob.Glob) error {
g.mu.Lock()
defer g.mu.Unlock()
g.callCount++
- return nil, nil
+ return nil
}
func (g *GlobbableServer) GetAndResetCount() int {
diff --git a/runtime/internal/rpc/benchmark/glob/README.txt b/runtime/internal/rpc/benchmark/glob/README.txt
deleted file mode 100644
index 0103cd2..0000000
--- a/runtime/internal/rpc/benchmark/glob/README.txt
+++ /dev/null
@@ -1,120 +0,0 @@
-Glob Benchmarks
-
-The benchmarks in this directory attempt to provide some guidance for the amount
-of buffering to use with the channels returned by Glob__ and GlobChildren__.
-
-The first set of benchmarks (BenchmarkChanN) shows the relationship between the
-buffer size and the latency of a very simple channel with one writer and one
-reader doing nothing else.
-
-The second set of benchmarks (BenchmarkGlobN) does the same thing but with a
-Glob__ server and a Glob client. The third set (BenchmarkGlobChildrenN) uses
-GlobChildren__.
-
-As of 2014-12-03, the conclusion is that the queue size has very little impact
-on performance.
-
-The BenchmarkChanN set shows that increasing the queue size improves latency for
-the very simple case, but not for Glob__ or GlobChildren__.
-
-An interesting observation is that all the benchmarks get slower as the number
-of cpus increases.
-
-Below are the test results for 1, 2, and 4 cpus on a HP Z420 workstation with
-2 × 6 cpu cores (Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz).
-
-$ ./glob.test -test.bench=. -test.benchtime=5s -test.cpu=1
-BenchmarkChan0 20000000 464 ns/op
-BenchmarkChan1 20000000 585 ns/op
-BenchmarkChan2 20000000 484 ns/op
-BenchmarkChan4 20000000 425 ns/op
-BenchmarkChan8 50000000 396 ns/op
-BenchmarkChan16 50000000 381 ns/op
-BenchmarkChan32 50000000 371 ns/op
-BenchmarkChan64 50000000 365 ns/op
-BenchmarkChan128 50000000 363 ns/op
-BenchmarkChan256 50000000 362 ns/op
-BenchmarkGlob0 500000 35029 ns/op
-BenchmarkGlob1 500000 63536 ns/op
-BenchmarkGlob2 500000 34753 ns/op
-BenchmarkGlob4 500000 26379 ns/op
-BenchmarkGlob8 500000 19293 ns/op
-BenchmarkGlob16 1000000 18149 ns/op
-BenchmarkGlob32 500000 52364 ns/op
-BenchmarkGlob64 500000 83879 ns/op
-BenchmarkGlob128 100000 88448 ns/op
-BenchmarkGlob256 100000 57922 ns/op
-BenchmarkGlobChildren0 100000 118448 ns/op
-BenchmarkGlobChildren1 100000 123274 ns/op
-BenchmarkGlobChildren2 100000 116110 ns/op
-BenchmarkGlobChildren4 100000 134175 ns/op
-BenchmarkGlobChildren8 100000 118776 ns/op
-BenchmarkGlobChildren16 100000 123191 ns/op
-BenchmarkGlobChildren32 100000 132195 ns/op
-BenchmarkGlobChildren64 100000 126004 ns/op
-BenchmarkGlobChildren128 100000 135072 ns/op
-BenchmarkGlobChildren256 100000 127399 ns/op
-
-$ ./glob.test -test.bench=. -test.benchtime=5s -test.cpu=2
-BenchmarkChan0-2 5000000 1595 ns/op
-BenchmarkChan1-2 5000000 1649 ns/op
-BenchmarkChan2-2 10000000 1245 ns/op
-BenchmarkChan4-2 10000000 1299 ns/op
-BenchmarkChan8-2 10000000 982 ns/op
-BenchmarkChan16-2 10000000 929 ns/op
-BenchmarkChan32-2 10000000 916 ns/op
-BenchmarkChan64-2 10000000 903 ns/op
-BenchmarkChan128-2 10000000 907 ns/op
-BenchmarkChan256-2 10000000 914 ns/op
-BenchmarkGlob0-2 500000 61455 ns/op
-BenchmarkGlob1-2 500000 46890 ns/op
-BenchmarkGlob2-2 200000 56462 ns/op
-BenchmarkGlob4-2 500000 22783 ns/op
-BenchmarkGlob8-2 200000 64783 ns/op
-BenchmarkGlob16-2 1000000 68119 ns/op
-BenchmarkGlob32-2 200000 78611 ns/op
-BenchmarkGlob64-2 500000 82180 ns/op
-BenchmarkGlob128-2 1000000 81548 ns/op
-BenchmarkGlob256-2 100000 88278 ns/op
-BenchmarkGlobChildren0-2 100000 83188 ns/op
-BenchmarkGlobChildren1-2 100000 81751 ns/op
-BenchmarkGlobChildren2-2 100000 81896 ns/op
-BenchmarkGlobChildren4-2 100000 81857 ns/op
-BenchmarkGlobChildren8-2 100000 81531 ns/op
-BenchmarkGlobChildren16-2 100000 89915 ns/op
-BenchmarkGlobChildren32-2 100000 81112 ns/op
-BenchmarkGlobChildren64-2 100000 80997 ns/op
-BenchmarkGlobChildren128-2 100000 81350 ns/op
-BenchmarkGlobChildren256-2 100000 81344 ns/op
-
-$ ./glob.test -test.bench=. -test.benchtime=5s -test.cpu=4
-BenchmarkChan0-4 5000000 2012 ns/op
-BenchmarkChan1-4 5000000 3149 ns/op
-BenchmarkChan2-4 5000000 1839 ns/op
-BenchmarkChan4-4 10000000 957 ns/op
-BenchmarkChan8-4 20000000 660 ns/op
-BenchmarkChan16-4 20000000 523 ns/op
-BenchmarkChan32-4 20000000 507 ns/op
-BenchmarkChan64-4 20000000 509 ns/op
-BenchmarkChan128-4 20000000 507 ns/op
-BenchmarkChan256-4 20000000 511 ns/op
-BenchmarkGlob0-4 100000 103269 ns/op
-BenchmarkGlob1-4 100000 101222 ns/op
-BenchmarkGlob2-4 100000 102049 ns/op
-BenchmarkGlob4-4 100000 102763 ns/op
-BenchmarkGlob8-4 100000 101939 ns/op
-BenchmarkGlob16-4 100000 102989 ns/op
-BenchmarkGlob32-4 100000 103898 ns/op
-BenchmarkGlob64-4 100000 102838 ns/op
-BenchmarkGlob128-4 100000 101532 ns/op
-BenchmarkGlob256-4 100000 101059 ns/op
-BenchmarkGlobChildren0-4 100000 106617 ns/op
-BenchmarkGlobChildren1-4 100000 102576 ns/op
-BenchmarkGlobChildren2-4 100000 106313 ns/op
-BenchmarkGlobChildren4-4 100000 102774 ns/op
-BenchmarkGlobChildren8-4 100000 102886 ns/op
-BenchmarkGlobChildren16-4 100000 106771 ns/op
-BenchmarkGlobChildren32-4 100000 103309 ns/op
-BenchmarkGlobChildren64-4 100000 105112 ns/op
-BenchmarkGlobChildren128-4 100000 102295 ns/op
-BenchmarkGlobChildren256-4 100000 102951 ns/op
diff --git a/runtime/internal/rpc/benchmark/glob/doc.go b/runtime/internal/rpc/benchmark/glob/doc.go
deleted file mode 100644
index ce0aad5..0000000
--- a/runtime/internal/rpc/benchmark/glob/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2015 The Vanadium 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 glob
-
-// This file exists only to prevent build failures from having a test-only
-// package.
diff --git a/runtime/internal/rpc/benchmark/glob/glob_test.go b/runtime/internal/rpc/benchmark/glob/glob_test.go
deleted file mode 100644
index a45053c..0000000
--- a/runtime/internal/rpc/benchmark/glob/glob_test.go
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright 2015 The Vanadium 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 glob_test
-
-import (
- "fmt"
- "testing"
-
- "v.io/v23"
- "v.io/v23/context"
- "v.io/v23/naming"
- "v.io/v23/rpc"
- "v.io/v23/security"
-
- "v.io/x/ref/lib/xrpc"
- _ "v.io/x/ref/runtime/factories/generic"
- "v.io/x/ref/test"
-)
-
-func TestNothing(t *testing.T) {
-}
-
-func RunBenchmarkChan(b *testing.B, bufferSize int) {
- ch := make(chan string, bufferSize)
- go func() {
- for i := 0; i < b.N; i++ {
- ch <- fmt.Sprintf("%09d", i)
- }
- close(ch)
- }()
- for _ = range ch {
- continue
- }
-}
-
-func BenchmarkChan0(b *testing.B) {
- RunBenchmarkChan(b, 0)
-}
-
-func BenchmarkChan1(b *testing.B) {
- RunBenchmarkChan(b, 1)
-}
-
-func BenchmarkChan2(b *testing.B) {
- RunBenchmarkChan(b, 2)
-}
-
-func BenchmarkChan4(b *testing.B) {
- RunBenchmarkChan(b, 4)
-}
-
-func BenchmarkChan8(b *testing.B) {
- RunBenchmarkChan(b, 8)
-}
-
-func BenchmarkChan16(b *testing.B) {
- RunBenchmarkChan(b, 16)
-}
-
-func BenchmarkChan32(b *testing.B) {
- RunBenchmarkChan(b, 32)
-}
-
-func BenchmarkChan64(b *testing.B) {
- RunBenchmarkChan(b, 64)
-}
-
-func BenchmarkChan128(b *testing.B) {
- RunBenchmarkChan(b, 128)
-}
-
-func BenchmarkChan256(b *testing.B) {
- RunBenchmarkChan(b, 256)
-}
-
-type disp struct {
- obj interface{}
-}
-
-func (d *disp) Lookup(_ *context.T, suffix string) (interface{}, security.Authorizer, error) {
- return d.obj, nil, nil
-}
-
-type globObject struct {
- b *testing.B
- bufferSize int
-}
-
-func (o *globObject) Glob__(_ *context.T, _ rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
- if pattern != "*" {
- panic("this benchmark only works with pattern='*'")
- }
- ch := make(chan naming.GlobReply, o.bufferSize)
- go func() {
- for i := 0; i < o.b.N; i++ {
- name := fmt.Sprintf("%09d", i)
- ch <- naming.GlobReplyEntry{naming.MountEntry{Name: name}}
- }
- close(ch)
- }()
- return ch, nil
-}
-
-type globChildrenObject struct {
- b *testing.B
- bufferSize int
-}
-
-func (o *globChildrenObject) GlobChildren__(_ *context.T, call rpc.ServerCall) (<-chan string, error) {
- if call.Suffix() != "" {
- return nil, nil
- }
- ch := make(chan string, o.bufferSize)
- go func() {
- for i := 0; i < o.b.N; i++ {
- ch <- fmt.Sprintf("%09d", i)
- }
- close(ch)
- }()
- return ch, nil
-}
-
-func globClient(b *testing.B, ctx *context.T, name string) (int, error) {
- client := v23.GetClient(ctx)
- call, err := client.StartCall(ctx, name, rpc.GlobMethod, []interface{}{"*"})
- if err != nil {
- return 0, err
- }
- var me naming.MountEntry
- b.ResetTimer()
- count := 0
- for {
- if err := call.Recv(&me); err != nil {
- break
- }
- count++
- }
- b.StopTimer()
- if err := call.Finish(); err != nil {
- return 0, err
- }
- return count, nil
-}
-
-func RunBenchmarkGlob(b *testing.B, obj interface{}) {
- ctx, shutdown := test.V23Init()
- defer shutdown()
-
- server, err := xrpc.NewDispatchingServer(ctx, "", &disp{obj})
- if err != nil {
- b.Fatalf("failed to start server: %v", err)
- }
- addr := server.Status().Endpoints[0].Name()
-
- count, err := globClient(b, ctx, addr)
- if err != nil {
- b.Fatalf("globClient failed: %v", err)
- }
- if count != b.N {
- b.Fatalf("unexpected number of results: got %d, expected %d", count, b.N)
- }
-}
-
-func BenchmarkGlob0(b *testing.B) {
- RunBenchmarkGlob(b, &globObject{b, 0})
-}
-
-func BenchmarkGlob1(b *testing.B) {
- RunBenchmarkGlob(b, &globObject{b, 1})
-}
-
-func BenchmarkGlob2(b *testing.B) {
- RunBenchmarkGlob(b, &globObject{b, 2})
-}
-
-func BenchmarkGlob4(b *testing.B) {
- RunBenchmarkGlob(b, &globObject{b, 4})
-}
-
-func BenchmarkGlob8(b *testing.B) {
- RunBenchmarkGlob(b, &globObject{b, 8})
-}
-
-func BenchmarkGlob16(b *testing.B) {
- RunBenchmarkGlob(b, &globObject{b, 16})
-}
-
-func BenchmarkGlob32(b *testing.B) {
- RunBenchmarkGlob(b, &globObject{b, 32})
-}
-
-func BenchmarkGlob64(b *testing.B) {
- RunBenchmarkGlob(b, &globObject{b, 64})
-}
-
-func BenchmarkGlob128(b *testing.B) {
- RunBenchmarkGlob(b, &globObject{b, 128})
-}
-
-func BenchmarkGlob256(b *testing.B) {
- RunBenchmarkGlob(b, &globObject{b, 256})
-}
-
-func BenchmarkGlobChildren0(b *testing.B) {
- RunBenchmarkGlob(b, &globChildrenObject{b, 0})
-}
-
-func BenchmarkGlobChildren1(b *testing.B) {
- RunBenchmarkGlob(b, &globChildrenObject{b, 1})
-}
-
-func BenchmarkGlobChildren2(b *testing.B) {
- RunBenchmarkGlob(b, &globChildrenObject{b, 2})
-}
-
-func BenchmarkGlobChildren4(b *testing.B) {
- RunBenchmarkGlob(b, &globChildrenObject{b, 4})
-}
-
-func BenchmarkGlobChildren8(b *testing.B) {
- RunBenchmarkGlob(b, &globChildrenObject{b, 8})
-}
-
-func BenchmarkGlobChildren16(b *testing.B) {
- RunBenchmarkGlob(b, &globChildrenObject{b, 16})
-}
-
-func BenchmarkGlobChildren32(b *testing.B) {
- RunBenchmarkGlob(b, &globChildrenObject{b, 32})
-}
-
-func BenchmarkGlobChildren64(b *testing.B) {
- RunBenchmarkGlob(b, &globChildrenObject{b, 64})
-}
-
-func BenchmarkGlobChildren128(b *testing.B) {
- RunBenchmarkGlob(b, &globChildrenObject{b, 128})
-}
-
-func BenchmarkGlobChildren256(b *testing.B) {
- RunBenchmarkGlob(b, &globChildrenObject{b, 256})
-}
diff --git a/runtime/internal/rpc/reserved.go b/runtime/internal/rpc/reserved.go
index 3e8a0ca..2f63127 100644
--- a/runtime/internal/rpc/reserved.go
+++ b/runtime/internal/rpc/reserved.go
@@ -5,6 +5,7 @@
package rpc
import (
+ "fmt"
"strings"
"v.io/v23/context"
@@ -188,6 +189,12 @@
// levels.
const maxRecursiveGlobDepth = 10
+type gState struct {
+ name string
+ glob *glob.Glob
+ depth int
+}
+
func (i *globInternal) Glob(ctx *context.T, call rpc.StreamServerCall, pattern string) error {
ctx.VI(3).Infof("rpc Glob: Incoming request: %q.Glob(%q)", i.receiver, pattern)
g, err := glob.Parse(pattern)
@@ -205,11 +212,6 @@
}
call = callWithMethodTags(ctx, call, tags)
- type gState struct {
- name string
- glob *glob.Glob
- depth int
- }
queue := []gState{gState{glob: g}}
someMatchesOmitted := false
@@ -227,7 +229,7 @@
suffix := subcall.Suffix()
if state.depth > maxRecursiveGlobDepth {
ctx.Errorf("rpc Glob: exceeded recursion limit (%d): %q", maxRecursiveGlobDepth, suffix)
- call.Send(naming.GlobReplyError{
+ subcall.Send(naming.GlobReplyError{
naming.GlobError{Name: state.name, Error: reserved.NewErrGlobMaxRecursionReached(ctx)},
})
continue
@@ -235,21 +237,21 @@
obj, auth, err := disp.Lookup(ctx, suffix)
if err != nil {
ctx.VI(3).Infof("rpc Glob: Lookup failed for %q: %v", suffix, err)
- call.Send(naming.GlobReplyError{
+ subcall.Send(naming.GlobReplyError{
naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrNoExist, ctx, err)},
})
continue
}
if obj == nil {
ctx.VI(3).Infof("rpc Glob: object not found for %q", suffix)
- call.Send(naming.GlobReplyError{
+ subcall.Send(naming.GlobReplyError{
naming.GlobError{Name: state.name, Error: verror.New(verror.ErrNoExist, ctx, "nil object")},
})
continue
}
// Verify that that requester is authorized for the current object.
- if err := authorize(ctx, call.Security(), auth); err != nil {
+ if err := authorize(ctx, subcall.Security(), auth); err != nil {
someMatchesOmitted = true
ctx.VI(3).Infof("rpc Glob: client is not authorized for %q: %v", suffix, err)
continue
@@ -260,13 +262,13 @@
invoker, err := objectToInvoker(obj)
if err != nil {
ctx.VI(3).Infof("rpc Glob: object for %q cannot be converted to invoker: %v", suffix, err)
- call.Send(naming.GlobReplyError{
+ subcall.Send(naming.GlobReplyError{
naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, ctx, err)},
})
continue
}
gs := invoker.Globber()
- if gs == nil || (gs.AllGlobber == nil && gs.ChildrenGlobber == nil) {
+ if gs == nil || (gs.AllGlobber == nil && gs.ChildrenGlobber == nil && gs.AllGlobberX == nil && gs.ChildrenGlobberX == nil) {
if state.glob.Len() == 0 {
subcall.Send(naming.GlobReplyEntry{naming.MountEntry{Name: state.name, IsLeaf: true}})
} else {
@@ -299,36 +301,106 @@
}
continue
}
- ctx.VI(3).Infof("rpc Glob: %q implements ChildrenGlobber", suffix)
- children, err := gs.ChildrenGlobber.GlobChildren__(ctx, subcall)
- // The requested object doesn't exist.
- if err != nil {
- subcall.Send(naming.GlobReplyError{naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, ctx, err)}})
- continue
- }
- // The glob pattern matches the current object.
- if state.glob.Len() == 0 {
- subcall.Send(naming.GlobReplyEntry{naming.MountEntry{Name: state.name}})
- }
- // The current object has no children.
- if children == nil {
- continue
- }
- depth := state.depth
- // This is a recursive pattern. Make sure we don't recurse forever.
- if state.glob.Len() == 0 {
- depth++
- }
- matcher, left := state.glob.Head(), state.glob.Tail()
- for child := range children {
- if len(child) == 0 || strings.Contains(child, "/") {
- ctx.Errorf("rpc Glob: %q.GlobChildren__() sent an invalid child name: %q", suffix, child)
+ if gs.ChildrenGlobber != nil {
+ ctx.VI(3).Infof("rpc Glob: %q implements ChildrenGlobber", suffix)
+ children, err := gs.ChildrenGlobber.GlobChildren__(ctx, subcall)
+ // The requested object doesn't exist.
+ if err != nil {
+ subcall.Send(naming.GlobReplyError{naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, ctx, err)}})
continue
}
- if matcher.Match(child) {
- next := naming.Join(state.name, child)
- queue = append(queue, gState{next, left, depth})
+ // The glob pattern matches the current object.
+ if state.glob.Len() == 0 {
+ subcall.Send(naming.GlobReplyEntry{naming.MountEntry{Name: state.name}})
}
+ // The current object has no children.
+ if children == nil {
+ continue
+ }
+ depth := state.depth
+ // This is a recursive pattern. Make sure we don't recurse forever.
+ if state.glob.Len() == 0 {
+ depth++
+ }
+ matcher, left := state.glob.Head(), state.glob.Tail()
+ for child := range children {
+ if len(child) == 0 || strings.Contains(child, "/") {
+ ctx.Errorf("rpc Glob: %q.GlobChildren__() sent an invalid child name: %q", suffix, child)
+ continue
+ }
+ if matcher.Match(child) {
+ next := naming.Join(state.name, child)
+ queue = append(queue, gState{next, left, depth})
+ }
+ }
+ continue
+ }
+ if gs.AllGlobberX != nil {
+ ctx.VI(3).Infof("rpc Glob: %q implements AllGlobberX", suffix)
+ send := func(reply naming.GlobReply) error {
+ select {
+ case <-ctx.Done():
+ return verror.New(verror.ErrAborted, ctx)
+ default:
+ }
+ switch v := reply.(type) {
+ case naming.GlobReplyEntry:
+ v.Value.Name = naming.Join(state.name, v.Value.Name)
+ return subcall.Send(v)
+ case naming.GlobReplyError:
+ v.Value.Name = naming.Join(state.name, v.Value.Name)
+ return subcall.Send(v)
+ }
+ return nil
+ }
+ if err := gs.AllGlobberX.Glob__(ctx, &globServerCall{subcall, send}, state.glob); err != nil {
+ ctx.VI(3).Infof("rpc Glob: %q.Glob(%q) failed: %v", suffix, state.glob, err)
+ subcall.Send(naming.GlobReplyError{naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, ctx, err)}})
+ }
+ continue
+ }
+ if gs.ChildrenGlobberX != nil {
+ ctx.VI(3).Infof("rpc Glob: %q implements ChildrenGlobberX", suffix)
+ depth := state.depth
+ if state.glob.Len() == 0 {
+ // The glob pattern matches the current object.
+ subcall.Send(naming.GlobReplyEntry{naming.MountEntry{Name: state.name}})
+ if state.glob.Recursive() {
+ // This is a recursive pattern. Make sure we don't recurse forever.
+ depth++
+ } else {
+ // The pattern can't possibly match any children of this node.
+ continue
+ }
+ }
+ matcher, tail := state.glob.Head(), state.glob.Tail()
+ send := func(reply naming.GlobChildrenReply) error {
+ select {
+ case <-ctx.Done():
+ return verror.New(verror.ErrAborted, ctx)
+ default:
+ }
+ switch v := reply.(type) {
+ case naming.GlobChildrenReplyName:
+ child := v.Value
+ if len(child) == 0 || strings.Contains(child, "/") {
+ return verror.New(verror.ErrBadArg, ctx, fmt.Sprintf("invalid child name: %q", child))
+ }
+ if !matcher.Match(child) {
+ return verror.New(verror.ErrBadArg, ctx, fmt.Sprintf("child name does not match: %q", child))
+ }
+ next := naming.Join(state.name, child)
+ queue = append(queue, gState{next, tail, depth})
+ case naming.GlobChildrenReplyError:
+ v.Value.Name = naming.Join(state.name, v.Value.Name)
+ return subcall.Send(naming.GlobReplyError{v.Value})
+ }
+ return nil
+ }
+ if err := gs.ChildrenGlobberX.GlobChildren__(ctx, &globChildrenServerCall{subcall, send}, matcher); err != nil {
+ subcall.Send(naming.GlobReplyError{naming.GlobError{Name: state.name, Error: verror.Convert(verror.ErrInternal, ctx, err)}})
+ }
+ continue
}
}
if someMatchesOmitted {
@@ -337,6 +409,36 @@
return nil
}
+type globServerCall struct {
+ rpc.ServerCall
+ send func(reply naming.GlobReply) error
+}
+
+func (g *globServerCall) SendStream() interface {
+ Send(naming.GlobReply) error
+} {
+ return g
+}
+
+func (g *globServerCall) Send(reply naming.GlobReply) error {
+ return g.send(reply)
+}
+
+type globChildrenServerCall struct {
+ rpc.ServerCall
+ send func(reply naming.GlobChildrenReply) error
+}
+
+func (g *globChildrenServerCall) SendStream() interface {
+ Send(naming.GlobChildrenReply) error
+} {
+ return g
+}
+
+func (g *globChildrenServerCall) Send(reply naming.GlobChildrenReply) error {
+ return g.send(reply)
+}
+
// derivedServerCall allows us to derive calls with slightly different properties,
// useful for our various special-cased reserved methods.
type derivedServerCall struct {
diff --git a/runtime/internal/rpc/test/glob_test.go b/runtime/internal/rpc/test/glob_test.go
index 8a53bd7..d16e4bd 100644
--- a/runtime/internal/rpc/test/glob_test.go
+++ b/runtime/internal/rpc/test/glob_test.go
@@ -35,6 +35,7 @@
"a/b/c2/d1",
"a/b/c2/d2",
"a/x/y/z",
+ "a/X/y/z",
"leaf",
}
tree := newNode()
@@ -62,6 +63,9 @@
{"", "...", []string{
"",
"a",
+ "a/X",
+ "a/X/y",
+ "a/X/y/z",
"a/b",
"a/b/c1",
"a/b/c1/d1",
@@ -76,6 +80,9 @@
}, nil},
{"a", "...", []string{
"",
+ "X",
+ "X/y",
+ "X/y/z",
"b",
"b/c1",
"b/c1/d1",
@@ -116,19 +123,28 @@
{"a/x/y/z", "...", []string{
"",
}, nil},
+ {"a/X", "...", []string{
+ "",
+ "y",
+ "y/z",
+ }, nil},
{"", "", []string{""}, nil},
{"", "*", []string{"a", "leaf"}, nil},
{"a", "", []string{""}, nil},
- {"a", "*", []string{"b", "x"}, nil},
+ {"a", "*", []string{"X", "b", "x"}, nil},
{"a/b", "", []string{""}, nil},
{"a/b", "*", []string{"c1", "c2"}, nil},
{"a/b/c1", "", []string{""}, nil},
{"a/b/c1", "*", []string{"d1", "d2"}, nil},
{"a/b/c1/d1", "*", []string{}, nil},
{"a/b/c1/d1", "", []string{""}, nil},
+ {"a/b/c2", "", []string{""}, nil},
+ {"a/b/c2", "*", []string{"d1", "d2"}, nil},
+ {"a/b/c2/d1", "*", []string{}, nil},
+ {"a/b/c2/d1", "", []string{""}, nil},
{"a", "*/c?", []string{"b/c1", "b/c2"}, nil},
- {"a", "*/*", []string{"b/c1", "b/c2", "x/y"}, nil},
- {"a", "*/*/*", []string{"b/c1/d1", "b/c1/d2", "b/c2/d1", "b/c2/d2", "x/y/z"}, nil},
+ {"a", "*/*", []string{"X/y", "b/c1", "b/c2", "x/y"}, nil},
+ {"a", "*/*/*", []string{"X/y/z", "b/c1/d1", "b/c1/d2", "b/c2/d1", "b/c2/d2", "x/y/z"}, nil},
{"a/x", "*/*", []string{"y/z"}, nil},
{"bad", "", []string{}, []naming.GlobError{{Name: "", Error: noExist}}},
{"bad/foo", "", []string{}, []naming.GlobError{{Name: "", Error: noExist}}},
@@ -270,6 +286,12 @@
if len(elems) < 2 || (elems[0] == "a" && elems[1] == "x") {
return &vChildrenObject{d.tree, elems}, auth, nil
}
+ if len(elems) < 2 || (elems[0] == "a" && elems[1] == "X") {
+ return &vChildrenXObject{d.tree, elems}, auth, nil
+ }
+ if len(elems) >= 3 && elems[0] == "a" && elems[1] == "b" && elems[2] == "c2" {
+ return &globXObject{d.tree, elems}, auth, nil
+ }
return &globObject{d.tree, elems}, auth, nil
}
@@ -334,6 +356,54 @@
return ch, nil
}
+type globXObject struct {
+ n *node
+ suffix []string
+}
+
+func (o *globXObject) Glob__(ctx *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
+ n := o.n.find(o.suffix, false)
+ if n == nil {
+ return verror.New(verror.ErrNoExist, ctx, o.suffix)
+ }
+ o.globLoop(call, "", g, n)
+ return nil
+}
+
+func (o *globXObject) globLoop(call rpc.GlobServerCall, name string, g *glob.Glob, n *node) {
+ if g.Len() == 0 {
+ call.SendStream().Send(naming.GlobReplyEntry{naming.MountEntry{Name: name}})
+ }
+ if g.Empty() {
+ return
+ }
+ matcher, left := g.Head(), g.Tail()
+ for leaf, child := range n.children {
+ if matcher.Match(leaf) {
+ o.globLoop(call, naming.Join(name, leaf), left, child)
+ }
+ }
+}
+
+type vChildrenXObject struct {
+ n *node
+ suffix []string
+}
+
+func (o *vChildrenXObject) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
+ n := o.n.find(o.suffix, false)
+ if n == nil {
+ return verror.New(verror.ErrNoExist, ctx, o.suffix)
+ }
+ sender := call.SendStream()
+ for child, _ := range n.children {
+ if m.Match(child) {
+ sender.Send(naming.GlobChildrenReplyName{child})
+ }
+ }
+ return nil
+}
+
type node struct {
children map[string]*node
}
diff --git a/services/application/applicationd/service.go b/services/application/applicationd/service.go
index e11f26d..c4e833e 100644
--- a/services/application/applicationd/service.go
+++ b/services/application/applicationd/service.go
@@ -9,6 +9,7 @@
"strings"
"v.io/v23/context"
+ "v.io/v23/glob"
"v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/security"
@@ -208,7 +209,7 @@
return i.allAppVersionsForProfiles(appName, profiles)
}
-func (i *appRepoService) GlobChildren__(ctx *context.T, _ rpc.ServerCall) (<-chan string, error) {
+func (i *appRepoService) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
ctx.VI(0).Infof("%v.GlobChildren__()", i.suffix)
i.store.Lock()
defer i.store.Unlock()
@@ -224,34 +225,34 @@
case 0:
results, err = i.allApplications()
if err != nil {
- return nil, err
+ return err
}
case 1:
results, err = i.allAppVersions(elems[0])
if err != nil {
- return nil, err
+ return err
}
case 2:
versions, err := i.allAppVersions(elems[0])
if err != nil {
- return nil, err
+ return err
}
for _, v := range versions {
if v == elems[1] {
- return nil, nil
+ return nil
}
}
- return nil, verror.New(verror.ErrNoExist, nil)
+ return verror.New(verror.ErrNoExist, nil)
default:
- return nil, verror.New(verror.ErrNoExist, nil)
+ return verror.New(verror.ErrNoExist, nil)
}
- ch := make(chan string, len(results))
for _, r := range results {
- ch <- r
+ if m.Match(r) {
+ call.SendStream().Send(naming.GlobChildrenReplyName{r})
+ }
}
- close(ch)
- return ch, nil
+ return nil
}
func (i *appRepoService) GetPermissions(ctx *context.T, call rpc.ServerCall) (perms access.Permissions, version string, err error) {
diff --git a/services/device/device/devicemanager_mock_test.go b/services/device/device/devicemanager_mock_test.go
index d683d0b..6d1e486 100644
--- a/services/device/device/devicemanager_mock_test.go
+++ b/services/device/device/devicemanager_mock_test.go
@@ -14,6 +14,7 @@
"time"
"v.io/v23/context"
+ "v.io/v23/glob"
"v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/security"
@@ -291,15 +292,13 @@
err error
}
-func (mdi *mockDeviceInvoker) Glob__(_ *context.T, _ rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
- gs := GlobStimulus{pattern}
+func (mdi *mockDeviceInvoker) Glob__(_ *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
+ gs := GlobStimulus{g.String()}
gr := mdi.tape.Record(gs).(GlobResponse)
- ch := make(chan naming.GlobReply, len(gr.results))
- defer close(ch)
for _, r := range gr.results {
- ch <- naming.GlobReplyEntry{naming.MountEntry{Name: r}}
+ call.SendStream().Send(naming.GlobReplyEntry{naming.MountEntry{Name: r}})
}
- return ch, gr.err
+ return gr.err
}
type dispatcher struct {
diff --git a/services/device/deviced/internal/impl/app_service.go b/services/device/deviced/internal/impl/app_service.go
index 2646685..576ffa3 100644
--- a/services/device/deviced/internal/impl/app_service.go
+++ b/services/device/deviced/internal/impl/app_service.go
@@ -135,6 +135,7 @@
"v.io/v23"
"v.io/v23/context"
+ "v.io/v23/glob"
"v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/security"
@@ -1429,7 +1430,7 @@
}
}
-func (i *appService) GlobChildren__(ctx *context.T, _ rpc.ServerCall) (<-chan string, error) {
+func (i *appService) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
tree := newTreeNode()
switch len(i.suffix) {
case 0:
@@ -1446,20 +1447,18 @@
}
i.scanInstance(ctx, tree, i.suffix[0], dir)
default:
- return nil, verror.New(verror.ErrNoExist, nil, i.suffix)
+ return verror.New(verror.ErrNoExist, nil, i.suffix)
}
n := tree.find(i.suffix, false)
if n == nil {
- return nil, verror.New(errors.ErrInvalidSuffix, nil)
+ return verror.New(errors.ErrInvalidSuffix, nil)
}
- ch := make(chan string)
- go func() {
- for child, _ := range n.children {
- ch <- child
+ for child, _ := range n.children {
+ if m.Match(child) {
+ call.SendStream().Send(naming.GlobChildrenReplyName{child})
}
- close(ch)
- }()
- return ch, nil
+ }
+ return nil
}
// TODO(rjkroege): Refactor to eliminate redundancy with newAppSpecificAuthorizer.
diff --git a/services/device/deviced/internal/impl/proxy_invoker.go b/services/device/deviced/internal/impl/proxy_invoker.go
index 5eb1d08..c395de6 100644
--- a/services/device/deviced/internal/impl/proxy_invoker.go
+++ b/services/device/deviced/internal/impl/proxy_invoker.go
@@ -10,6 +10,7 @@
"v.io/v23"
"v.io/v23/context"
+ "v.io/v23/glob"
"v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/security/access"
@@ -213,12 +214,11 @@
}
func (p *proxyInvoker) Globber() *rpc.GlobState {
- return &rpc.GlobState{AllGlobber: p}
+ return &rpc.GlobState{AllGlobberX: p}
}
type call struct {
- rpc.ServerCall
- ch chan<- naming.GlobReply
+ rpc.GlobServerCall
}
func (c *call) Recv(v interface{}) error {
@@ -226,17 +226,13 @@
}
func (c *call) Send(v interface{}) error {
- c.ch <- v.(naming.GlobReply)
- return nil
+ return c.SendStream().Send(v.(naming.GlobReply))
}
-func (p *proxyInvoker) Glob__(ctx *context.T, serverCall rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
- ch := make(chan naming.GlobReply)
- go func() {
- p.Invoke(ctx, &call{serverCall, ch}, rpc.GlobMethod, []interface{}{&pattern})
- close(ch)
- }()
- return ch, nil
+func (p *proxyInvoker) Glob__(ctx *context.T, serverCall rpc.GlobServerCall, g *glob.Glob) error {
+ pattern := g.String()
+ p.Invoke(ctx, &call{serverCall}, rpc.GlobMethod, []interface{}{&pattern})
+ return nil
}
// numResults returns the number of result values for the given method.
diff --git a/services/internal/binarylib/service.go b/services/internal/binarylib/service.go
index e9bf201..8a706e9 100644
--- a/services/internal/binarylib/service.go
+++ b/services/internal/binarylib/service.go
@@ -41,6 +41,8 @@
"syscall"
"v.io/v23/context"
+ "v.io/v23/glob"
+ "v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/security"
"v.io/v23/security/access"
@@ -351,23 +353,21 @@
return nil
}
-func (i *binaryService) GlobChildren__(ctx *context.T, _ rpc.ServerCall) (<-chan string, error) {
+func (i *binaryService) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
elems := strings.Split(i.suffix, "/")
if len(elems) == 1 && elems[0] == "" {
elems = nil
}
n := i.createObjectNameTree().find(elems, false)
if n == nil {
- return nil, verror.New(ErrOperationFailed, ctx)
+ return verror.New(ErrOperationFailed, ctx)
}
- ch := make(chan string)
- go func() {
- for k, _ := range n.children {
- ch <- k
+ for k, _ := range n.children {
+ if m.Match(k) {
+ call.SendStream().Send(naming.GlobChildrenReplyName{k})
}
- close(ch)
- }()
- return ch, nil
+ }
+ return nil
}
func (i *binaryService) GetPermissions(ctx *context.T, call rpc.ServerCall) (perms access.Permissions, version string, err error) {
diff --git a/services/internal/logreaderlib/logfile.go b/services/internal/logreaderlib/logfile.go
index 3f24a21..5a4109d 100644
--- a/services/internal/logreaderlib/logfile.go
+++ b/services/internal/logreaderlib/logfile.go
@@ -16,6 +16,8 @@
"strings"
"v.io/v23/context"
+ "v.io/v23/glob"
+ "v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/services/logreader"
"v.io/v23/verror"
@@ -112,42 +114,44 @@
return reader.tell(), nil
}
-// GlobChildren__ returns the list of files in a directory streamed on a
-// channel. The list is empty if the object is a file.
-func (i *logfileService) GlobChildren__(ctx *context.T, _ rpc.ServerCall) (<-chan string, error) {
+// GlobChildren__ returns the list of files in a directory on a stream.
+// The list is empty if the object is a file.
+func (i *logfileService) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
ctx.VI(1).Infof("%v.GlobChildren__()", i.suffix)
dirName, err := translateNameToFilename(i.root, i.suffix)
if err != nil {
- return nil, err
+ return err
}
stat, err := os.Stat(dirName)
if err != nil {
if os.IsNotExist(err) {
- return nil, verror.New(verror.ErrNoExist, ctx, dirName)
+ return verror.New(verror.ErrNoExist, ctx, dirName)
}
- return nil, verror.New(errOperationFailed, ctx, dirName)
+ return verror.New(errOperationFailed, ctx, dirName)
}
if !stat.IsDir() {
- return nil, nil
+ return nil
}
- ch := make(chan string)
- go func() {
- defer close(ch)
- f, err := os.Open(dirName)
+ f, err := os.Open(dirName)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ for {
+ fi, err := f.Readdir(100)
+ if err == io.EOF {
+ break
+ }
if err != nil {
- return
+ return err
}
- defer f.Close()
- for {
- fi, err := f.Readdir(100)
- if err != nil {
- return
- }
- for _, file := range fi {
- ch <- file.Name()
+ for _, file := range fi {
+ name := file.Name()
+ if m.Match(name) {
+ call.SendStream().Send(naming.GlobChildrenReplyName{name})
}
}
- }()
- return ch, nil
+ }
+ return nil
}
diff --git a/services/internal/statslib/stats.go b/services/internal/statslib/stats.go
index 6fc9b49..ddc2735 100644
--- a/services/internal/statslib/stats.go
+++ b/services/internal/statslib/stats.go
@@ -11,6 +11,7 @@
"time"
"v.io/v23/context"
+ "v.io/v23/glob"
"v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/services/stats"
@@ -38,21 +39,17 @@
}
// Glob__ returns the name of all objects that match pattern.
-func (i *statsService) Glob__(ctx *context.T, call rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
- ctx.VI(1).Infof("%v.Glob__(%q)", i.suffix, pattern)
-
- ch := make(chan naming.GlobReply)
- go func() {
- defer close(ch)
- it := libstats.Glob(i.suffix, pattern, time.Time{}, false)
- for it.Advance() {
- ch <- naming.GlobReplyEntry{naming.MountEntry{Name: it.Value().Key}}
- }
- if err := it.Err(); err != nil {
- ctx.VI(1).Infof("libstats.Glob(%q, %q) failed: %v", i.suffix, pattern, err)
- }
- }()
- return ch, nil
+func (i *statsService) Glob__(ctx *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
+ ctx.VI(1).Infof("%v.Glob__(%q)", i.suffix, g.String())
+ sender := call.SendStream()
+ it := libstats.Glob(i.suffix, g.String(), time.Time{}, false)
+ for it.Advance() {
+ sender.Send(naming.GlobReplyEntry{naming.MountEntry{Name: it.Value().Key}})
+ }
+ if err := it.Err(); err != nil {
+ ctx.VI(1).Infof("libstats.Glob(%q, %q) failed: %v", i.suffix, g.String(), err)
+ }
+ return nil
}
// WatchGlob returns the name and value of the objects that match the request,
diff --git a/services/mounttable/mounttablelib/mounttable.go b/services/mounttable/mounttablelib/mounttable.go
index 666a355..c391c89 100644
--- a/services/mounttable/mounttablelib/mounttable.go
+++ b/services/mounttable/mounttablelib/mounttable.go
@@ -645,7 +645,7 @@
}
// globStep is called with n and n.parent locked. Returns with both unlocked.
-func (mt *mountTable) globStep(ctx *context.T, call security.Call, n *node, name string, pattern *glob.Glob, ch chan<- naming.GlobReply) {
+func (mt *mountTable) globStep(ctx *context.T, call security.Call, n *node, name string, pattern *glob.Glob, gCall rpc.GlobServerCall) {
if shouldAbort(ctx) {
n.parent.Unlock()
n.Unlock()
@@ -679,7 +679,7 @@
}
// Hold no locks while we are sending on the channel to avoid livelock.
n.Unlock()
- ch <- naming.GlobReplyEntry{me}
+ gCall.SendStream().Send(naming.GlobReplyEntry{me})
return
}
@@ -721,7 +721,7 @@
c.Unlock()
continue
}
- mt.globStep(ctx, call, c, naming.Join(name, k), suffix, ch)
+ mt.globStep(ctx, call, c, naming.Join(name, k), suffix, gCall)
n.Lock()
}
}
@@ -749,7 +749,7 @@
// Hold no locks while we are sending on the channel to avoid livelock.
n.Unlock()
// Intermediate nodes are marked as serving a mounttable since they answer the mounttable methods.
- ch <- naming.GlobReplyEntry{naming.MountEntry{Name: name, ServesMountTable: true}}
+ gCall.SendStream().Send(naming.GlobReplyEntry{naming.MountEntry{Name: name, ServesMountTable: true}})
}
// Glob finds matches in the namespace. If we reach a mount point before matching the
@@ -763,38 +763,29 @@
// a state that never existed in the mounttable. For example, if someone removes c/d and later
// adds a/b while a Glob is in progress, the Glob may return a set of nodes that includes both
// c/d and a/b.
-func (ms *mountContext) Glob__(ctx *context.T, call rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
+func (ms *mountContext) Glob__(ctx *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
ctx.VI(2).Infof("mt.Glob %v", ms.elems)
scall := call.Security()
- g, err := glob.Parse(pattern)
- if err != nil {
- return nil, err
- }
-
mt := ms.mt
- ch := make(chan naming.GlobReply)
- go func() {
- defer close(ch)
- // If there was an access error, just ignore the entry, i.e., make it invisible.
- n, err := mt.findNode(ctx, scall, ms.elems, false, nil, nil)
- if err != nil {
- return
- }
- // If the current name is not fully resolvable on this nameserver we
- // don't need to evaluate the glob expression. Send a partially resolved
- // name back to the client.
- if n == nil {
- ms.linkToLeaf(ctx, scall, ch)
- return
- }
- mt.globStep(ctx, scall, n, "", g, ch)
- }()
- return ch, nil
+ // If there was an access error, just ignore the entry, i.e., make it invisible.
+ n, err := mt.findNode(ctx, scall, ms.elems, false, nil, nil)
+ if err != nil {
+ return nil
+ }
+ // If the current name is not fully resolvable on this nameserver we
+ // don't need to evaluate the glob expression. Send a partially resolved
+ // name back to the client.
+ if n == nil {
+ ms.linkToLeaf(ctx, scall, call)
+ return nil
+ }
+ mt.globStep(ctx, scall, n, "", g, call)
+ return nil
}
-func (ms *mountContext) linkToLeaf(ctx *context.T, call security.Call, ch chan<- naming.GlobReply) {
- n, elems, err := ms.mt.findMountPoint(ctx, call, ms.elems)
+func (ms *mountContext) linkToLeaf(ctx *context.T, sCall security.Call, gCall rpc.GlobServerCall) {
+ n, elems, err := ms.mt.findMountPoint(ctx, sCall, ms.elems)
if err != nil || n == nil {
return
}
@@ -804,7 +795,7 @@
servers[i].Server = naming.Join(s.Server, strings.Join(elems, "/"))
}
n.Unlock()
- ch <- naming.GlobReplyEntry{naming.MountEntry{Name: "", Servers: servers}}
+ gCall.SendStream().Send(naming.GlobReplyEntry{naming.MountEntry{Name: "", Servers: servers}})
}
func (ms *mountContext) SetPermissions(ctx *context.T, call rpc.ServerCall, perms access.Permissions, version string) error {
diff --git a/services/mounttable/mounttablelib/mounttable_test.go b/services/mounttable/mounttablelib/mounttable_test.go
index 981bc7a..879943b 100644
--- a/services/mounttable/mounttablelib/mounttable_test.go
+++ b/services/mounttable/mounttablelib/mounttable_test.go
@@ -18,6 +18,7 @@
"v.io/v23"
"v.io/v23/context"
"v.io/v23/conventions"
+ "v.io/v23/glob"
"v.io/v23/naming"
"v.io/v23/options"
"v.io/v23/rpc"
@@ -436,7 +437,9 @@
}
}
-type fakeServerCall struct{}
+type fakeServerCall struct {
+ sendCount int
+}
func (fakeServerCall) Security() security.Call { return security.NewCall(&security.CallParams{}) }
func (fakeServerCall) Suffix() string { return "" }
@@ -444,6 +447,15 @@
func (fakeServerCall) RemoteEndpoint() naming.Endpoint { return nil }
func (fakeServerCall) GrantedBlessings() security.Blessings { return security.Blessings{} }
func (fakeServerCall) Server() rpc.Server { return nil }
+func (c *fakeServerCall) SendStream() interface {
+ Send(naming.GlobReply) error
+} {
+ return c
+}
+func (c *fakeServerCall) Send(reply naming.GlobReply) error {
+ c.sendCount++
+ return nil
+}
func TestGlobAborts(t *testing.T) {
ctx, shutdown := test.V23Init()
@@ -470,15 +482,10 @@
glob := func(ctx *context.T) (int, error) {
root, _, _ := mt.Lookup(ctx, "")
- ch, err := root.(rpc.Globber).Globber().AllGlobber.Glob__(ctx, fakeServerCall{}, "...")
- if err != nil {
- return 0, err
- }
- num := 0
- for range ch {
- num++
- }
- return num, nil
+ g, _ := glob.Parse("...")
+ fCall := &fakeServerCall{}
+ root.(rpc.Globber).Globber().AllGlobberX.Glob__(ctx, fCall, g)
+ return fCall.sendCount, nil
}
got, err := glob(ctx)
diff --git a/services/mounttable/mounttablelib/neighborhood.go b/services/mounttable/mounttablelib/neighborhood.go
index 44a2cdb..75488c2 100644
--- a/services/mounttable/mounttablelib/neighborhood.go
+++ b/services/mounttable/mounttablelib/neighborhood.go
@@ -268,39 +268,29 @@
}
// Glob__ implements rpc.AllGlobber
-func (ns *neighborhoodService) Glob__(ctx *context.T, _ rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
- g, err := glob.Parse(pattern)
- if err != nil {
- return nil, err
- }
-
+func (ns *neighborhoodService) Glob__(ctx *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
// return all neighbors that match the first element of the pattern.
nh := ns.nh
+ sender := call.SendStream()
switch len(ns.elems) {
case 0:
- ch := make(chan naming.GlobReply)
- go func() {
- defer close(ch)
- matcher := g.Head()
- for k, n := range nh.neighbors() {
- if matcher.Match(k) {
- ch <- naming.GlobReplyEntry{naming.MountEntry{Name: k, Servers: n, ServesMountTable: true}}
- }
+ matcher := g.Head()
+ for k, n := range nh.neighbors() {
+ if matcher.Match(k) {
+ sender.Send(naming.GlobReplyEntry{naming.MountEntry{Name: k, Servers: n, ServesMountTable: true}})
}
- }()
- return ch, nil
+ }
+ return nil
case 1:
neighbor := nh.neighbor(ns.elems[0])
if neighbor == nil {
- return nil, verror.New(naming.ErrNoSuchName, ctx, ns.elems[0])
+ return verror.New(naming.ErrNoSuchName, ctx, ns.elems[0])
}
- ch := make(chan naming.GlobReply, 1)
- ch <- naming.GlobReplyEntry{naming.MountEntry{Name: "", Servers: neighbor, ServesMountTable: true}}
- close(ch)
- return ch, nil
+ sender.Send(naming.GlobReplyEntry{naming.MountEntry{Name: "", Servers: neighbor, ServesMountTable: true}})
+ return nil
default:
- return nil, verror.New(naming.ErrNoSuchName, ctx, ns.elems)
+ return verror.New(naming.ErrNoSuchName, ctx, ns.elems)
}
}
diff --git a/services/role/roled/internal/discharger.go b/services/role/roled/internal/discharger.go
index d6db009..f0321ee 100644
--- a/services/role/roled/internal/discharger.go
+++ b/services/role/roled/internal/discharger.go
@@ -9,6 +9,7 @@
"v.io/v23"
"v.io/v23/context"
+ "v.io/v23/glob"
"v.io/v23/rpc"
"v.io/v23/security"
"v.io/v23/verror"
@@ -59,6 +60,6 @@
return discharge, nil
}
-func (d *dischargerImpl) GlobChildren__(ctx *context.T, call rpc.ServerCall) (<-chan string, error) {
- return globChildren(ctx, call.Security(), d.serverConfig)
+func (d *dischargerImpl) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
+ return globChildren(ctx, call, d.serverConfig, m)
}
diff --git a/services/role/roled/internal/glob.go b/services/role/roled/internal/glob.go
index bb854f2..88b0478 100644
--- a/services/role/roled/internal/glob.go
+++ b/services/role/roled/internal/glob.go
@@ -10,25 +10,29 @@
"strings"
"v.io/v23/context"
+ "v.io/v23/glob"
+ "v.io/v23/naming"
+ "v.io/v23/rpc"
"v.io/v23/security"
"v.io/v23/verror"
)
-func globChildren(ctx *context.T, call security.Call, serverConfig *serverConfig) (<-chan string, error) {
- n := findRoles(ctx, call, serverConfig.root)
- suffix := call.Suffix()
+func globChildren(ctx *context.T, call rpc.GlobChildrenServerCall, serverConfig *serverConfig, m *glob.Element) error {
+ sCall := call.Security()
+ n := findRoles(ctx, sCall, serverConfig.root)
+ suffix := sCall.Suffix()
if len(suffix) > 0 {
n = n.find(strings.Split(suffix, "/"), false)
}
if n == nil {
- return nil, verror.New(verror.ErrNoExistOrNoAccess, ctx)
+ return verror.New(verror.ErrNoExistOrNoAccess, ctx)
}
- ch := make(chan string, len(n.children))
for c := range n.children {
- ch <- c
+ if m.Match(c) {
+ call.SendStream().Send(naming.GlobChildrenReplyName{c})
+ }
}
- close(ch)
- return ch, nil
+ return nil
}
// findRoles finds all the roles to which the caller has access.
diff --git a/services/role/roled/internal/role.go b/services/role/roled/internal/role.go
index a46a31d..5e4aadc 100644
--- a/services/role/roled/internal/role.go
+++ b/services/role/roled/internal/role.go
@@ -10,6 +10,7 @@
"v.io/v23"
"v.io/v23/context"
+ "v.io/v23/glob"
"v.io/v23/rpc"
"v.io/v23/security"
"v.io/v23/verror"
@@ -46,8 +47,8 @@
return createBlessings(ctx, call.Security(), i.roleConfig, v23.GetPrincipal(ctx), extensions, caveats, i.serverConfig.dischargerLocation)
}
-func (i *roleService) GlobChildren__(ctx *context.T, call rpc.ServerCall) (<-chan string, error) {
- return globChildren(ctx, call.Security(), i.serverConfig)
+func (i *roleService) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, m *glob.Element) error {
+ return globChildren(ctx, call, i.serverConfig, m)
}
// filterNonMembers returns only the blessing names that are authorized members
diff --git a/services/wspr/internal/rpc/server/invoker.go b/services/wspr/internal/rpc/server/invoker.go
index f10c43c..9e6e40c 100644
--- a/services/wspr/internal/rpc/server/invoker.go
+++ b/services/wspr/internal/rpc/server/invoker.go
@@ -6,7 +6,7 @@
import (
"v.io/v23/context"
- "v.io/v23/naming"
+ "v.io/v23/glob"
"v.io/v23/rpc"
"v.io/v23/vdl"
"v.io/v23/vdlroot/signature"
@@ -80,16 +80,23 @@
return results, nil
}
-// TODO(bjornick,rthellend): Find a reasonable way to implement this for JS.
func (i *invoker) Globber() *rpc.GlobState {
if i.globFunc == nil {
return nil
}
- return &rpc.GlobState{AllGlobber: i}
+ return &rpc.GlobState{AllGlobberX: i}
}
-func (i *invoker) Glob__(ctx *context.T, call rpc.ServerCall, pattern string) (<-chan naming.GlobReply, error) {
- return i.globFunc(ctx, call, pattern)
+func (i *invoker) Glob__(ctx *context.T, call rpc.GlobServerCall, g *glob.Glob) error {
+ // TODO(rthellend,bjornick): Should we convert globFunc to match the
+ // new Glob__ interface?
+ ch, err := i.globFunc(ctx, call, g.String())
+ if ch != nil {
+ for reply := range ch {
+ call.SendStream().Send(reply)
+ }
+ }
+ return err
}
func (i *invoker) Signature(ctx *context.T, call rpc.ServerCall) ([]signature.Interface, error) {