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

MultiPart: 2/4

Change-Id: I68d6eb5121f295542f48c84aab498bdef2756374
diff --git a/services/wsprd/app/app.go b/services/wsprd/app/app.go
index ce64c62..3135d13 100644
--- a/services/wsprd/app/app.go
+++ b/services/wsprd/app/app.go
@@ -5,7 +5,6 @@
 import (
 	"bytes"
 	"encoding/hex"
-	"flag"
 	"fmt"
 	"io"
 	"reflect"
@@ -31,8 +30,13 @@
 	"v.io/x/ref/services/wsprd/rpc/server"
 )
 
-// pkgPath is the prefix os errors in this package.
-const pkgPath = "v.io/x/ref/services/wsprd/app"
+const (
+	// pkgPath is the prefix os errors in this package.
+	pkgPath = "v.io/x/ref/services/wsprd/app"
+
+	// defaultRetryTimeout is the default RPC timeout.
+	defaultRetryTimeout = 2 * time.Second
+)
 
 // Errors
 var (
@@ -43,18 +47,6 @@
 	invalidBlessingsHandle = verror.Register(pkgPath+".invalidBlessingsHandle", verror.NoRetry, "{1} {2} invalid blessings handle {_}")
 )
 
-// TODO(bjornick,nlacasse): Remove the retryTimeout flag once we able
-// to pass it in from javascript. For now all RPCs have the same
-// retryTimeout, set by command line flag.
-var retryTimeout *int
-
-func init() {
-	// TODO(bjornick,nlacasse): Remove the retryTimeout flag once we able
-	// to pass it in from javascript. For now all RPCs have the same
-	// retryTimeout, set by command line flag.
-	retryTimeout = flag.Int("retry-timeout", 2, "Duration in seconds to retry starting an RPC call. 0 means never retry.")
-}
-
 type outstandingRequest struct {
 	stream *outstandingStream
 	cancel context.CancelFunc
@@ -208,10 +200,39 @@
 	}
 }
 
+// callOpts turns a slice of type []RpcCallOption object into an array of rpc.CallOpt.
+func (c *Controller) callOpts(opts []RpcCallOption) ([]rpc.CallOpt, error) {
+	var callOpts []rpc.CallOpt
+
+	retryTimeoutSet := false
+
+	for _, opt := range opts {
+		switch v := opt.(type) {
+		case RpcCallOptionAllowedServersPolicy:
+			callOpts = append(callOpts, options.AllowedServersPolicy(v.Value))
+		case RpcCallOptionRetryTimeout:
+			retryTimeoutSet = true
+			callOpts = append(callOpts, options.RetryTimeout(v.Value))
+		default:
+			return nil, fmt.Errorf("Unknown RpcCallOption type %T", v)
+		}
+	}
+
+	// If no RetryTimeout was provided, use the default.
+	if !retryTimeoutSet {
+		callOpts = append(callOpts, options.RetryTimeout(defaultRetryTimeout))
+	}
+
+	return callOpts, nil
+}
+
 func (c *Controller) startCall(ctx *context.T, w lib.ClientWriter, msg *RpcRequest, inArgs []interface{}) (rpc.ClientCall, error) {
 	methodName := lib.UppercaseFirstCharacter(msg.Method)
-	retryTimeoutOpt := options.RetryTimeout(time.Duration(*retryTimeout) * time.Second)
-	clientCall, err := v23.GetClient(ctx).StartCall(ctx, msg.Name, methodName, inArgs, retryTimeoutOpt)
+	callOpts, err := c.callOpts(msg.CallOptions)
+	if err != nil {
+		return nil, err
+	}
+	clientCall, err := v23.GetClient(ctx).StartCall(ctx, msg.Name, methodName, inArgs, callOpts...)
 	if err != nil {
 		return nil, fmt.Errorf("error starting call (name: %v, method: %v, args: %v): %v", msg.Name, methodName, inArgs, err)
 	}
@@ -664,7 +685,7 @@
 
 // getSignature uses the signature manager to get and cache the signature of a remote server.
 func (c *Controller) getSignature(ctx *context.T, name string) ([]signature.Interface, error) {
-	retryTimeoutOpt := options.RetryTimeout(time.Duration(*retryTimeout) * time.Second)
+	retryTimeoutOpt := options.RetryTimeout(defaultRetryTimeout)
 	return c.signatureManager.Signature(ctx, name, retryTimeoutOpt)
 }
 
diff --git a/services/wsprd/app/app.vdl b/services/wsprd/app/app.vdl
index 46a1834..243f2cd 100644
--- a/services/wsprd/app/app.vdl
+++ b/services/wsprd/app/app.vdl
@@ -5,6 +5,7 @@
 import (
 	"time"
 
+	"v.io/v23/security"
 	"v.io/v23/vtrace"
 )
 
@@ -16,6 +17,24 @@
 	IsStreaming  bool
 	Deadline     time.WireDeadline
 	TraceRequest vtrace.Request
+	CallOptions  []RpcCallOption
+}
+
+// TODO(nlacasse): It would be nice if RpcCallOption were a struct with
+// optional types, like:
+//
+//   type RpcCallOptions struct {
+//     AllowedServersPolicy ?[]security.BlessingPattern
+//     RetryTimeout         ?time.Duration
+//   }
+//
+// Unfortunately, optional types are currently only supported for structs, not
+// slices or primitive types.  Once optional types are better supported, switch
+// this to a struct with optional types.
+
+type RpcCallOption union {
+	AllowedServersPolicy []security.BlessingPattern
+	RetryTimeout         time.Duration
 }
 
 type RpcResponse struct {
diff --git a/services/wsprd/app/app.vdl.go b/services/wsprd/app/app.vdl.go
index 53d02a3..bd2650d 100644
--- a/services/wsprd/app/app.vdl.go
+++ b/services/wsprd/app/app.vdl.go
@@ -10,7 +10,9 @@
 	"v.io/v23/vdl"
 
 	// VDL user imports
-	"v.io/v23/vdlroot/time"
+	"time"
+	"v.io/v23/security"
+	time_2 "v.io/v23/vdlroot/time"
 	"v.io/v23/vtrace"
 )
 
