blob: 5793780115a13dc789111c6d2775a9f5ff97fa37 [file] [log] [blame]
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001package ipc
2
3import (
4 "errors"
5 "fmt"
6 "io"
Bogdan Caprita187269b2014-05-13 19:59:46 -07007 "net"
Bogdan Caprita783f7792014-05-15 09:29:17 -07008 "os"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07009 "reflect"
10 "strings"
Bogdan Caprita27953142014-05-12 11:41:42 -070011 "sync"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070012 "testing"
13 "time"
14
Bogdan Caprita783f7792014-05-15 09:29:17 -070015 "veyron/lib/testutil"
16 "veyron/lib/testutil/blackbox"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070017 imanager "veyron/runtimes/google/ipc/stream/manager"
Asim Shankar0ea02ab2014-06-09 11:39:24 -070018 "veyron/runtimes/google/ipc/stream/proxy"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070019 "veyron/runtimes/google/ipc/stream/vc"
20 "veyron/runtimes/google/ipc/version"
Asim Shankarae713442014-06-05 15:31:23 -070021 "veyron/runtimes/google/lib/publisher"
Bogdan Caprita187269b2014-05-13 19:59:46 -070022 inaming "veyron/runtimes/google/naming"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070023 isecurity "veyron/runtimes/google/security"
Asim Shankar45054a62014-05-15 10:32:54 -070024 "veyron/security/caveat"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070025
26 "veyron2"
Matt Rosencrantz29147f72014-06-06 12:46:01 -070027 "veyron2/context"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070028 "veyron2/ipc"
29 "veyron2/ipc/stream"
30 "veyron2/naming"
31 "veyron2/security"
32 "veyron2/verror"
33 "veyron2/vlog"
34)
35
36var (
37 errMethod = verror.Abortedf("server returned an error")
38 clientID security.PrivateID
39 serverID security.PrivateID
40)
41
42var errAuthorizer = errors.New("ipc: application Authorizer denied access")
43
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -070044type fakeContext struct{}
45
Jiri Simsa5293dcb2014-05-10 09:56:38 -070046type userType string
47
48type testServer struct{}
49
50func (*testServer) Closure(call ipc.ServerCall) {
51}
52
53func (*testServer) Error(call ipc.ServerCall) error {
54 return errMethod
55}
56
57func (*testServer) Echo(call ipc.ServerCall, arg string) string {
58 return fmt.Sprintf("method:%q,suffix:%q,arg:%q", call.Method(), call.Suffix(), arg)
59}
60
61func (*testServer) EchoUser(call ipc.ServerCall, arg string, u userType) (string, userType) {
62 return fmt.Sprintf("method:%q,suffix:%q,arg:%q", call.Method(), call.Suffix(), arg), u
63}
64
65func (*testServer) EchoIDs(call ipc.ServerCall) (server, client string) {
66 return fmt.Sprintf("%v", call.LocalID()), fmt.Sprintf("%v", call.RemoteID())
67}
68
Asim Shankarb54d7642014-06-05 13:08:04 -070069func (*testServer) EchoBlessing(call ipc.ServerCall, arg string) (result, blessing string) {
70 return arg, fmt.Sprintf("%v", call.Blessing())
71}
72
Jiri Simsa5293dcb2014-05-10 09:56:38 -070073func (*testServer) EchoAndError(call ipc.ServerCall, arg string) (string, error) {
74 result := fmt.Sprintf("method:%q,suffix:%q,arg:%q", call.Method(), call.Suffix(), arg)
75 if arg == "error" {
76 return result, errMethod
77 }
78 return result, nil
79}
80
81func (*testServer) Stream(call ipc.ServerCall, arg string) (string, error) {
82 result := fmt.Sprintf("method:%q,suffix:%q,arg:%q", call.Method(), call.Suffix(), arg)
83 var u userType
84 var err error
85 for err = call.Recv(&u); err == nil; err = call.Recv(&u) {
86 result += " " + string(u)
87 if err := call.Send(u); err != nil {
88 return "", err
89 }
90 }
91 if err == io.EOF {
92 err = nil
93 }
94 return result, err
95}
96
97func (*testServer) Unauthorized(ipc.ServerCall) (string, error) {
98 return "UnauthorizedResult", fmt.Errorf("Unauthorized should never be called")
99}
100
101type testServerAuthorizer struct{}
102
103func (testServerAuthorizer) Authorize(c security.Context) error {
104 if c.Method() != "Unauthorized" {
105 return nil
106 }
107 return errAuthorizer
108}
109
110type testServerDisp struct{ server interface{} }
111
112func (t testServerDisp) Lookup(suffix string) (ipc.Invoker, security.Authorizer, error) {
113 // If suffix is "nilAuth" we use default authorization, if it is "aclAuth" we
114 // use an ACL based authorizer, and otherwise we use the custom testServerAuthorizer.
115 if suffix == "nilAuth" {
116 return ipc.ReflectInvoker(t.server), nil, nil
117 }
118 if suffix == "aclAuth" {
119 // Only authorize clients matching patterns "client" or "server/*".
120 acl := security.ACL{
121 "server/*": security.LabelSet(security.AdminLabel),
122 "client": security.LabelSet(security.AdminLabel),
123 }
Ankur992269a2014-05-13 13:03:24 -0700124 return ipc.ReflectInvoker(t.server), security.NewACLAuthorizer(acl), nil
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700125 }
126 return ipc.ReflectInvoker(t.server), testServerAuthorizer{}, nil
127}
128
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700129// namespace is a simple partial implementation of naming.Namespace. In
Bogdan Caprita27953142014-05-12 11:41:42 -0700130// particular, it ignores TTLs and not allow fully overlapping mount names.
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700131type namespace struct {
Bogdan Caprita27953142014-05-12 11:41:42 -0700132 sync.Mutex
133 mounts map[string][]string
134}
135
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700136func newNamespace() naming.Namespace {
137 return &namespace{mounts: make(map[string][]string)}
Bogdan Caprita27953142014-05-12 11:41:42 -0700138}
139
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700140func (ns *namespace) Mount(ctx context.T, name, server string, _ time.Duration) error {
141 ns.Lock()
142 defer ns.Unlock()
143 for n, _ := range ns.mounts {
Bogdan Caprita27953142014-05-12 11:41:42 -0700144 if n != name && (strings.HasPrefix(name, n) || strings.HasPrefix(n, name)) {
145 return fmt.Errorf("simple mount table does not allow names that are a prefix of each other")
146 }
147 }
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700148 ns.mounts[name] = append(ns.mounts[name], server)
Bogdan Caprita27953142014-05-12 11:41:42 -0700149 return nil
150}
151
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700152func (ns *namespace) Unmount(ctx context.T, name, server string) error {
Bogdan Caprita27953142014-05-12 11:41:42 -0700153 var servers []string
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700154 ns.Lock()
155 defer ns.Unlock()
156 for _, s := range ns.mounts[name] {
Bogdan Caprita27953142014-05-12 11:41:42 -0700157 // When server is "", we remove all servers under name.
158 if len(server) > 0 && s != server {
159 servers = append(servers, s)
160 }
161 }
162 if len(servers) > 0 {
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700163 ns.mounts[name] = servers
Bogdan Caprita27953142014-05-12 11:41:42 -0700164 } else {
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700165 delete(ns.mounts, name)
Bogdan Caprita27953142014-05-12 11:41:42 -0700166 }
167 return nil
168}
169
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700170func (ns *namespace) Resolve(ctx context.T, name string) ([]string, error) {
Bogdan Caprita27953142014-05-12 11:41:42 -0700171 if address, _ := naming.SplitAddressName(name); len(address) > 0 {
172 return []string{name}, nil
173 }
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700174 ns.Lock()
175 defer ns.Unlock()
176 for prefix, servers := range ns.mounts {
Bogdan Caprita27953142014-05-12 11:41:42 -0700177 if strings.HasPrefix(name, prefix) {
178 suffix := strings.TrimLeft(strings.TrimPrefix(name, prefix), "/")
179 var ret []string
180 for _, s := range servers {
181 ret = append(ret, naming.Join(s, suffix))
182 }
183 return ret, nil
184 }
185 }
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700186 return nil, verror.NotFoundf("Resolve name %q not found in %v", name, ns.mounts)
Bogdan Caprita27953142014-05-12 11:41:42 -0700187}
188
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700189func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
Bogdan Caprita27953142014-05-12 11:41:42 -0700190 panic("ResolveToMountTable not implemented")
191 return nil, nil
192}
193
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700194func (ns *namespace) Unresolve(ctx context.T, name string) ([]string, error) {
Bogdan Caprita27953142014-05-12 11:41:42 -0700195 panic("Unresolve not implemented")
196 return nil, nil
197}
198
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700199func (ns *namespace) Glob(ctx context.T, pattern string) (chan naming.MountEntry, error) {
Bogdan Caprita27953142014-05-12 11:41:42 -0700200 panic("Glob not implemented")
201 return nil, nil
202}
203
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700204func (ns *namespace) SetRoots([]string) error {
Cosmos Nicolaoue6e87f12014-06-03 14:29:10 -0700205 panic("SetRoots not implemented")
206 return nil
207}
208
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700209func startServer(t *testing.T, serverID security.PrivateID, sm stream.Manager, ns naming.Namespace, ts interface{}) ipc.Server {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700210 vlog.VI(1).Info("InternalNewServer")
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700211 server, err := InternalNewServer(InternalNewContext(), sm, ns, listenerID(serverID))
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700212 if err != nil {
213 t.Errorf("InternalNewServer failed: %v", err)
214 }
215 vlog.VI(1).Info("server.Register")
216 disp := testServerDisp{ts}
217 if err := server.Register("server", disp); err != nil {
218 t.Errorf("server.Register failed: %v", err)
219 }
220 vlog.VI(1).Info("server.Listen")
221 if _, err := server.Listen("tcp", "localhost:0"); err != nil {
222 t.Errorf("server.Listen failed: %v", err)
223 }
224 vlog.VI(1).Info("server.Publish")
225 if err := server.Publish("mountpoint"); err != nil {
226 t.Errorf("server.Publish failed: %v", err)
227 }
228 return server
229}
230
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700231func verifyMount(t *testing.T, ns naming.Namespace, name string) {
232 if _, err := ns.Resolve(InternalNewContext(), name); err != nil {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700233 t.Errorf("%s not found in mounttable", name)
234 }
235}
236
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700237func verifyMountMissing(t *testing.T, ns naming.Namespace, name string) {
238 if servers, err := ns.Resolve(InternalNewContext(), name); err == nil {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700239 t.Errorf("%s not supposed to be found in mounttable; got %d servers instead", name, len(servers))
240 }
241}
242
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700243func stopServer(t *testing.T, server ipc.Server, ns naming.Namespace) {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700244 vlog.VI(1).Info("server.Stop")
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700245 verifyMount(t, ns, "mountpoint/server")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700246
247 // Check that we can still publish.
248 server.Publish("should_appear_in_mt")
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700249 verifyMount(t, ns, "should_appear_in_mt/server")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700250
251 if err := server.Stop(); err != nil {
252 t.Errorf("server.Stop failed: %v", err)
253 }
254 // Check that we can no longer publish after Stop.
255 server.Publish("should_not_appear_in_mt")
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700256 verifyMountMissing(t, ns, "should_not_appear_in_mt/server")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700257
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700258 verifyMountMissing(t, ns, "mountpoint/server")
259 verifyMountMissing(t, ns, "should_appear_in_mt/server")
260 verifyMountMissing(t, ns, "should_not_appear_in_mt/server")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700261 vlog.VI(1).Info("server.Stop DONE")
262}
263
Bogdan Caprita27953142014-05-12 11:41:42 -0700264type bundle struct {
265 client ipc.Client
266 server ipc.Server
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700267 ns naming.Namespace
Bogdan Caprita27953142014-05-12 11:41:42 -0700268 sm stream.Manager
269}
270
271func (b bundle) cleanup(t *testing.T) {
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700272 stopServer(t, b.server, b.ns)
Bogdan Caprita27953142014-05-12 11:41:42 -0700273 b.client.Close()
274}
275
276func createBundle(t *testing.T, clientID, serverID security.PrivateID, ts interface{}) (b bundle) {
277 b.sm = imanager.InternalNew(naming.FixedRoutingID(0x555555555))
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700278 b.ns = newNamespace()
279 b.server = startServer(t, serverID, b.sm, b.ns, ts)
Bogdan Caprita27953142014-05-12 11:41:42 -0700280 var err error
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700281 b.client, err = InternalNewClient(b.sm, b.ns, veyron2.LocalID(clientID))
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700282 if err != nil {
283 t.Fatalf("InternalNewClient failed: %v", err)
284 }
Bogdan Caprita27953142014-05-12 11:41:42 -0700285 return
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700286}
287
Asim Shankarb54d7642014-06-05 13:08:04 -0700288func bless(blessor security.PrivateID, blessee security.PublicID, name string, caveats ...security.ServiceCaveat) security.PublicID {
289 blessed, err := blessor.Bless(blessee, name, 24*time.Hour, caveats)
290 if err != nil {
291 panic(err)
292 }
293 return blessed
294}
295
Bogdan Caprita783f7792014-05-15 09:29:17 -0700296func derive(blessor security.PrivateID, name string, caveats ...security.ServiceCaveat) security.PrivateID {
Asim Shankar70a28872014-05-13 14:49:20 -0700297 id, err := isecurity.NewPrivateID("irrelevant")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700298 if err != nil {
299 panic(err)
300 }
Asim Shankarb54d7642014-06-05 13:08:04 -0700301 derivedID, err := id.Derive(bless(blessor, id.PublicID(), name, caveats...))
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700302 if err != nil {
303 panic(err)
304 }
305 return derivedID
306}
307
308func matchesErrorPattern(err error, pattern string) bool {
309 if (len(pattern) == 0) != (err == nil) {
310 return false
311 }
312 return err == nil || strings.Index(err.Error(), pattern) >= 0
313}
314
315func TestStartCall(t *testing.T) {
Asim Shankarb54d7642014-06-05 13:08:04 -0700316 authorizeErr := "not authorized because"
317 nameErr := "does not match the provided pattern"
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700318
Asim Shankar45054a62014-05-15 10:32:54 -0700319 cavOnlyV1 := security.UniversalCaveat(caveat.PeerIdentity{"client/v1"})
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700320 now := time.Now()
321 cavExpired := security.ServiceCaveat{
322 Service: security.AllPrincipals,
Asim Shankar45054a62014-05-15 10:32:54 -0700323 Caveat: &caveat.Expiry{IssueTime: now, ExpiryTime: now},
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700324 }
325
Bogdan Caprita783f7792014-05-15 09:29:17 -0700326 clientV1ID := derive(clientID, "v1")
327 clientV2ID := derive(clientID, "v2")
328 serverV1ID := derive(serverID, "v1", cavOnlyV1)
329 serverExpiredID := derive(serverID, "expired", cavExpired)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700330
331 tests := []struct {
332 clientID, serverID security.PrivateID
333 pattern security.PrincipalPattern // pattern on the server identity expected by client.
334 err string
335 }{
336 // Client accepts talking to server only if server's identity matches the
337 // provided pattern.
338 {clientID, serverID, security.AllPrincipals, ""},
339 {clientID, serverID, "server", ""},
340 {clientID, serverID, "server/v1", ""},
341 {clientID, serverID, "anotherServer", nameErr},
342
343 // All clients reject talking to a server with an expired identity.
344 {clientID, serverExpiredID, security.AllPrincipals, authorizeErr},
345 {clientV1ID, serverExpiredID, security.AllPrincipals, authorizeErr},
346 {clientV2ID, serverExpiredID, security.AllPrincipals, authorizeErr},
347
348 // Only clientV1 accepts talking to serverV1.
349 {clientV1ID, serverV1ID, security.AllPrincipals, ""},
350 {clientV2ID, serverV1ID, security.AllPrincipals, authorizeErr},
351 }
352 // Servers and clients will be created per-test, use the same stream manager and mounttable.
353 mgr := imanager.InternalNew(naming.FixedRoutingID(0x1111111))
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700354 ns := newNamespace()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700355 for _, test := range tests {
356 name := fmt.Sprintf("(clientID:%q serverID:%q)", test.clientID, test.serverID)
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700357 server := startServer(t, test.serverID, mgr, ns, &testServer{})
358 client, err := InternalNewClient(mgr, ns, veyron2.LocalID(test.clientID))
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700359 if err != nil {
360 t.Errorf("%s: Client creation failed: %v", name, err)
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700361 stopServer(t, server, ns)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700362 continue
363 }
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -0700364 if _, err := client.StartCall(&fakeContext{}, "mountpoint/server/suffix", "irrelevant", nil, veyron2.RemoteID(test.pattern)); !matchesErrorPattern(err, test.err) {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700365 t.Errorf(`%s: client.StartCall: got error "%v", want to match "%v"`, name, err, test.err)
366 }
367 client.Close()
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700368 stopServer(t, server, ns)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700369 }
370}
371
372func TestRPC(t *testing.T) {
Tilak Sharma0c766112014-05-20 17:47:27 -0700373 testRPC(t, true)
374}
375
376// TestCloseSendOnFinish tests that Finish informs the server that no more
377// inputs will be sent by the client if CloseSend has not already done so.
378func TestRPCCloseSendOnFinish(t *testing.T) {
379 testRPC(t, false)
380}
381
382func testRPC(t *testing.T, shouldCloseSend bool) {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700383 type v []interface{}
384 type testcase struct {
385 name string
386 method string
387 args v
388 streamArgs v
389 startErr error
390 results v
391 finishErr error
392 }
393 tests := []testcase{
394 {"mountpoint/server/suffix", "Closure", nil, nil, nil, nil, nil},
395 {"mountpoint/server/suffix", "Error", nil, nil, nil, v{errMethod}, nil},
396
397 {"mountpoint/server/suffix", "Echo", v{"foo"}, nil, nil, v{`method:"Echo",suffix:"suffix",arg:"foo"`}, nil},
398 {"mountpoint/server/suffix/abc", "Echo", v{"bar"}, nil, nil, v{`method:"Echo",suffix:"suffix/abc",arg:"bar"`}, nil},
399
400 {"mountpoint/server/suffix", "EchoUser", v{"foo", userType("bar")}, nil, nil, v{`method:"EchoUser",suffix:"suffix",arg:"foo"`, userType("bar")}, nil},
401 {"mountpoint/server/suffix/abc", "EchoUser", v{"baz", userType("bla")}, nil, nil, v{`method:"EchoUser",suffix:"suffix/abc",arg:"baz"`, userType("bla")}, nil},
402 {"mountpoint/server/suffix", "Stream", v{"foo"}, v{userType("bar"), userType("baz")}, nil, v{`method:"Stream",suffix:"suffix",arg:"foo" bar baz`, nil}, nil},
403 {"mountpoint/server/suffix/abc", "Stream", v{"123"}, v{userType("456"), userType("789")}, nil, v{`method:"Stream",suffix:"suffix/abc",arg:"123" 456 789`, nil}, nil},
404 {"mountpoint/server/suffix", "EchoIDs", nil, nil, nil, v{"server", "client"}, nil},
405 {"mountpoint/server/suffix", "EchoAndError", v{"bugs bunny"}, nil, nil, v{`method:"EchoAndError",suffix:"suffix",arg:"bugs bunny"`, nil}, nil},
406 {"mountpoint/server/suffix", "EchoAndError", v{"error"}, nil, nil, v{`method:"EchoAndError",suffix:"suffix",arg:"error"`, errMethod}, nil},
407 }
408 name := func(t testcase) string {
409 return fmt.Sprintf("%s.%s(%v)", t.name, t.method, t.args)
410 }
Bogdan Caprita27953142014-05-12 11:41:42 -0700411 b := createBundle(t, clientID, serverID, &testServer{})
412 defer b.cleanup(t)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700413 for _, test := range tests {
414 vlog.VI(1).Infof("%s client.StartCall", name(test))
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -0700415 call, err := b.client.StartCall(&fakeContext{}, test.name, test.method, test.args)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700416 if err != test.startErr {
417 t.Errorf(`%s client.StartCall got error "%v", want "%v"`, name(test), err, test.startErr)
418 continue
419 }
420 for _, sarg := range test.streamArgs {
421 vlog.VI(1).Infof("%s client.Send(%v)", name(test), sarg)
422 if err := call.Send(sarg); err != nil {
423 t.Errorf(`%s call.Send(%v) got unexpected error "%v"`, name(test), sarg, err)
424 }
425 var u userType
426 if err := call.Recv(&u); err != nil {
427 t.Errorf(`%s call.Recv(%v) got unexpected error "%v"`, name(test), sarg, err)
428 }
429 if !reflect.DeepEqual(u, sarg) {
430 t.Errorf("%s call.Recv got value %v, want %v", name(test), u, sarg)
431 }
432 }
Tilak Sharma0c766112014-05-20 17:47:27 -0700433 if shouldCloseSend {
434 vlog.VI(1).Infof("%s call.CloseSend", name(test))
435 if err := call.CloseSend(); err != nil {
436 t.Errorf(`%s call.CloseSend got unexpected error "%v"`, name(test), err)
437 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700438 }
439 vlog.VI(1).Infof("%s client.Finish", name(test))
440 results := makeResultPtrs(test.results)
441 err = call.Finish(results...)
442 if err != test.finishErr {
443 t.Errorf(`%s call.Finish got error "%v", want "%v"`, name(test), err, test.finishErr)
444 }
445 checkResultPtrs(t, name(test), results, test.results)
446 }
447}
448
Asim Shankarb54d7642014-06-05 13:08:04 -0700449// granter implements ipc.Granter, returning a fixed (security.PublicID, error) pair.
450type granter struct {
451 ipc.CallOpt
452 id security.PublicID
453 err error
454}
455
456func (g granter) Grant(id security.PublicID) (security.PublicID, error) { return g.id, g.err }
457
458func TestBlessing(t *testing.T) {
459 b := createBundle(t, clientID, serverID, &testServer{})
460 defer b.cleanup(t)
461
462 tests := []struct {
463 granter ipc.CallOpt
464 blessing, starterr, finisherr string
465 }{
466 {blessing: "<nil>"},
467 {granter: granter{id: bless(clientID, serverID.PublicID(), "blessed")}, blessing: "client/blessed"},
468 {granter: granter{err: errors.New("hell no")}, starterr: "hell no"},
469 {granter: granter{id: clientID.PublicID()}, finisherr: "blessing provided not bound to this server"},
470 }
471 for _, test := range tests {
472 call, err := b.client.StartCall(&fakeContext{}, "mountpoint/server/suffix", "EchoBlessing", []interface{}{"argument"}, test.granter)
473 if !matchesErrorPattern(err, test.starterr) {
474 t.Errorf("%+v: StartCall returned error %v", test, err)
475 }
476 if err != nil {
477 continue
478 }
479 var result, blessing string
480 if err = call.Finish(&result, &blessing); !matchesErrorPattern(err, test.finisherr) {
481 t.Errorf("%+v: Finish returned error %v", test, err)
482 }
483 if err != nil {
484 continue
485 }
486 if result != "argument" || blessing != test.blessing {
487 t.Errorf("%+v: Got (%q, %q)", test, result, blessing)
488 }
489 }
490}
491
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700492func TestRPCAuthorization(t *testing.T) {
493 cavOnlyEcho := security.ServiceCaveat{
494 Service: security.AllPrincipals,
Asim Shankar45054a62014-05-15 10:32:54 -0700495 Caveat: caveat.MethodRestriction{"Echo"},
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700496 }
497 now := time.Now()
498 cavExpired := security.ServiceCaveat{
499 Service: security.AllPrincipals,
Asim Shankar45054a62014-05-15 10:32:54 -0700500 Caveat: &caveat.Expiry{IssueTime: now, ExpiryTime: now},
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700501 }
502
Bogdan Caprita783f7792014-05-15 09:29:17 -0700503 blessedByServerOnlyEcho := derive(serverID, "onlyEcho", cavOnlyEcho)
504 blessedByServerExpired := derive(serverID, "expired", cavExpired)
505 blessedByClient := derive(clientID, "blessed")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700506
507 const (
508 expiredIDErr = "forbids credential from being used at this time"
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700509 aclAuthErr = "no matching ACL entry found"
510 )
511 invalidMethodErr := func(method string) string {
Asim Shankar45054a62014-05-15 10:32:54 -0700512 return fmt.Sprintf(`caveat.MethodRestriction{"Echo"} forbids invocation of method %s`, method)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700513 }
514
515 type v []interface{}
516 type testcase struct {
517 clientID security.PrivateID
518 name string
519 method string
520 args v
521 results v
522 finishErr string
523 }
524 tests := []testcase{
525 // Clients whose identities have invalid caveats are not by authorized by any authorizer.
526 {blessedByServerExpired, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{""}, expiredIDErr},
527 {blessedByServerExpired, "mountpoint/server/suffix", "Echo", v{"foo"}, v{""}, expiredIDErr},
528 {blessedByServerOnlyEcho, "mountpoint/server/nilAuth", "Closure", nil, nil, invalidMethodErr("Closure")},
529 {blessedByServerOnlyEcho, "mountpoint/server/suffix", "Closure", nil, nil, invalidMethodErr("Closure")},
530 // Only clients with a trusted name that matches either the server's identity or an identity blessed
531 // by the server are authorized by the (default) nilAuth authorizer.
Ankur992269a2014-05-13 13:03:24 -0700532 {clientID, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{""}, aclAuthErr},
533 {blessedByClient, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{""}, aclAuthErr},
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700534 {serverID, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{`method:"Echo",suffix:"nilAuth",arg:"foo"`}, ""},
535 {serverID, "mountpoint/server/nilAuth", "Closure", nil, nil, ""},
536 {blessedByServerOnlyEcho, "mountpoint/server/nilAuth", "Echo", v{"foo"}, v{`method:"Echo",suffix:"nilAuth",arg:"foo"`}, ""},
537 // Only clients matching the server's ACL are authorized.
538 {clientID, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{`method:"Echo",suffix:"aclAuth",arg:"foo"`}, ""},
539 {blessedByClient, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{""}, aclAuthErr},
540 {serverID, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{`method:"Echo",suffix:"aclAuth",arg:"foo"`}, ""},
541 {blessedByServerOnlyEcho, "mountpoint/server/aclAuth", "Echo", v{"foo"}, v{`method:"Echo",suffix:"aclAuth",arg:"foo"`}, ""},
542 {clientID, "mountpoint/server/aclAuth", "Closure", nil, nil, ""},
543 {blessedByClient, "mountpoint/server/aclAuth", "Closure", nil, nil, aclAuthErr},
544 {serverID, "mountpoint/server/aclAuth", "Closure", nil, nil, ""},
545
546 // All methods except "Unauthorized" are authorized by the custom authorizer.
547 {clientID, "mountpoint/server/suffix", "Echo", v{"foo"}, v{`method:"Echo",suffix:"suffix",arg:"foo"`}, ""},
548 {blessedByClient, "mountpoint/server/suffix", "Echo", v{"foo"}, v{`method:"Echo",suffix:"suffix",arg:"foo"`}, ""},
549 {serverID, "mountpoint/server/suffix", "Echo", v{"foo"}, v{`method:"Echo",suffix:"suffix",arg:"foo"`}, ""},
550 {blessedByServerOnlyEcho, "mountpoint/server/suffix", "Echo", v{"foo"}, v{`method:"Echo",suffix:"suffix",arg:"foo"`}, ""},
551 {clientID, "mountpoint/server/suffix", "Closure", nil, nil, ""},
552 {blessedByClient, "mountpoint/server/suffix", "Closure", nil, nil, ""},
553 {serverID, "mountpoint/server/suffix", "Closure", nil, nil, ""},
554 {clientID, "mountpoint/server/suffix", "Unauthorized", nil, v{""}, "application Authorizer denied access"},
555 {blessedByClient, "mountpoint/server/suffix", "Unauthorized", nil, v{""}, "application Authorizer denied access"},
556 {serverID, "mountpoint/server/suffix", "Unauthorized", nil, v{""}, "application Authorizer denied access"},
557 }
558 name := func(t testcase) string {
559 return fmt.Sprintf("%q RPCing %s.%s(%v)", t.clientID.PublicID(), t.name, t.method, t.args)
560 }
561
Bogdan Caprita27953142014-05-12 11:41:42 -0700562 b := createBundle(t, nil, serverID, &testServer{})
563 defer b.cleanup(t)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700564 for _, test := range tests {
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700565 client, err := InternalNewClient(b.sm, b.ns, veyron2.LocalID(test.clientID))
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700566 if err != nil {
567 t.Fatalf("InternalNewClient failed: %v", err)
568 }
569 defer client.Close()
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -0700570 call, err := client.StartCall(&fakeContext{}, test.name, test.method, test.args)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700571 if err != nil {
572 t.Errorf(`%s client.StartCall got unexpected error: "%v"`, name(test), err)
573 continue
574 }
575 if err := call.CloseSend(); err != nil {
576 t.Errorf(`%s call.CloseSend got unexpected error: "%v"`, name(test), err)
577 }
578 results := makeResultPtrs(test.results)
579 err = call.Finish(results...)
580 if !matchesErrorPattern(err, test.finishErr) {
581 t.Errorf(`%s call.Finish got error: "%v", want to match: "%v"`, name(test), err, test.finishErr)
582 }
583 }
584}
585
586type cancelTestServer struct {
587 started chan struct{}
588 cancelled chan struct{}
589}
590
591func newCancelTestServer() *cancelTestServer {
592 return &cancelTestServer{
593 started: make(chan struct{}),
594 cancelled: make(chan struct{}),
595 }
596}
597
598func (s *cancelTestServer) CancelStreamReader(call ipc.ServerCall) error {
599 close(s.started)
600 for {
601 var b []byte
602 if err := call.Recv(&b); err != nil && err != io.EOF {
603 return err
604 }
605 if call.IsClosed() {
606 close(s.cancelled)
607 return nil
608 }
609 }
610}
611
612// CancelStreamIgnorer doesn't read from it's input stream so all it's
613// buffers fill. The intention is to show that call.IsClosed is updated
614// even when the stream is stalled.
615func (s *cancelTestServer) CancelStreamIgnorer(call ipc.ServerCall) error {
616 close(s.started)
617 for {
618 time.Sleep(time.Millisecond)
619 if call.IsClosed() {
620 close(s.cancelled)
621 return nil
622 }
623 }
624}
625
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -0700626func waitForCancel(t *testing.T, ts *cancelTestServer, call ipc.Call) {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700627 <-ts.started
628 call.Cancel()
629 <-ts.cancelled
630}
631
632// TestCancel tests cancellation while the server is reading from a stream.
633func TestCancel(t *testing.T) {
634 ts := newCancelTestServer()
Bogdan Caprita27953142014-05-12 11:41:42 -0700635 b := createBundle(t, clientID, serverID, ts)
636 defer b.cleanup(t)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700637
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -0700638 call, err := b.client.StartCall(&fakeContext{}, "mountpoint/server/suffix", "CancelStreamReader", []interface{}{})
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700639 if err != nil {
640 t.Fatalf("Start call failed: %v", err)
641 }
642 for i := 0; i <= 10; i++ {
643 b := []byte{1, 2, 3}
644 if err := call.Send(b); err != nil {
645 t.Errorf("clientCall.Send error %q", err)
646 }
647 }
648 waitForCancel(t, ts, call)
649}
650
651// TestCancelWithFullBuffers tests that even if the writer has filled the buffers and
652// the server is not reading that the cancel message gets through.
653func TestCancelWithFullBuffers(t *testing.T) {
654 ts := newCancelTestServer()
Bogdan Caprita27953142014-05-12 11:41:42 -0700655 b := createBundle(t, clientID, serverID, ts)
656 defer b.cleanup(t)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700657
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -0700658 call, err := b.client.StartCall(&fakeContext{}, "mountpoint/server/suffix", "CancelStreamIgnorer", []interface{}{})
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700659 if err != nil {
660 t.Fatalf("Start call failed: %v", err)
661 }
662 // Fill up all the write buffers to ensure that cancelling works even when the stream
663 // is blocked.
664 call.Send(make([]byte, vc.MaxSharedBytes))
665 call.Send(make([]byte, vc.DefaultBytesBufferedPerFlow))
666
667 waitForCancel(t, ts, call)
668}
669
670type streamRecvInGoroutineServer struct{ c chan error }
671
672func (s *streamRecvInGoroutineServer) RecvInGoroutine(call ipc.ServerCall) error {
673 // Spawn a goroutine to read streaming data from the client.
674 go func() {
675 var i interface{}
676 for {
677 err := call.Recv(&i)
678 if err != nil {
679 s.c <- err
680 return
681 }
682 }
683 }()
684 // Imagine the server did some processing here and now that it is done,
685 // it does not care to see what else the client has to say.
686 return nil
687}
688
689func TestStreamReadTerminatedByServer(t *testing.T) {
690 s := &streamRecvInGoroutineServer{c: make(chan error, 1)}
Bogdan Caprita27953142014-05-12 11:41:42 -0700691 b := createBundle(t, clientID, serverID, s)
692 defer b.cleanup(t)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700693
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -0700694 call, err := b.client.StartCall(&fakeContext{}, "mountpoint/server/suffix", "RecvInGoroutine", []interface{}{})
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700695 if err != nil {
696 t.Fatalf("StartCall failed: %v", err)
697 }
698
699 c := make(chan error, 1)
700 go func() {
701 for i := 0; true; i++ {
702 if err := call.Send(i); err != nil {
703 c <- err
704 return
705 }
706 }
707 }()
708
709 // The goroutine at the server executing "Recv" should have terminated
710 // with EOF.
711 if err := <-s.c; err != io.EOF {
712 t.Errorf("Got %v at server, want io.EOF", err)
713 }
714 // The client Send should have failed since the RPC has been
715 // terminated.
716 if err := <-c; err == nil {
717 t.Errorf("Client Send should fail as the server should have closed the flow")
718 }
719}
720
721// TestConnectWithIncompatibleServers tests that clients ignore incompatible endpoints.
722func TestConnectWithIncompatibleServers(t *testing.T) {
Bogdan Caprita27953142014-05-12 11:41:42 -0700723 b := createBundle(t, clientID, serverID, &testServer{})
724 defer b.cleanup(t)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700725
726 // Publish some incompatible endpoints.
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700727 publisher := publisher.New(InternalNewContext(), b.ns, publishPeriod)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700728 defer publisher.WaitForStop()
729 defer publisher.Stop()
730 publisher.AddName("incompatible")
731 publisher.AddServer("/@2@tcp@localhost:10000@@1000000@2000000@@")
732 publisher.AddServer("/@2@tcp@localhost:10001@@2000000@3000000@@")
733
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -0700734 _, err := b.client.StartCall(&fakeContext{}, "incompatible/server/suffix", "Echo", []interface{}{"foo"})
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700735 if !strings.Contains(err.Error(), version.NoCompatibleVersionErr.Error()) {
736 t.Errorf("Expected error %v, found: %v", version.NoCompatibleVersionErr, err)
737 }
738
739 // Now add a server with a compatible endpoint and try again.
Bogdan Caprita27953142014-05-12 11:41:42 -0700740 b.server.Publish("incompatible")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700741
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -0700742 call, err := b.client.StartCall(&fakeContext{}, "incompatible/server/suffix", "Echo", []interface{}{"foo"})
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700743 if err != nil {
Asim Shankar3a8a7e22014-05-12 18:01:44 -0700744 t.Fatal(err)
745 }
746 var result string
747 if err = call.Finish(&result); err != nil {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700748 t.Errorf("Unexpected error finishing call %v", err)
749 }
Asim Shankar3a8a7e22014-05-12 18:01:44 -0700750 expected := `method:"Echo",suffix:"suffix",arg:"foo"`
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700751 if result != expected {
752 t.Errorf("Wrong result returned. Got %s, wanted %s", result, expected)
753 }
754}
755
Bogdan Caprita187269b2014-05-13 19:59:46 -0700756// TestPublishOptions verifies that the options that are relevant to how
757// a server publishes its endpoints have the right effect.
758func TestPublishOptions(t *testing.T) {
759 sm := imanager.InternalNew(naming.FixedRoutingID(0x555555555))
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700760 ns := newNamespace()
Bogdan Caprita187269b2014-05-13 19:59:46 -0700761 cases := []struct {
762 opts []ipc.ServerOpt
763 expect []string
764 }{
765 {[]ipc.ServerOpt{}, []string{"127.0.0.1", "127.0.0.1"}},
766 {[]ipc.ServerOpt{veyron2.PublishAll}, []string{"127.0.0.1", "127.0.0.1"}},
767 {[]ipc.ServerOpt{veyron2.PublishFirst}, []string{"127.0.0.1"}},
Bogdan Caprita502de9d2014-05-14 18:48:06 -0700768 {[]ipc.ServerOpt{veyron2.EndpointRewriteOpt("example1.com"), veyron2.EndpointRewriteOpt("example2.com")}, []string{"example2.com", "example2.com"}},
Bogdan Caprita187269b2014-05-13 19:59:46 -0700769 {[]ipc.ServerOpt{veyron2.PublishFirst, veyron2.EndpointRewriteOpt("example.com")}, []string{"example.com"}},
770 }
771 for i, c := range cases {
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700772 server, err := InternalNewServer(InternalNewContext(), sm, ns, append([]ipc.ServerOpt{listenerID(serverID)}, c.opts...)...)
Bogdan Caprita187269b2014-05-13 19:59:46 -0700773 if err != nil {
774 t.Errorf("InternalNewServer failed: %v", err)
775 continue
776 }
777 if _, err := server.Listen("tcp", "localhost:0"); err != nil {
778 t.Errorf("server.Listen failed: %v", err)
779 server.Stop()
780 continue
781 }
782 if _, err := server.Listen("tcp", "localhost:0"); err != nil {
783 t.Errorf("server.Listen failed: %v", err)
784 server.Stop()
785 continue
786 }
787 if err := server.Publish("mountpoint"); err != nil {
788 t.Errorf("server.Publish failed: %v", err)
789 server.Stop()
790 continue
791 }
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700792 servers, err := ns.Resolve(InternalNewContext(), "mountpoint")
Bogdan Caprita187269b2014-05-13 19:59:46 -0700793 if err != nil {
794 t.Errorf("mountpoint not found in mounttable")
795 server.Stop()
796 continue
797 }
798 var got []string
799 for _, s := range servers {
800 address, _ := naming.SplitAddressName(s)
801 ep, err := inaming.NewEndpoint(address)
802 if err != nil {
803 t.Errorf("case #%d: server with invalid endpoint %q: %v", i, address, err)
804 continue
805 }
806 host, _, err := net.SplitHostPort(ep.Addr().String())
807 if err != nil {
808 t.Errorf("case #%d: server endpoint with invalid address %q: %v", i, ep.Addr(), err)
809 continue
810 }
811 got = append(got, host)
812 }
813 if want := c.expect; !reflect.DeepEqual(want, got) {
814 t.Errorf("case #%d: expected mounted servers with addresses %q, got %q instead", i, want, got)
815 }
816 server.Stop()
817 }
818}
819
Bogdan Caprita783f7792014-05-15 09:29:17 -0700820// TestReconnect verifies that the client transparently re-establishes the
821// connection to the server if the server dies and comes back (on the same
822// endpoint).
823func TestReconnect(t *testing.T) {
824 b := createBundle(t, clientID, nil, nil) // We only need the client from the bundle.
825 defer b.cleanup(t)
826 idFile := testutil.SaveIdentityToFile(derive(clientID, "server"))
827 server := blackbox.HelperCommand(t, "runServer", "127.0.0.1:0", idFile)
828 server.Cmd.Start()
829 addr, err := server.ReadLineFromChild()
830 if err != nil {
831 t.Fatalf("Failed to read server address from process: %v", err)
832 }
833 ep, err := inaming.NewEndpoint(addr)
834 if err != nil {
835 t.Fatalf("inaming.NewEndpoint(%q): %v", addr, err)
836 }
837 serverName := naming.JoinAddressName(ep.String(), "server/suffix")
838 makeCall := func() (string, error) {
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -0700839 call, err := b.client.StartCall(&fakeContext{}, serverName, "Echo", []interface{}{"bratman"})
Bogdan Caprita783f7792014-05-15 09:29:17 -0700840 if err != nil {
841 return "", err
842 }
843 var result string
844 if err = call.Finish(&result); err != nil {
845 return "", err
846 }
847 return result, nil
848 }
849 expected := `method:"Echo",suffix:"suffix",arg:"bratman"`
850 if result, err := makeCall(); err != nil || result != expected {
851 t.Errorf("Got (%q, %v) want (%q, nil)", result, err, expected)
852 }
853 // Kill the server, verify client can't talk to it anymore.
854 server.Cleanup()
855 if _, err := makeCall(); err == nil {
856 t.Fatal("Expected call to fail since server is dead")
857 }
858 // Resurrect the server with the same address, verify client
859 // re-establishes the connection.
860 server = blackbox.HelperCommand(t, "runServer", addr, idFile)
861 defer server.Cleanup()
862 server.Cmd.Start()
863 if addr2, err := server.ReadLineFromChild(); addr2 != addr || err != nil {
864 t.Fatalf("Got (%q, %v) want (%q, nil)", addr2, err, addr)
865 }
866 if result, err := makeCall(); err != nil || result != expected {
867 t.Errorf("Got (%q, %v) want (%q, nil)", result, err, expected)
868 }
869}
870
Asim Shankar0ea02ab2014-06-09 11:39:24 -0700871type proxyHandle struct {
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700872 ns naming.Namespace
Asim Shankar0ea02ab2014-06-09 11:39:24 -0700873 process *blackbox.Child
874 mount string
875}
876
877func (h *proxyHandle) Start(t *testing.T) error {
878 h.process = blackbox.HelperCommand(t, "runProxy")
879 h.process.Cmd.Start()
880 var err error
881 if h.mount, err = h.process.ReadLineFromChild(); err != nil {
882 return err
883 }
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700884 if err := h.ns.Mount(&fakeContext{}, "proxy", h.mount, time.Hour); err != nil {
Asim Shankar0ea02ab2014-06-09 11:39:24 -0700885 return err
886 }
887 return nil
888}
889
890func (h *proxyHandle) Stop() error {
891 if h.process == nil {
892 return nil
893 }
894 h.process.Cleanup()
895 h.process = nil
896 if len(h.mount) == 0 {
897 return nil
898 }
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700899 return h.ns.Unmount(&fakeContext{}, "proxy", h.mount)
Asim Shankar0ea02ab2014-06-09 11:39:24 -0700900}
901
902func TestProxy(t *testing.T) {
903 sm := imanager.InternalNew(naming.FixedRoutingID(0x555555555))
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700904 ns := newNamespace()
905 client, err := InternalNewClient(sm, ns, veyron2.LocalID(clientID))
Asim Shankar0ea02ab2014-06-09 11:39:24 -0700906 if err != nil {
907 t.Fatal(err)
908 }
909 defer client.Close()
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700910 server, err := InternalNewServer(InternalNewContext(), sm, ns, listenerID(serverID))
Asim Shankar0ea02ab2014-06-09 11:39:24 -0700911 if err != nil {
912 t.Fatal(err)
913 }
914 defer server.Stop()
915 if err := server.Register("server", testServerDisp{&testServer{}}); err != nil {
916 t.Fatal(err)
917 }
918
919 name := "mountpoint/server/suffix"
920 makeCall := func() (string, error) {
921 call, err := client.StartCall(&fakeContext{}, name, "Echo", []interface{}{"batman"})
922 if err != nil {
923 return "", err
924 }
925 var result string
926 if err = call.Finish(&result); err != nil {
927 return "", err
928 }
929 return result, nil
930 }
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700931 proxy := &proxyHandle{ns: ns}
Asim Shankar0ea02ab2014-06-09 11:39:24 -0700932 if err := proxy.Start(t); err != nil {
933 t.Fatal(err)
934 }
935 defer proxy.Stop()
936 if _, err := server.Listen(inaming.Network, "proxy"); err != nil {
937 t.Fatal(err)
938 }
939 if err := server.Publish("mountpoint"); err != nil {
940 t.Fatal(err)
941 }
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700942 verifyMount(t, ns, name)
Asim Shankar0ea02ab2014-06-09 11:39:24 -0700943 // Proxied endpoint should be published and RPC should succeed (through proxy)
944 const expected = `method:"Echo",suffix:"suffix",arg:"batman"`
945 if result, err := makeCall(); result != expected || err != nil {
946 t.Fatalf("Got (%v, %v) want (%v, nil)", result, err, expected)
947 }
948
949 // Proxy dies, calls should fail and the name should be unmounted.
950 if err := proxy.Stop(); err != nil {
951 t.Fatal(err)
952 }
953 if result, err := makeCall(); err == nil {
954 t.Fatalf(`Got (%v, %v) want ("", <non-nil>) as proxy is down`, result, err)
955 }
956 for {
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700957 if _, err := ns.Resolve(InternalNewContext(), name); err != nil {
Asim Shankar0ea02ab2014-06-09 11:39:24 -0700958 break
959 }
960 }
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700961 verifyMountMissing(t, ns, name)
Asim Shankar0ea02ab2014-06-09 11:39:24 -0700962
963 // Proxy restarts, calls should eventually start succeeding.
964 if err := proxy.Start(t); err != nil {
965 t.Fatal(err)
966 }
967 for {
968 if result, err := makeCall(); err == nil {
969 if result != expected {
970 t.Errorf("Got (%v, %v) want (%v, nil)", result, err, expected)
971 }
972 break
973 }
974 }
975}
976
Bogdan Caprita783f7792014-05-15 09:29:17 -0700977func loadIdentityFromFile(file string) security.PrivateID {
978 f, err := os.Open(file)
979 if err != nil {
980 vlog.Fatalf("failed to open %v: %v", file, err)
981 }
982 id, err := security.LoadIdentity(f)
983 f.Close()
984 if err != nil {
985 vlog.Fatalf("Failed to load identity from %v: %v", file, err)
986 }
987 return id
988}
989
990func runServer(argv []string) {
991 mgr := imanager.InternalNew(naming.FixedRoutingID(0x1111111))
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700992 ns := newNamespace()
Bogdan Caprita783f7792014-05-15 09:29:17 -0700993 id := loadIdentityFromFile(argv[1])
994 isecurity.TrustIdentityProviders(id)
Cosmos Nicolaou4e029972014-06-13 14:53:08 -0700995 server, err := InternalNewServer(InternalNewContext(), mgr, ns, listenerID(id))
Bogdan Caprita783f7792014-05-15 09:29:17 -0700996 if err != nil {
997 vlog.Fatalf("InternalNewServer failed: %v", err)
998 }
999 disp := testServerDisp{new(testServer)}
1000 if err := server.Register("server", disp); err != nil {
1001 vlog.Fatalf("server.Register failed: %v", err)
1002 }
1003 ep, err := server.Listen("tcp", argv[0])
1004 if err != nil {
1005 vlog.Fatalf("server.Listen failed: %v", err)
1006 }
1007 fmt.Println(ep.Addr())
1008 // Live forever (parent process should explicitly kill us).
1009 <-make(chan struct{})
1010}
1011
Asim Shankar0ea02ab2014-06-09 11:39:24 -07001012func runProxy([]string) {
1013 rid, err := naming.NewRoutingID()
1014 if err != nil {
1015 vlog.Fatal(err)
1016 }
Robin Thellend020cd222014-06-10 14:29:28 -07001017 proxy, err := proxy.New(rid, nil, "tcp4", "127.0.0.1:0", "")
Asim Shankar0ea02ab2014-06-09 11:39:24 -07001018 if err != nil {
1019 vlog.Fatal(err)
1020 }
1021 fmt.Println("/" + proxy.Endpoint().String())
1022 <-make(chan struct{})
1023}
1024
Bogdan Caprita783f7792014-05-15 09:29:17 -07001025// Required by blackbox framework.
1026func TestHelperProcess(t *testing.T) {
1027 blackbox.HelperProcess(t)
1028}
1029
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001030func init() {
1031 var err error
Asim Shankar70a28872014-05-13 14:49:20 -07001032 if clientID, err = isecurity.NewPrivateID("client"); err != nil {
Bogdan Caprita783f7792014-05-15 09:29:17 -07001033 vlog.Fatalf("failed isecurity.NewPrivateID: %s", err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001034 }
Asim Shankar70a28872014-05-13 14:49:20 -07001035 if serverID, err = isecurity.NewPrivateID("server"); err != nil {
Bogdan Caprita783f7792014-05-15 09:29:17 -07001036 vlog.Fatalf("failed isecurity.NewPrivateID: %s", err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001037 }
1038 isecurity.TrustIdentityProviders(clientID)
1039 isecurity.TrustIdentityProviders(serverID)
Bogdan Caprita783f7792014-05-15 09:29:17 -07001040
1041 blackbox.CommandTable["runServer"] = runServer
Asim Shankar0ea02ab2014-06-09 11:39:24 -07001042 blackbox.CommandTable["runProxy"] = runProxy
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001043}