blob: 6ee62ae9afec3ae201c36f8131d148de694a67ab [file] [log] [blame]
Robin Thellendb9dd9bb2014-10-29 13:54:08 -07001package impl
2
3import (
4 "fmt"
5 "io"
6
Matt Rosencrantz5180d162014-12-03 13:48:40 -08007 "veyron.io/veyron/veyron2"
Robin Thellendb9dd9bb2014-10-29 13:54:08 -07008 "veyron.io/veyron/veyron2/ipc"
Robin Thellend39ac3232014-12-02 09:50:41 -08009 "veyron.io/veyron/veyron2/naming"
Asim Shankar68885192014-11-26 12:48:35 -080010 "veyron.io/veyron/veyron2/services/security/access"
Todd Wang67fc8d72014-12-03 14:36:33 -080011 "veyron.io/veyron/veyron2/vdl/vdlroot/src/signature"
Robin Thellendb9dd9bb2014-10-29 13:54:08 -070012)
13
14// proxyInvoker is an ipc.Invoker implementation that proxies all requests
15// to a remote object, i.e. requests to <suffix> are forwarded to
16// <remote> transparently.
17//
18// remote is the name of the remote object.
Asim Shankar68885192014-11-26 12:48:35 -080019// access is the access tag require to access the object.
Robin Thellendb9dd9bb2014-10-29 13:54:08 -070020// sigStub is used to get the signature of the remote object.
21type proxyInvoker struct {
22 remote string
Asim Shankar68885192014-11-26 12:48:35 -080023 access access.Tag
Robin Thellendb9dd9bb2014-10-29 13:54:08 -070024 sigStub signatureStub
25}
26
Todd Wang5739dda2014-11-16 22:44:02 -080027var _ ipc.Invoker = (*proxyInvoker)(nil)
28
Robin Thellendb9dd9bb2014-10-29 13:54:08 -070029type signatureStub interface {
Todd Wang1fe7cdd2014-11-12 12:51:49 -080030 Signature(ipc.ServerContext) (ipc.ServiceSignature, error)
Robin Thellendb9dd9bb2014-10-29 13:54:08 -070031}
32
Asim Shankar214f89c2014-11-03 16:35:47 -080033func (p *proxyInvoker) Prepare(method string, numArgs int) (argptrs, tags []interface{}, err error) {
Robin Thellendb9dd9bb2014-10-29 13:54:08 -070034 argptrs = make([]interface{}, numArgs)
35 for i, _ := range argptrs {
36 var x interface{}
37 argptrs[i] = &x
38 }
Asim Shankar68885192014-11-26 12:48:35 -080039 tags = []interface{}{p.access}
Robin Thellendb9dd9bb2014-10-29 13:54:08 -070040 return
41}
42
43func (p *proxyInvoker) Invoke(method string, inCall ipc.ServerCall, argptrs []interface{}) (results []interface{}, err error) {
44 // We accept any values as argument and pass them through to the remote
45 // server.
46 args := make([]interface{}, len(argptrs))
47 for i, ap := range argptrs {
48 args[i] = ap
49 }
Matt Rosencrantz5180d162014-12-03 13:48:40 -080050 runtime := veyron2.RuntimeFromContext(inCall)
51 outCall, err := runtime.Client().StartCall(inCall, p.remote, method, args)
Robin Thellendb9dd9bb2014-10-29 13:54:08 -070052 if err != nil {
53 return nil, err
54 }
55
56 // Each RPC has a bi-directional stream, and there is no way to know in
57 // advance how much data will be sent in either direction, if any.
58 //
59 // This method (Invoke) must return when the remote server is done with
60 // the RPC, which is when outCall.Recv() returns EOF. When that happens,
61 // we need to call outCall.Finish() to get the return values, and then
62 // return these values to the client.
63 //
64 // While we are forwarding data from the server to the client, we must
65 // also forward data from the client to the server. This happens in a
66 // separate goroutine. This goroutine may return after Invoke has
67 // returned if the client doesn't call CloseSend() explicitly.
68 //
69 // Any error, other than EOF, will be returned to the client, if
70 // possible. The only situation where it is not possible to send an
71 // error to the client is when the error comes from forwarding data from
72 // the client to the server and Invoke has already returned or is about
73 // to return. In this case, the error is lost. So, it is possible that
74 // the client could successfully Send() data that the server doesn't
75 // actually receive if the server terminates the RPC while the data is
76 // in the proxy.
77 fwd := func(src, dst ipc.Stream, errors chan<- error) {
78 for {
79 var obj interface{}
80 switch err := src.Recv(&obj); err {
81 case io.EOF:
82 if call, ok := src.(ipc.Call); ok {
83 if err := call.CloseSend(); err != nil {
84 errors <- err
85 }
86 }
87 return
88 case nil:
89 break
90 default:
91 errors <- err
92 return
93 }
94 if err := dst.Send(obj); err != nil {
95 errors <- err
96 return
97 }
98 }
99 }
100 errors := make(chan error, 2)
101 go fwd(inCall, outCall, errors)
102 fwd(outCall, inCall, errors)
103 select {
104 case err := <-errors:
105 return nil, err
106 default:
107 }
108
109 nResults, err := p.numResults(method)
110 if err != nil {
111 return nil, err
112 }
113
114 // We accept any return values, without type checking, and return them
115 // to the client.
116 res := make([]interface{}, nResults)
117 for i := 0; i < len(res); i++ {
118 var foo interface{}
119 res[i] = &foo
120 }
121 err = outCall.Finish(res...)
122 results = make([]interface{}, len(res))
123 for i, r := range res {
124 results[i] = *r.(*interface{})
125 }
126 return results, err
127}
128
Todd Wang5739dda2014-11-16 22:44:02 -0800129// TODO(toddw): Expose a helper function that performs all error checking based
130// on reflection, to simplify the repeated logic processing results.
Todd Wang67fc8d72014-12-03 14:36:33 -0800131func (p *proxyInvoker) Signature(ctx ipc.ServerContext) ([]signature.Interface, error) {
Todd Wang5739dda2014-11-16 22:44:02 -0800132 call, ok := ctx.(ipc.ServerCall)
133 if !ok {
134 return nil, fmt.Errorf("couldn't upgrade ipc.ServerContext to ipc.ServerCall")
135 }
136 results, err := p.Invoke(ipc.ReservedSignature, call, nil)
137 if err != nil {
138 return nil, err
139 }
140 if len(results) != 2 {
141 return nil, fmt.Errorf("unexpected number of result values. Got %d, want 2.", len(results))
142 }
143 if results[1] != nil {
144 err, ok := results[1].(error)
145 if !ok {
146 return nil, fmt.Errorf("unexpected error type. Got %T, want error.", err)
147 }
148 return nil, err
149 }
Todd Wang67fc8d72014-12-03 14:36:33 -0800150 var res []signature.Interface
Todd Wang5739dda2014-11-16 22:44:02 -0800151 if results[0] != nil {
Todd Wang67fc8d72014-12-03 14:36:33 -0800152 sig, ok := results[0].([]signature.Interface)
Todd Wang5739dda2014-11-16 22:44:02 -0800153 if !ok {
Todd Wang67fc8d72014-12-03 14:36:33 -0800154 return nil, fmt.Errorf("unexpected result value type. Got %T, want []signature.Interface.", sig)
Todd Wang5739dda2014-11-16 22:44:02 -0800155 }
156 }
157 return res, nil
158}
159
Todd Wang67fc8d72014-12-03 14:36:33 -0800160func (p *proxyInvoker) MethodSignature(ctx ipc.ServerContext, method string) (signature.Method, error) {
161 empty := signature.Method{}
Todd Wang5739dda2014-11-16 22:44:02 -0800162 call, ok := ctx.(ipc.ServerCall)
163 if !ok {
164 return empty, fmt.Errorf("couldn't upgrade ipc.ServerContext to ipc.ServerCall")
165 }
166 results, err := p.Invoke(ipc.ReservedMethodSignature, call, []interface{}{&method})
167 if err != nil {
168 return empty, err
169 }
170 if len(results) != 2 {
171 return empty, fmt.Errorf("unexpected number of result values. Got %d, want 2.", len(results))
172 }
173 if results[1] != nil {
174 err, ok := results[1].(error)
175 if !ok {
176 return empty, fmt.Errorf("unexpected error type. Got %T, want error.", err)
177 }
178 return empty, err
179 }
Todd Wang67fc8d72014-12-03 14:36:33 -0800180 var res signature.Method
Todd Wang5739dda2014-11-16 22:44:02 -0800181 if results[0] != nil {
Todd Wang67fc8d72014-12-03 14:36:33 -0800182 sig, ok := results[0].(signature.Method)
Todd Wang5739dda2014-11-16 22:44:02 -0800183 if !ok {
Todd Wang67fc8d72014-12-03 14:36:33 -0800184 return empty, fmt.Errorf("unexpected result value type. Got %T, want signature.Method.", sig)
Todd Wang5739dda2014-11-16 22:44:02 -0800185 }
186 }
187 return res, nil
188}
189
Robin Thellend39ac3232014-12-02 09:50:41 -0800190func (p *proxyInvoker) Globber() *ipc.GlobState {
Robin Thellend8e9cc242014-11-26 09:43:10 -0800191 return &ipc.GlobState{AllGlobber: p}
Robin Thellendb16d7162014-11-07 13:47:26 -0800192}
193
Robin Thellend39ac3232014-12-02 09:50:41 -0800194type call struct {
195 ipc.ServerContext
196 ch chan<- naming.VDLMountEntry
197}
198
199func (c *call) Recv(v interface{}) error {
200 return io.EOF
201}
202
203func (c *call) Send(v interface{}) error {
204 c.ch <- v.(naming.VDLMountEntry)
205 return nil
206}
207
208func (p *proxyInvoker) Glob__(ctx ipc.ServerContext, pattern string) (<-chan naming.VDLMountEntry, error) {
209 ch := make(chan naming.VDLMountEntry)
210 go func() {
211 p.Invoke(ipc.GlobMethod, &call{ctx, ch}, []interface{}{&pattern})
212 close(ch)
213 }()
214 return ch, nil
Robin Thellend94bc4642014-11-05 18:05:08 -0800215}
216
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700217// numResults returns the number of result values for the given method.
218func (p *proxyInvoker) numResults(method string) (int, error) {
Todd Wang5739dda2014-11-16 22:44:02 -0800219 // TODO(toddw): Replace this mechanism when the new signature mechanism is
220 // complete.
221 switch method {
222 case ipc.GlobMethod:
223 return 1, nil
224 case ipc.ReservedSignature, ipc.ReservedMethodSignature:
225 return 2, nil
226 }
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700227 sig, err := p.sigStub.Signature(nil)
228 if err != nil {
229 return 0, err
230 }
Robin Thellendb9dd9bb2014-10-29 13:54:08 -0700231 m, ok := sig.Methods[method]
232 if !ok {
233 return 0, fmt.Errorf("unknown method %q", method)
234 }
235 return len(m.OutArgs), nil
236}