@@ -20,8 +22,9 @@
 	NumInArgs    int32
 	NumOutArgs   int32
 	IsStreaming  bool
-	Deadline     time.Deadline
+	Deadline     time_2.Deadline
 	TraceRequest vtrace.Request
+	CallOptions  []RpcCallOption
 }
 
 func (RpcRequest) __VDLReflect(struct {
@@ -29,6 +32,43 @@
 }) {
 }
 
+type (
+	// RpcCallOption represents any single field of the RpcCallOption union type.
+	RpcCallOption interface {
+		// Index returns the field index.
+		Index() int
+		// Interface returns the field value as an interface.
+		Interface() interface{}
+		// Name returns the field name.
+		Name() string
+		// __VDLReflect describes the RpcCallOption union type.
+		__VDLReflect(__RpcCallOptionReflect)
+	}
+	// RpcCallOptionAllowedServersPolicy represents field AllowedServersPolicy of the RpcCallOption union type.
+	RpcCallOptionAllowedServersPolicy struct{ Value []security.BlessingPattern }
+	// RpcCallOptionRetryTimeout represents field RetryTimeout of the RpcCallOption union type.
+	RpcCallOptionRetryTimeout struct{ Value time.Duration }
+	// __RpcCallOptionReflect describes the RpcCallOption union type.
+	__RpcCallOptionReflect struct {
+		Name  string "v.io/x/ref/services/wsprd/app.RpcCallOption"
+		Type  RpcCallOption
+		Union struct {
+			AllowedServersPolicy RpcCallOptionAllowedServersPolicy
+			RetryTimeout         RpcCallOptionRetryTimeout
+		}
+	}
+)
+
+func (x RpcCallOptionAllowedServersPolicy) Index() int                          { return 0 }
+func (x RpcCallOptionAllowedServersPolicy) Interface() interface{}              { return x.Value }
+func (x RpcCallOptionAllowedServersPolicy) Name() string                        { return "AllowedServersPolicy" }
+func (x RpcCallOptionAllowedServersPolicy) __VDLReflect(__RpcCallOptionReflect) {}
+
+func (x RpcCallOptionRetryTimeout) Index() int                          { return 1 }
+func (x RpcCallOptionRetryTimeout) Interface() interface{}              { return x.Value }
+func (x RpcCallOptionRetryTimeout) Name() string                        { return "RetryTimeout" }
+func (x RpcCallOptionRetryTimeout) __VDLReflect(__RpcCallOptionReflect) {}
+
 type RpcResponse struct {
 	OutArgs       []*vdl.Value
 	TraceResponse vtrace.Response
@@ -41,5 +81,6 @@
 
 func init() {
 	vdl.Register((*RpcRequest)(nil))
+	vdl.Register((*RpcCallOption)(nil))
 	vdl.Register((*RpcResponse)(nil))
 }