Repository: gammazero/nexus
Pull request #20 (2017-08-11 19:44:32)

Define `map[string]interface{}` as `Dict` type
Also, define `[]interface{}` and `List` type.


Patch:
diff --git a/auth/anonymous.go b/auth/anonymous.go
index 98065c4..adb4ead 100644
--- a/auth/anonymous.go
+++ b/auth/anonymous.go
@@ -10,9 +10,9 @@ var AnonymousAuth Authenticator = &anonymousAuth{}
 
 // Authenticate an anonymous client.  This always succeeds, and provides the
 // authmethod and authrole for the WELCOME message.
-func (a *anonymousAuth) Authenticate(details map[string]interface{}, client wamp.Peer) (*wamp.Welcome, error) {
+func (a *anonymousAuth) Authenticate(details wamp.Dict, client wamp.Peer) (*wamp.Welcome, error) {
 	// Create welcome details containing auth info.
-	details = map[string]interface{}{
+	details = wamp.Dict{
 		"authid":       wamp.GlobalID(),
 		"authmethod":   "anonymous",
 		"authrole":     "anonymous",
diff --git a/auth/authentication.go b/auth/authentication.go
index fbe7b28..5dda1a5 100644
--- a/auth/authentication.go
+++ b/auth/authentication.go
@@ -21,7 +21,7 @@ const defaultCRAuthTimeout = time.Minute
 type Authenticator interface {
 	// Authenticate takes HELLO details and returns a WELCOME message if
 	// successful, otherwise it returns an error.
-	Authenticate(details map[string]interface{}, client wamp.Peer) (*wamp.Welcome, error)
+	Authenticate(details wamp.Dict, client wamp.Peer) (*wamp.Welcome, error)
 }
 
 // PendingCRAuth is a pending challenge-response.  It contains whatever data is
diff --git a/auth/challenge_response.go b/auth/challenge_response.go
index efaec0e..9e0046c 100644
--- a/auth/challenge_response.go
+++ b/auth/challenge_response.go
@@ -8,7 +8,7 @@ import (
 
 // Challenge returns a PendingCRAuth for Challenge/Response authentication.
 type Challenger interface {
-	Challenge(details map[string]interface{}) (PendingCRAuth, error)
+	Challenge(details wamp.Dict) (PendingCRAuth, error)
 }
 
 // CRAuthenticator
@@ -22,7 +22,7 @@ func NewCRAuthenticator(challenger Challenger) (*CRAuthenticator, error) {
 	}, nil
 }
 
-func (cr *CRAuthenticator) Authenticate(details map[string]interface{}, client wamp.Peer) (*wamp.Welcome, error) {
+func (cr *CRAuthenticator) Authenticate(details wamp.Dict, client wamp.Peer) (*wamp.Welcome, error) {
 	pendingCRAuth, err := cr.challenger.Challenge(details)
 	if err != nil {
 		return nil, err
diff --git a/auth/ticket.go b/auth/ticket.go
index b8f295c..cf37aee 100644
--- a/auth/ticket.go
+++ b/auth/ticket.go
@@ -28,7 +28,7 @@ func NewTicketAuthenticator(userDB UserDB) Authenticator {
 }
 
 // Create a PendingCRAuth for ticket CR authentication.
-func (t *ticketAuthenticator) Challenge(details map[string]interface{}) (PendingCRAuth, error) {
+func (t *ticketAuthenticator) Challenge(details wamp.Dict) (PendingCRAuth, error) {
 	// The HELLO.Details.authid|string is the authentication ID (e.g. username)
 	// the client wishes to authenticate as. For Ticket-based authentication,
 	// this MUST be provided.
@@ -74,7 +74,7 @@ func (p *pendingTicketAuth) Msg() *wamp.Challenge {
 	// ticket (using authmethod) and provides no additional challenge info.
 	return &wamp.Challenge{
 		AuthMethod: "ticket",
-		Extra:      map[string]interface{}{},
+		Extra:      wamp.Dict{},
 	}
 }
 
@@ -94,7 +94,7 @@ func (p *pendingTicketAuth) Authenticate(msg *wamp.Authenticate) (*wamp.Welcome,
 	}
 
 	// Create welcome details containing auth info.
-	details := map[string]interface{}{
+	details := wamp.Dict{
 		"authid":       p.authID,
 		"authmethod":   "ticket",
 		"authrole":     p.role,
diff --git a/client/client.go b/client/client.go
index 71353da..ddf9507 100644
--- a/client/client.go
+++ b/client/client.go
@@ -5,27 +5,28 @@ import (
 	"errors"
 	"fmt"
 	"strings"
+	"sync"
 	"time"
 
 	"github.com/gammazero/nexus/logger"
 	"github.com/gammazero/nexus/wamp"
 )
 
-var clientRoleFeatures = map[string]interface{}{
-	"publisher": map[string]interface{}{
-		"features": map[string]interface{}{
+var clientRoleFeatures = wamp.Dict{
+	"publisher": wamp.Dict{
+		"features": wamp.Dict{
 			"subscriber_blackwhite_listing": true,
 			"publisher_exclusion":           true,
 		},
 	},
-	"subscriber": map[string]interface{}{
-		"features": map[string]interface{}{
+	"subscriber": wamp.Dict{
+		"features": wamp.Dict{
 			"pattern_based_subscription": true,
 			"publisher_identification":   true,
 		},
 	},
-	"callee": map[string]interface{}{
-		"features": map[string]interface{}{
+	"callee": wamp.Dict{
+		"features": wamp.Dict{
 			"pattern_based_registration": true,
 			"shared_registration":        true,
 			"call_canceling":             true,
@@ -34,8 +35,8 @@ var clientRoleFeatures = map[string]interface{}{
 			"progressive_call_results":   true,
 		},
 	},
-	"caller": map[string]interface{}{
-		"features": map[string]interface{}{
+	"caller": wamp.Dict{
+		"features": wamp.Dict{
 			"call_canceling":        true,
 			"call_timeout":          true,
 			"caller_identification": true,
@@ -45,8 +46,8 @@ var clientRoleFeatures = map[string]interface{}{
 
 // InvokeResult represents the result of invoking a procedure.
 type InvokeResult struct {
-	Args   []interface{}
-	Kwargs map[string]interface{}
+	Args   wamp.List
+	Kwargs wamp.Dict
 	Err    wamp.URI
 }
 
@@ -69,9 +70,12 @@ type Client struct {
 	actionChan chan func()
 	idGen      *wamp.IDGen
 
+	stopping          chan struct{}
+	activeInvHandlers sync.WaitGroup
+
 	id           wamp.ID
 	realm        string
-	realmDetails map[string]interface{}
+	realmDetails wamp.Dict
 
 	log logger.Logger
 }
@@ -104,6 +108,7 @@ func NewClient(p wamp.Peer, responseTimeout time.Duration, logger logger.Logger)
 
 		actionChan: make(chan func()),
 		idGen:      wamp.NewIDGen(),
+		stopping:   make(chan struct{}),
 
 		log: logger,
 	}
@@ -117,23 +122,23 @@ func (c *Client) run() {
 	}
 }
 
-// AuthFunc takes the HELLO details and CHALLENGE details and returns the
+// AuthFunc takes the HELLO details and CHALLENGE extra data and returns the
 // signature string and a details map.
 //
 // In response to a CHALLENGE message, the Client MUST send an AUTHENTICATE
 // message.  Therefore, AuthFunc does not return an error.  If an error is
-// encountered within AuthFunc, then an empty signature shouuld be returned
+// encountered within AuthFunc, then an empty signature should be returned
 // since the client cannot give a valid signature response.
 //
 // This is used to create the authHandler map passed to JoinRealm.
-type AuthFunc func(map[string]interface{}, map[string]interface{}) (string, map[string]interface{})
+type AuthFunc func(helloDetails wamp.Dict, challengeExtra wamp.Dict) (signature string, details wamp.Dict)
 
 // JoinRealm joins a WAMP realm, handling challenge/response authentication if
 // needed.
 //
 // authHandlers is a map of WAMP authmethods to functions that handle each
 // auth type.  This can be nil if router is expected to allow anonymous.
-func (c *Client) JoinRealm(realm string, details map[string]interface{}, authHandlers map[string]AuthFunc) (map[string]interface{}, error) {
+func (c *Client) JoinRealm(realm string, details wamp.Dict, authHandlers map[string]AuthFunc) (wamp.Dict, error) {
 	joinChan := make(chan bool)
 	c.actionChan <- func() {
 		joinChan <- (c.realm == "")
@@ -143,11 +148,11 @@ func (c *Client) JoinRealm(realm string, details map[string]interface{}, authHan
 		return nil, errors.New("client is already member of realm " + c.realm)
 	}
 	if details == nil {
-		details = map[string]interface{}{}
+		details = wamp.Dict{}
 	}
 	details["roles"] = clientRoles()
 	if len(authHandlers) > 0 {
-		authmethods := make([]interface{}, len(authHandlers))
+		authmethods := make(wamp.List, len(authHandlers))
 		var i int
 		for am := range authHandlers {
 			authmethods[i] = am
@@ -200,7 +205,7 @@ func (c *Client) JoinRealm(realm string, details map[string]interface{}, authHan
 }
 
 // EventHandler handles a publish event.
-type EventHandler func(args []interface{}, kwargs map[string]interface{}, details map[string]interface{})
+type EventHandler func(args wamp.List, kwargs wamp.Dict, details wamp.Dict)
 
 // Subscribe subscribes the client to the specified topic.
 //
@@ -210,9 +215,9 @@ type EventHandler func(args []interface{}, kwargs map[string]interface{}, detail
 // To request a pattern-based subscription set:
 //   options["match"] = "prefix" or "wildcard"
 //
-func (c *Client) Subscribe(topic string, fn EventHandler, options map[string]interface{}) error {
+func (c *Client) Subscribe(topic string, fn EventHandler, options wamp.Dict) error {
 	if options == nil {
-		options = map[string]interface{}{}
+		options = wamp.Dict{}
 	}
 	id := c.idGen.Next()
 	c.expectReply(id)
@@ -313,9 +318,9 @@ func (c *Client) Unsubscribe(topic string) error {
 // To request that publisher's identity is disclosed to subscribers, set:
 //   opts["disclose_me"] = true
 //
-func (c *Client) Publish(topic string, options map[string]interface{}, args []interface{}, kwargs map[string]interface{}) error {
+func (c *Client) Publish(topic string, options wamp.Dict, args wamp.List, kwargs wamp.Dict) error {
 	if options == nil {
-		options = make(map[string]interface{})
+		options = make(wamp.Dict)
 	}
 
 	// Check if the client is asking for a PUBLISHED response.
@@ -357,7 +362,7 @@ func (c *Client) Publish(topic string, options map[string]interface{}, args []in
 // The Context is used to signal that the router issues an INTERRUPT request to
 // cancel the call-in-progress.  The client application can use this to
 // abandon what it is doing, if it chooses to pay attention to ctx.Done().
-type InvocationHandler func(context.Context, []interface{}, map[string]interface{}, map[string]interface{}) (result *InvokeResult)
+type InvocationHandler func(context.Context, wamp.List, wamp.Dict, wamp.Dict) (result *InvokeResult)
 
 // Register registers the client to handle invocations of the specified
 // procedure.  The InvocationHandler is set to be called for each procedure
@@ -369,7 +374,7 @@ type InvocationHandler func(context.Context, []interface{}, map[string]interface
 // To request a shared registration pattern set:
 //  options["invoke"] = "single", "roundrobin", "random", "first", "last"
 //
-func (c *Client) Register(procedure string, fn InvocationHandler, options map[string]interface{}) error {
+func (c *Client) Register(procedure string, fn InvocationHandler, options wamp.Dict) error {
 	id := c.idGen.Next()
 	c.expectReply(id)
 	register := &wamp.Register{
@@ -507,7 +512,7 @@ func (werr RPCError) Error() string {
 // invocation or interrupt from the callee is discarded when received.
 //
 // If the callee does not support call canceling, then behavior is "skip".
-func (c *Client) Call(ctx context.Context, procedure string, options map[string]interface{}, args []interface{}, kwargs map[string]interface{}, cancelMode string) (*wamp.Result, error) {
+func (c *Client) Call(ctx context.Context, procedure string, options wamp.Dict, args wamp.List, kwargs wamp.Dict, cancelMode string) (*wamp.Result, error) {
 	switch cancelMode {
 	case "kill", "killnowait", "skip":
 	case "":
@@ -563,10 +568,10 @@ func (c *Client) LeaveRealm() error {
 		return errors.New("client has not joined a realm")
 	}
 
-	// Send GOODBYE to router.  The reouter will respond with a GOODBYE message
+	// Send GOODBYE to router.  The router will respond with a GOODBYE message
 	// which is handled by receiveFromRouter, and causes it to exit.
 	c.peer.Send(&wamp.Goodbye{
-		Details: map[string]interface{}{},
+		Details: wamp.Dict{},
 		Reason:  wamp.ErrCloseRealm,
 	})
 	return nil
@@ -577,15 +582,23 @@ func (c *Client) Close() error {
 	if err := c.LeaveRealm(); err != nil {
 		return err
 	}
+	// Cancel any invocation handlers that are still running, without them
+	// returning results to the router, since router has already said goodbye.
+	close(c.stopping)
+
+	// Wait for any active invocation handlers to finish, so that is is safe to
+	// close the actionChan which stops the client.
+	c.activeInvHandlers.Wait()
+	close(c.actionChan)
 	c.peer.Close()
 	return nil
 }
 
-func (c *Client) handleCRAuth(challenge *wamp.Challenge, details map[string]interface{}, authHandlers map[string]AuthFunc) (wamp.Message, error) {
+func (c *Client) handleCRAuth(challenge *wamp.Challenge, details wamp.Dict, authHandlers map[string]AuthFunc) (wamp.Message, error) {
 	// Look up the authentication function for the specified authmethod.
 	authFunc, ok := authHandlers[challenge.AuthMethod]
 	if !ok {
-		// The router send a challenge for an auth mehtod the client does not
+		// The router send a challenge for an auth method the client does not
 		// know how to deal with.  In response to a CHALLENGE message, the
 		// Client MUST send an AUTHENTICATE message.  So, send empty
 		// AUTHENTICATE since client does not know what to put in it.
@@ -617,7 +630,7 @@ func (c *Client) handleCRAuth(challenge *wamp.Challenge, details map[string]inte
 }
 
 // clientRoles advertises support for these client roles.
-func clientRoles() map[string]interface{} {
+func clientRoles() wamp.Dict {
 	return clientRoleFeatures
 }
 
@@ -627,7 +640,7 @@ func unexpectedMsgError(msg wamp.Message, expected wamp.MessageType) error {
 	s := fmt.Sprint("received unexpected ", msg.MessageType(),
 		" message when expecting ", expected)
 
-	var details map[string]interface{}
+	var details wamp.Dict
 	var reason string
 	switch m := msg.(type) {
 	case *wamp.Abort:
@@ -688,7 +701,6 @@ func (c *Client) receiveFromRouter() {
 		}
 	}
 
-	close(c.actionChan)
 	c.log.Println("Client", c.id, "closed")
 }
 
@@ -718,9 +730,9 @@ func (c *Client) handleInvocation(msg *wamp.Invocation) {
 			c.peer.Send(&wamp.Error{
 				Type:      wamp.INVOCATION,
 				Request:   msg.Request,
-				Details:   map[string]interface{}{},
+				Details:   wamp.Dict{},
 				Error:     wamp.ErrInvalidArgument,
-				Arguments: []interface{}{errMsg},
+				Arguments: wamp.List{errMsg},
 			})
 			c.log.Print(errMsg)
 			return
@@ -730,7 +742,7 @@ func (c *Client) handleInvocation(msg *wamp.Invocation) {
 		var cancel context.CancelFunc
 		ctx, cancel := context.WithCancel(context.Background())
 		c.invHandlerKill[msg.Request] = cancel
-
+		c.activeInvHandlers.Add(1)
 		go func() {
 			defer cancel()
 			resChan := make(chan *InvokeResult)
@@ -746,6 +758,7 @@ func (c *Client) handleInvocation(msg *wamp.Invocation) {
 			defer func() {
 				c.actionChan <- func() {
 					delete(c.invHandlerKill, msg.Request)
+					c.activeInvHandlers.Done()
 				}
 			}()
 
@@ -753,6 +766,11 @@ func (c *Client) handleInvocation(msg *wamp.Invocation) {
 			var result *InvokeResult
 			select {
 			case result = <-resChan:
+			case <-c.stopping:
+				c.log.Print("Client stopping, invocation handler canceled")
+				// Return without sending response to server.  This will also
+				// cancel the context.
+				return
 			case <-ctx.Done():
 				// Received an INTERRUPT message from the router.
 				result = &InvokeResult{Err: wamp.ErrCanceled}
@@ -763,7 +781,7 @@ func (c *Client) handleInvocation(msg *wamp.Invocation) {
 				c.peer.Send(&wamp.Error{
 					Type:        wamp.INVOCATION,
 					Request:     msg.Request,
-					Details:     map[string]interface{}{},
+					Details:     wamp.Dict{},
 					Arguments:   result.Args,
 					ArgumentsKw: result.Kwargs,
 					Error:       result.Err,
@@ -772,7 +790,7 @@ func (c *Client) handleInvocation(msg *wamp.Invocation) {
 			}
 			c.peer.Send(&wamp.Yield{
 				Request:     msg.Request,
-				Options:     map[string]interface{}{},
+				Options:     wamp.Dict{},
 				Arguments:   result.Args,
 				ArgumentsKw: result.Kwargs,
 			})
diff --git a/examples/rpc/client_callee/callee.go b/examples/rpc/client_callee/callee.go
index 7fd7ccd..d74bd53 100644
--- a/examples/rpc/client_callee/callee.go
+++ b/examples/rpc/client_callee/callee.go
@@ -44,7 +44,7 @@ func main() {
 	}
 }
 
-func sum(ctx context.Context, args []interface{}, kwargs, details map[string]interface{}) *client.InvokeResult {
+func sum(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
 	fmt.Print("Calculating sum")
 	var sum int64
 	for i := range args {
@@ -53,5 +53,5 @@ func sum(ctx context.Context, args []interface{}, kwargs, details map[string]int
 			sum += n
 		}
 	}
-	return &client.InvokeResult{Args: []interface{}{sum}}
+	return &client.InvokeResult{Args: wamp.List{sum}}
 }
diff --git a/examples/rpc/client_caller/caller.go b/examples/rpc/client_caller/caller.go
index b48c353..807bfdf 100644
--- a/examples/rpc/client_caller/caller.go
+++ b/examples/rpc/client_caller/caller.go
@@ -30,7 +30,7 @@ func main() {
 	procName := "sum"
 
 	// Test calling the procedure.
-	callArgs := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
+	callArgs := wamp.List{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
 	ctx := context.Background()
 	fmt.Println("Call remote procedure to sum numbers 1-10")
 	result, err := caller.Call(ctx, procName, nil, callArgs, nil, "")
diff --git a/examples/rpc/server_embedded_callee/server.go b/examples/rpc/server_embedded_callee/server.go
index 2118917..8d41872 100644
--- a/examples/rpc/server_embedded_callee/server.go
+++ b/examples/rpc/server_embedded_callee/server.go
@@ -61,7 +61,7 @@ func main() {
 	log.Fatal(server.ListenAndServe())
 }
 
-func sum(ctx context.Context, args []interface{}, kwargs, details map[string]interface{}) *client.InvokeResult {
+func sum(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
 	fmt.Print("Calculating sum")
 	var sum int64
 	for i := range args {
@@ -70,5 +70,5 @@ func sum(ctx context.Context, args []interface{}, kwargs, details map[string]int
 			sum += n
 		}
 	}
-	return &client.InvokeResult{Args: []interface{}{sum}}
+	return &client.InvokeResult{Args: wamp.List{sum}}
 }
diff --git a/router/broker.go b/router/broker.go
index c0b2722..f46067e 100644
--- a/router/broker.go
+++ b/router/broker.go
@@ -12,8 +12,8 @@ import (
 // - testament_meta_api
 
 // Features supported by this broker.
-var brokerFeatures = map[string]interface{}{
-	"features": map[string]interface{}{
+var brokerFeatures = wamp.Dict{
+	"features": wamp.Dict{
 		"subscriber_blackwhite_listing": true,
 		"pattern_based_subscription":    true,
 		"publisher_exclusion":           true,
@@ -62,7 +62,7 @@ type Broker interface {
 	//
 	// The data returned is suitable for use as the "features" section of the
 	// broker role in a WELCOME message.
-	Features() map[string]interface{}
+	Features() wamp.Dict
 }
 
 type broker struct {
@@ -116,7 +116,7 @@ func NewBroker(strictURI, allowDisclose bool) Broker {
 }
 
 // Features returns the features supported by this broker.
-func (b *broker) Features() map[string]interface{} {
+func (b *broker) Features() wamp.Dict {
 	return brokerFeatures
 }
 
@@ -140,7 +140,7 @@ func (b *broker) Publish(pub *Session, msg *wamp.Publish) {
 				Type:      msg.MessageType(),
 				Request:   msg.Request,
 				Error:     wamp.ErrInvalidURI,
-				Arguments: []interface{}{errMsg},
+				Arguments: wamp.List{errMsg},
 			})
 		}
 		return
@@ -162,7 +162,7 @@ func (b *broker) Publish(pub *Session, msg *wamp.Publish) {
 			pub.Send(&wamp.Error{
 				Type:    msg.MessageType(),
 				Request: msg.Request,
-				Details: map[string]interface{}{},
+				Details: wamp.Dict{},
 				Error:   wamp.ErrOptionDisallowedDiscloseMe,
 			})
 		}
@@ -197,7 +197,7 @@ func (b *broker) Subscribe(sub *Session, msg *wamp.Subscribe) {
 			Type:      msg.MessageType(),
 			Request:   msg.Request,
 			Error:     wamp.ErrInvalidURI,
-			Arguments: []interface{}{errMsg},
+			Arguments: wamp.List{errMsg},
 		})
 		return
 	}
@@ -447,7 +447,7 @@ func (b *broker) pubEvent(pub *Session, msg *wamp.Publish, pubID wamp.ID, subs m
 			continue
 		}
 
-		details := map[string]interface{}{}
+		details := wamp.Dict{}
 
 		// If a subscription was established with a pattern-based matching
 		// policy, a Broker MUST supply the original PUBLISH.Topic as provided
@@ -498,7 +498,7 @@ func (b *broker) pubSubMeta(metaTopic wamp.URI, subSessID, subID wamp.ID) {
 	pubID := wamp.GlobalID()
 	sendMeta := func(subs map[wamp.ID]*Session, sendTopic bool) {
 		for id, sub := range subs {
-			details := map[string]interface{}{}
+			details := wamp.Dict{}
 			if sendTopic {
 				details["topic"] = metaTopic
 			}
@@ -506,7 +506,7 @@ func (b *broker) pubSubMeta(metaTopic wamp.URI, subSessID, subID wamp.ID) {
 				Publication:  pubID,
 				Subscription: id,
 				Details:      details,
-				Arguments:    []interface{}{subSessID, subID},
+				Arguments:    wamp.List{subSessID, subID},
 			})
 		}
 	}
@@ -522,11 +522,11 @@ func (b *broker) pubSubCreateMeta(subTopic wamp.URI, subSessID, subID wamp.ID, m
 	pubID := wamp.GlobalID()
 	sendMeta := func(subs map[wamp.ID]*Session, sendTopic bool) {
 		for id, sub := range subs {
-			details := map[string]interface{}{}
+			details := wamp.Dict{}
 			if sendTopic {
 				details["topic"] = wamp.MetaEventSubOnCreate
 			}
-			subDetails := map[string]interface{}{
+			subDetails := wamp.Dict{
 				"id":      subID,
 				"created": created,
 				"uri":     subTopic,
@@ -536,7 +536,7 @@ func (b *broker) pubSubCreateMeta(subTopic wamp.URI, subSessID, subID wamp.ID, m
 				Publication:  pubID,
 				Subscription: id,
 				Details:      details,
-				Arguments:    []interface{}{subSessID, subDetails},
+				Arguments:    wamp.List{subSessID, subDetails},
 			})
 		}
 	}
diff --git a/router/dealer.go b/router/dealer.go
index 4351804..ef10f50 100644
--- a/router/dealer.go
+++ b/router/dealer.go
@@ -14,8 +14,8 @@ import (
 // - call trust levels
 
 // Features supported by this dealer.
-var dealerFeatures = map[string]interface{}{
-	"features": map[string]interface{}{
+var dealerFeatures = wamp.Dict{
+	"features": wamp.Dict{
 		"call_canceling":             true,
 		"call_timeout":               true,
 		"caller_identification":      true,
@@ -81,7 +81,7 @@ type Dealer interface {
 	//
 	// The data returned is suitable for use as the "features" section of the
 	// dealer role in a WELCOME message.
-	Features() map[string]interface{}
+	Features() wamp.Dict
 
 	// RegList retrieves registration IDs listed according to match policies.
 	RegList(*wamp.Invocation) wamp.Message
@@ -127,7 +127,7 @@ type invocation struct {
 }
 
 // Features returns the features supported by this dealer.
-func (d *dealer) Features() map[string]interface{} {
+func (d *dealer) Features() wamp.Dict {
 	return dealerFeatures
 }
 
@@ -226,7 +226,7 @@ func (d *dealer) Register(callee *Session, msg *wamp.Register) {
 			Type:      msg.MessageType(),
 			Request:   msg.Request,
 			Error:     wamp.ErrInvalidURI,
-			Arguments: []interface{}{errMsg},
+			Arguments: wamp.List{errMsg},
 		})
 		return
 	}
@@ -244,7 +244,7 @@ func (d *dealer) Register(callee *Session, msg *wamp.Register) {
 				Type:      msg.MessageType(),
 				Request:   msg.Request,
 				Error:     wamp.ErrInvalidURI,
-				Arguments: []interface{}{errMsg},
+				Arguments: wamp.List{errMsg},
 			})
 			return
 		}
@@ -257,7 +257,7 @@ func (d *dealer) Register(callee *Session, msg *wamp.Register) {
 		callee.Send(&wamp.Error{
 			Type:    msg.MessageType(),
 			Request: msg.Request,
-			Details: map[string]interface{}{},
+			Details: wamp.Dict{},
 			Error:   wamp.ErrOptionDisallowedDiscloseMe,
 		})
 		return
@@ -382,7 +382,7 @@ func (d *dealer) register(callee *Session, msg *wamp.Register, match, invokePoli
 			// wamp.registration.on_create is fired when a registration is
 			// created through a registration request for an URI which was
 			// previously without a registration.
-			details := map[string]interface{}{
+			details := wamp.Dict{
 				"id":      regID,
 				"created": created,
 				"uri":     msg.Procedure,
@@ -392,7 +392,7 @@ func (d *dealer) register(callee *Session, msg *wamp.Register, match, invokePoli
 			d.metaClient.Send(&wamp.Publish{
 				Request:   wamp.GlobalID(),
 				Topic:     wamp.MetaEventRegOnCreate,
-				Arguments: []interface{}{callee.ID, details},
+				Arguments: wamp.List{callee.ID, details},
 			})
 		}
 	} else {
@@ -407,7 +407,7 @@ func (d *dealer) register(callee *Session, msg *wamp.Register, match, invokePoli
 			callee.Send(&wamp.Error{
 				Type:    msg.MessageType(),
 				Request: msg.Request,
-				Details: map[string]interface{}{},
+				Details: wamp.Dict{},
 				Error:   wamp.ErrProcedureAlreadyExists,
 			})
 			return
@@ -422,7 +422,7 @@ func (d *dealer) register(callee *Session, msg *wamp.Register, match, invokePoli
 			callee.Send(&wamp.Error{
 				Type:    msg.MessageType(),
 				Request: msg.Request,
-				Details: map[string]interface{}{},
+				Details: wamp.Dict{},
 				Error:   wamp.ErrProcedureAlreadyExists,
 			})
 			return
@@ -456,7 +456,7 @@ func (d *dealer) register(callee *Session, msg *wamp.Register, match, invokePoli
 		d.metaClient.Send(&wamp.Publish{
 			Request:   wamp.GlobalID(),
 			Topic:     wamp.MetaEventRegOnRegister,
-			Arguments: []interface{}{callee.ID, regID},
+			Arguments: wamp.List{callee.ID, regID},
 		})
 	}
 }
@@ -476,7 +476,7 @@ func (d *dealer) unregister(callee *Session, msg *wamp.Unregister) {
 		callee.Send(&wamp.Error{
 			Type:    msg.MessageType(),
 			Request: msg.Request,
-			Details: map[string]interface{}{},
+			Details: wamp.Dict{},
 			Error:   wamp.ErrNoSuchRegistration,
 		})
 		return
@@ -489,7 +489,7 @@ func (d *dealer) unregister(callee *Session, msg *wamp.Unregister) {
 	d.metaClient.Send(&wamp.Publish{
 		Request:   wamp.GlobalID(),
 		Topic:     wamp.MetaEventRegOnUnregister,
-		Arguments: []interface{}{callee.ID, msg.Registration},
+		Arguments: wamp.List{callee.ID, msg.Registration},
 	})
 
 	if delReg {
@@ -500,7 +500,7 @@ func (d *dealer) unregister(callee *Session, msg *wamp.Unregister) {
 		d.metaClient.Send(&wamp.Publish{
 			Request:   wamp.GlobalID(),
 			Topic:     wamp.MetaEventRegOnDelete,
-			Arguments: []interface{}{callee.ID, msg.Registration},
+			Arguments: wamp.List{callee.ID, msg.Registration},
 		})
 	}
 }
@@ -546,7 +546,7 @@ func (d *dealer) call(caller *Session, msg *wamp.Call) {
 		caller.Send(&wamp.Error{
 			Type:    msg.MessageType(),
 			Request: msg.Request,
-			Details: map[string]interface{}{},
+			Details: wamp.Dict{},
 			Error:   wamp.ErrNoSuchProcedure,
 		})
 		return
@@ -580,7 +580,7 @@ func (d *dealer) call(caller *Session, msg *wamp.Call) {
 	} else {
 		callee = reg.callees[0]
 	}
-	details := map[string]interface{}{}
+	details := wamp.Dict{}
 
 	// A Caller might want to issue a call providing a timeout for the call to
 	// finish.
@@ -615,7 +615,7 @@ func (d *dealer) call(caller *Session, msg *wamp.Call) {
 				caller.Send(&wamp.Error{
 					Type:    msg.MessageType(),
 					Request: msg.Request,
-					Details: map[string]interface{}{},
+					Details: wamp.Dict{},
 					Error:   wamp.ErrOptionDisallowedDiscloseMe,
 				})
 			}
@@ -657,9 +657,9 @@ func (d *dealer) call(caller *Session, msg *wamp.Call) {
 		d.error(&wamp.Error{
 			Type:      wamp.INVOCATION,
 			Request:   invocationID,
-			Details:   map[string]interface{}{},
+			Details:   wamp.Dict{},
 			Error:     wamp.ErrNetworkFailure,
-			Arguments: []interface{}{"client blocked - cannot call procedure"},
+			Arguments: wamp.List{"client blocked - cannot call procedure"},
 		})
 	}
 }
@@ -710,7 +710,7 @@ func (d *dealer) cancel(caller *Session, msg *wamp.Cancel) {
 			// Send INTERRUPT message to callee.
 			err := invk.callee.Send(&wamp.Interrupt{
 				Request: invocationID,
-				Options: map[string]interface{}{},
+				Options: wamp.Dict{},
 			})
 			if err == nil {
 				log.Println("Dealer sent INTERRUPT to to cancel invocation",
@@ -743,7 +743,7 @@ func (d *dealer) cancel(caller *Session, msg *wamp.Cancel) {
 		Type:    wamp.CALL,
 		Request: msg.Request,
 		Error:   wamp.ErrCanceled,
-		Details: map[string]interface{}{},
+		Details: wamp.Dict{},
 	})
 }
 
@@ -773,7 +773,7 @@ func (d *dealer) yield(callee *Session, msg *wamp.Yield) {
 			msg.Request)
 		return
 	}
-	details := map[string]interface{}{}
+	details := wamp.Dict{}
 	if !progress {
 		delete(d.calls, callID)
 		details["progress"] = true
@@ -838,7 +838,7 @@ func (d *dealer) removeSession(callee *Session) {
 		d.metaClient.Send(&wamp.Publish{
 			Request:   wamp.GlobalID(),
 			Topic:     wamp.MetaEventRegOnUnregister,
-			Arguments: []interface{}{callee.ID, regID},
+			Arguments: wamp.List{callee.ID, regID},
 		})
 
 		if !delReg {
@@ -851,7 +851,7 @@ func (d *dealer) removeSession(callee *Session) {
 		d.metaClient.Send(&wamp.Publish{
 			Request:   wamp.GlobalID(),
 			Topic:     wamp.MetaEventRegOnDelete,
-			Arguments: []interface{}{callee.ID, regID},
+			Arguments: wamp.List{callee.ID, regID},
 		})
 	}
 	delete(d.calleeRegIDSet, callee)
@@ -926,14 +926,14 @@ func (d *dealer) RegList(msg *wamp.Invocation) wamp.Message {
 		sync <- struct{}{}
 	}
 	<-sync
-	dict := map[string]interface{}{
+	dict := wamp.Dict{
 		"exact":    exactRegs,
 		"prefix":   pfxRegs,
 		"wildcard": wcRegs,
 	}
 	return &wamp.Yield{
 		Request:   msg.Request,
-		Arguments: []interface{}{dict},
+		Arguments: wamp.List{dict},
 	}
 }
 
@@ -944,7 +944,7 @@ func (d *dealer) RegLookup(msg *wamp.Invocation) wamp.Message {
 		if procedure, ok := wamp.AsURI(msg.Arguments[0]); ok {
 			var match string
 			if len(msg.Arguments) > 1 {
-				opts := msg.Arguments[1].(map[string]interface{})
+				opts := msg.Arguments[1].(wamp.Dict)
 				match = wamp.OptionString(opts, "match")
 			}
 			sync := make(chan wamp.ID)
@@ -970,7 +970,7 @@ func (d *dealer) RegLookup(msg *wamp.Invocation) wamp.Message {
 	}
 	return &wamp.Yield{
 		Request:   msg.Request,
-		Arguments: []interface{}{regID},
+		Arguments: wamp.List{regID},
 	}
 }
 
@@ -992,20 +992,20 @@ func (d *dealer) RegMatch(msg *wamp.Invocation) wamp.Message {
 	}
 	return &wamp.Yield{
 		Request:   msg.Request,
-		Arguments: []interface{}{regID},
+		Arguments: wamp.List{regID},
 	}
 }
 
 // RegGet retrieves information on a particular registration.
 func (d *dealer) RegGet(msg *wamp.Invocation) wamp.Message {
-	var dict map[string]interface{}
+	var dict wamp.Dict
 	if len(msg.Arguments) != 0 {
 		if i64, ok := wamp.AsInt64(msg.Arguments[0]); ok {
 			sync := make(chan struct{})
 			regID := wamp.ID(i64)
 			d.actionChan <- func() {
 				if reg, ok := d.registrations[regID]; ok {
-					dict = map[string]interface{}{
+					dict = wamp.Dict{
 						"id":      regID,
 						"created": reg.created,
 						"uri":     reg.procedure,
@@ -1022,13 +1022,13 @@ func (d *dealer) RegGet(msg *wamp.Invocation) wamp.Message {
 		return &wamp.Error{
 			Type:    msg.MessageType(),
 			Request: msg.Request,
-			Details: map[string]interface{}{},
+			Details: wamp.Dict{},
 			Error:   wamp.ErrNoSuchRegistration,
 		}
 	}
 	return &wamp.Yield{
 		Request:   msg.Request,
-		Arguments: []interface{}{dict},
+		Arguments: wamp.List{dict},
 	}
 }
 
@@ -1056,13 +1056,13 @@ func (d *dealer) RegListCallees(msg *wamp.Invocation) wamp.Message {
 		return &wamp.Error{
 			Type:    msg.MessageType(),
 			Request: msg.Request,
-			Details: map[string]interface{}{},
+			Details: wamp.Dict{},
 			Error:   wamp.ErrNoSuchRegistration,
 		}
 	}
 	return &wamp.Yield{
 		Request:   msg.Request,
-		Arguments: []interface{}{calleeIDs},
+		Arguments: wamp.List{calleeIDs},
 	}
 }
 
@@ -1093,12 +1093,12 @@ func (d *dealer) RegCountCallees(msg *wamp.Invocation) wamp.Message {
 		return &wamp.Error{
 			Type:    msg.MessageType(),
 			Request: msg.Request,
-			Details: map[string]interface{}{},
+			Details: wamp.Dict{},
 			Error:   wamp.ErrNoSuchRegistration,
 		}
 	}
 	return &wamp.Yield{
 		Request:   msg.Request,
-		Arguments: []interface{}{count},
+		Arguments: wamp.List{count},
 	}
 }
diff --git a/router/realm.go b/router/realm.go
index e0fb851..6c5ed9c 100644
--- a/router/realm.go
+++ b/router/realm.go
@@ -232,7 +232,7 @@ func (r *realm) onJoin(sess *Session) {
 	<-sync
 
 	// The event payload consists of a single positional argument details|dict.
-	details := map[string]interface{}{
+	details := wamp.Dict{
 		"session":      sess.ID,
 		"authid":       wamp.OptionString(sess.Details, "authid"),
 		"authrole":     wamp.OptionString(sess.Details, "authrole"),
@@ -245,7 +245,7 @@ func (r *realm) onJoin(sess *Session) {
 	r.metaClient.Send(&wamp.Publish{
 		Request:   wamp.GlobalID(),
 		Topic:     wamp.MetaEventSessionOnJoin,
-		Arguments: []interface{}{details},
+		Arguments: wamp.List{details},
 	})
 }
 
@@ -267,7 +267,7 @@ func (r *realm) onLeave(sess *Session) {
 	r.metaClient.Send(&wamp.Publish{
 		Request:   wamp.GlobalID(),
 		Topic:     wamp.MetaEventSessionOnLeave,
-		Arguments: []interface{}{sess.ID},
+		Arguments: wamp.List{sess.ID},
 	})
 
 	r.waitHandlers.Done()
@@ -321,7 +321,7 @@ func (r *realm) handleInternalSession(sess *Session) {
 			log.Printf("Stop session %s: %v", sess, reason)
 			sess.Send(&wamp.Goodbye{
 				Reason:  reason,
-				Details: map[string]interface{}{},
+				Details: wamp.Dict{},
 			})
 			return
 		}
@@ -399,7 +399,7 @@ func (r *realm) handleInternalSession(sess *Session) {
 			// Handle client leaving realm.
 			sess.Send(&wamp.Goodbye{
 				Reason:  wamp.ErrGoodbyeAndOut,
-				Details: map[string]interface{}{},
+				Details: wamp.Dict{},
 			})
 			log.Println("GOODBYE from session", sess, "reason:", msg.Reason)
 			return
@@ -413,13 +413,13 @@ func (r *realm) handleInternalSession(sess *Session) {
 
 // authClient authenticates the client according to the authmethods in the
 // HELLO message details and the authenticators available for this realm.
-func (r *realm) authClient(client wamp.Peer, details map[string]interface{}) (*wamp.Welcome, error) {
+func (r *realm) authClient(client wamp.Peer, details wamp.Dict) (*wamp.Welcome, error) {
 	var authmethods []string
 	if _authmethods, ok := details["authmethods"]; ok {
 		switch _authmethods := _authmethods.(type) {
 		case []string:
 			authmethods = _authmethods
-		case []interface{}:
+		case wamp.List:
 			for _, x := range _authmethods {
 				authmethods = append(authmethods, x.(string))
 			}
@@ -440,7 +440,7 @@ func (r *realm) authClient(client wamp.Peer, details map[string]interface{}) (*w
 		return nil, err
 	}
 	welcome.Details["authmethod"] = method
-	welcome.Details["roles"] = map[string]interface{}{
+	welcome.Details["roles"] = wamp.Dict{
 		"broker": r.broker.Features(),
 		"dealer": r.dealer.Features(),
 	}
@@ -503,7 +503,7 @@ func (r *realm) metaProcedureHandler() {
 				r.metaClient.Send(&wamp.Error{
 					Type:    msg.MessageType(),
 					Request: msg.Request,
-					Details: map[string]interface{}{},
+					Details: wamp.Dict{},
 					Error:   wamp.ErrNoSuchProcedure,
 				})
 				continue
@@ -548,7 +548,7 @@ func (r *realm) sessionCount(msg *wamp.Invocation) wamp.Message {
 	nclients := <-retChan
 	return &wamp.Yield{
 		Request:   msg.Request,
-		Arguments: []interface{}{nclients},
+		Arguments: wamp.List{nclients},
 	}
 }
 
@@ -585,7 +585,7 @@ func (r *realm) sessionList(msg *wamp.Invocation) wamp.Message {
 		}
 	}
 	list := <-retChan
-	return &wamp.Yield{Request: msg.Request, Arguments: []interface{}{list}}
+	return &wamp.Yield{Request: msg.Request, Arguments: wamp.List{list}}
 }
 
 func (r *realm) sessionGet(msg *wamp.Invocation) wamp.Message {
@@ -593,7 +593,7 @@ func (r *realm) sessionGet(msg *wamp.Invocation) wamp.Message {
 		return &wamp.Error{
 			Type:    wamp.INVOCATION,
 			Request: msg.Request,
-			Details: map[string]interface{}{},
+			Details: wamp.Dict{},
 			Error:   wamp.ErrNoSuchSession,
 		}
 	}
@@ -625,6 +625,6 @@ func (r *realm) sessionGet(msg *wamp.Invocation) wamp.Message {
 	}
 	return &wamp.Yield{
 		Request:   msg.Request,
-		Arguments: []interface{}{dict},
+		Arguments: wamp.List{dict},
 	}
 }
diff --git a/router/router.go b/router/router.go
index cbb6ad4..6c2b9f0 100644
--- a/router/router.go
+++ b/router/router.go
@@ -135,7 +135,7 @@ func (r *router) Attach(client wamp.Peer) error {
 	sendAbort := func(reason wamp.URI, abortErr error) {
 		abortMsg := wamp.Abort{Reason: reason}
 		if abortErr != nil {
-			abortMsg.Details = map[string]interface{}{"error": abortErr.Error()}
+			abortMsg.Details = wamp.Dict{"error": abortErr.Error()}
 			log.Println("Aborting client connection:", abortErr)
 		}
 		client.Send(&abortMsg)
@@ -225,7 +225,7 @@ func (r *router) Attach(client wamp.Peer) error {
 		sendAbort(wamp.ErrNoSuchRole, err)
 		return err
 	}
-	roleVals, ok := _roleVals.(map[string]interface{})
+	roleVals, ok := _roleVals.(wamp.Dict)
 	if !ok || len(roleVals) == 0 {
 		err = errors.New("no client roles specified")
 		sendAbort(wamp.ErrNoSuchRole, err)
@@ -245,7 +245,7 @@ func (r *router) Attach(client wamp.Peer) error {
 	// specify otherwise.
 	if _, ok = hello.Details["authmethods"]; !ok {
 		if hello.Details == nil {
-			hello.Details = map[string]interface{}{}
+			hello.Details = wamp.Dict{}
 		}
 		hello.Details["authmethods"] = []string{"anonymous"}
 	}
@@ -264,7 +264,7 @@ func (r *router) Attach(client wamp.Peer) error {
 	welcome.ID = wamp.GlobalID()
 
 	// Populate session details.
-	details := map[string]interface{}{}
+	details := wamp.Dict{}
 	details["realm"] = hello.Realm
 	details["roles"] = welcome.Details["roles"]
 	details["authid"] = welcome.Details["authid"]
diff --git a/router/session.go b/router/session.go
index 98f7fa0..54dc6c6 100644
--- a/router/session.go
+++ b/router/session.go
@@ -10,7 +10,7 @@ import (
 type Session struct {
 	wamp.Peer
 	ID      wamp.ID
-	Details map[string]interface{}
+	Details wamp.Dict
 
 	stop chan wamp.URI
 }
diff --git a/wamp/dict.go b/wamp/dict.go
index df9c7d4..05e8374 100644
--- a/wamp/dict.go
+++ b/wamp/dict.go
@@ -7,17 +7,17 @@ import (
 )
 
 // NormalizeDict takes a dict and creates a new normalized dict where all
-// map[string]xxx are converted to map[string]interface{}.  Values that cannot
+// map[string]xxx are converted to Dict.  Values that cannot
 // be converted, or are already the correct map type, remain the same.
 //
 // This is used for initial conversion of hello details.  The original dict is
 // not mutated.
-func NormalizeDict(v interface{}) map[string]interface{} {
+func NormalizeDict(v interface{}) Dict {
 	val := reflect.ValueOf(v)
 	if val.Kind() != reflect.Map {
 		return nil
 	}
-	dict := map[string]interface{}{}
+	dict := Dict{}
 	for _, key := range val.MapKeys() {
 		if key.Kind() == reflect.Interface {
 			key = key.Elem()
@@ -28,6 +28,15 @@ func NormalizeDict(v interface{}) map[string]interface{} {
 		cv := val.MapIndex(key)
 		newVal := NormalizeDict(cv.Interface())
 		if newVal == nil {
+			// If the value is interface{} representing []interface{}, then
+			// convert the slice to a List type.
+			if cv.Kind() == reflect.Interface && cv.Elem().Kind() == reflect.Slice {
+				cv = cv.Elem()
+				listType := reflect.TypeOf(List{})
+				if cv.Type().ConvertibleTo(listType) {
+					cv = cv.Convert(listType)
+				}
+			}
 			dict[key.String()] = cv.Interface()
 			continue
 		}
@@ -38,16 +47,16 @@ func NormalizeDict(v interface{}) map[string]interface{} {
 
 // Return the child dictionary for the given key, or nil if not present.
 //
-// If the child is not a map[string]interface{}, an attempt is made to convert
+// If the child is not a Dict, an attempt is made to convert
 // it.  The dict is not modified since features may be looked up cuncurrently
 // for the same session.
-func DictChild(dict map[string]interface{}, key string) map[string]interface{} {
+func DictChild(dict Dict, key string) Dict {
 	iface, ok := dict[key]
 	if !ok || iface == nil {
 		// Map does not have the specified key or value is nil.
 		return nil
 	}
-	child, ok := iface.(map[string]interface{})
+	child, ok := iface.(Dict)
 	if !ok {
 		// value is not in expected form; try to convert
 		// Session details are normalized whensession is attached, so this
@@ -69,7 +78,7 @@ func DictChild(dict map[string]interface{}, key string) map[string]interface{} {
 // For example, the path []string{"roles","callee","features","call_timeout"}
 // returns  the value of the call_timeout feature as interface{}.  An error
 // is returned if the value is not present.
-func DictValue(dict map[string]interface{}, path []string) (interface{}, error) {
+func DictValue(dict Dict, path []string) (interface{}, error) {
 	for i := range path[:len(path)-1] {
 		dict = DictChild(dict, path[i])
 		if dict == nil {
@@ -92,7 +101,7 @@ func DictValue(dict map[string]interface{}, path []string) (interface{}, error)
 // For example: "roles.subscriber.features.publisher_identification" returns
 // the value of the publisher_identification feature.  An error is returned if
 // the value is not present or is not a boolean type.
-func DictFlag(dict map[string]interface{}, path []string) (bool, error) {
+func DictFlag(dict Dict, path []string) (bool, error) {
 	v, err := DictValue(dict, path)
 	if err != nil {
 		return false, err
@@ -107,7 +116,7 @@ func DictFlag(dict map[string]interface{}, path []string) (bool, error) {
 
 // OptionString returns the string value of the option with the specified name.
 // If the option is not present, an empty string is returned.
-func OptionString(opts map[string]interface{}, optionName string) string {
+func OptionString(opts Dict, optionName string) string {
 	var opt string
 	if _opt, ok := opts[optionName]; ok && _opt != nil {
 		opt, _ = _opt.(string)
@@ -117,7 +126,7 @@ func OptionString(opts map[string]interface{}, optionName string) string {
 
 // OptionURI returns the URI value of the option with the specified name.
 // If the option is not present, an empty URI is returned.
-func OptionURI(opts map[string]interface{}, optionName string) URI {
+func OptionURI(opts Dict, optionName string) URI {
 	var opt URI
 	if _opt, ok := opts[optionName]; ok && _opt != nil {
 		opt, _ = AsURI(_opt)
@@ -127,7 +136,7 @@ func OptionURI(opts map[string]interface{}, optionName string) URI {
 
 // OptionID returns the ID value of the option with the specified name.
 // If the option is not present, an ID 0 is returned.
-func OptionID(opts map[string]interface{}, optionName string) ID {
+func OptionID(opts Dict, optionName string) ID {
 	var opt ID
 	if _opt, ok := opts[optionName]; ok && _opt != nil {
 		opt, _ = AsID(_opt)
@@ -178,7 +187,7 @@ func AsInt64(v interface{}) (int64, bool) {
 
 // OptionInt64 returns the int64 value of the option with the specified name.
 // If the option is not present, a value of 0 is returned.
-func OptionInt64(opts map[string]interface{}, optionName string) int64 {
+func OptionInt64(opts Dict, optionName string) int64 {
 	if opt, ok := opts[optionName]; ok && opt != nil {
 		if i64, ok := AsInt64(opt); ok {
 			return i64
@@ -189,7 +198,7 @@ func OptionInt64(opts map[string]interface{}, optionName string) int64 {
 
 // OptionString returns the boolean value of the option with the specified
 // name.  If the option is not present, false is returned.
-func OptionFlag(opts map[string]interface{}, optionName string) bool {
+func OptionFlag(opts Dict, optionName string) bool {
 	var opt bool
 	if _opt, ok := opts[optionName]; ok && _opt != nil {
 		opt, _ = _opt.(bool)
@@ -198,9 +207,9 @@ func OptionFlag(opts map[string]interface{}, optionName string) bool {
 }
 
 // SetOption sets a single option name-value pair in message options dict.
-func SetOption(dict map[string]interface{}, name string, value interface{}) map[string]interface{} {
+func SetOption(dict Dict, name string, value interface{}) Dict {
 	if dict == nil {
-		dict = map[string]interface{}{}
+		dict = Dict{}
 	}
 	dict[name] = value
 	return dict
diff --git a/wamp/message.go b/wamp/message.go
index 5614d4e..2b0a4f6 100644
--- a/wamp/message.go
+++ b/wamp/message.go
@@ -7,6 +7,10 @@ type Message interface {
 	MessageType() MessageType
 }
 
+type Dict map[string]interface{}
+
+type List []interface{}
+
 // Message Codes and Direction
 const (
 	//                           // | Pub  | Brk  | Subs | Calr | Dealr| Calee|
@@ -140,7 +144,7 @@ func NewMessage(t MessageType) Message {
 // [HELLO, Realm|uri, Details|dict]
 type Hello struct {
 	Realm   URI
-	Details map[string]interface{}
+	Details Dict
 }
 
 func (msg *Hello) MessageType() MessageType { return HELLO }
@@ -150,7 +154,7 @@ func (msg *Hello) MessageType() MessageType { return HELLO }
 // [WELCOME, Session|id, Details|dict]
 type Welcome struct {
 	ID      ID
-	Details map[string]interface{}
+	Details Dict
 }
 
 func (msg *Welcome) MessageType() MessageType { return WELCOME }
@@ -160,7 +164,7 @@ func (msg *Welcome) MessageType() MessageType { return WELCOME }
 //
 // [ABORT, Details|dict, Reason|uri]
 type Abort struct {
-	Details map[string]interface{}
+	Details Dict
 	Reason  URI
 }
 
@@ -171,7 +175,7 @@ func (msg *Abort) MessageType() MessageType { return ABORT }
 //
 // [GOODBYE, Details|dict, Reason|uri]
 type Goodbye struct {
-	Details map[string]interface{}
+	Details Dict
 	Reason  URI
 }
 
@@ -188,10 +192,10 @@ func (msg *Goodbye) MessageType() MessageType { return GOODBYE }
 type Error struct {
 	Type        MessageType
 	Request     ID
-	Details     map[string]interface{}
+	Details     Dict
 	Error       URI
-	Arguments   []interface{}          `wamp:"omitempty"`
-	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
+	Arguments   List `wamp:"omitempty"`
+	ArgumentsKw Dict `wamp:"omitempty"`
 }
 
 func (msg *Error) MessageType() MessageType { return ERROR }
@@ -206,10 +210,10 @@ func (msg *Error) MessageType() MessageType { return ERROR }
 //     ArgumentsKw|dict]
 type Publish struct {
 	Request     ID
-	Options     map[string]interface{}
+	Options     Dict
 	Topic       URI
-	Arguments   []interface{}          `wamp:"omitempty"`
-	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
+	Arguments   List `wamp:"omitempty"`
+	ArgumentsKw Dict `wamp:"omitempty"`
 }
 
 func (msg *Publish) MessageType() MessageType { return PUBLISH }
@@ -229,7 +233,7 @@ func (msg *Published) MessageType() MessageType { return PUBLISHED }
 // [SUBSCRIBE, Request|id, Options|dict, Topic|uri]
 type Subscribe struct {
 	Request ID
-	Options map[string]interface{}
+	Options Dict
 	Topic   URI
 }
 
@@ -276,9 +280,9 @@ func (msg *Unsubscribed) MessageType() MessageType { return UNSUBSCRIBED }
 type Event struct {
 	Subscription ID
 	Publication  ID
-	Details      map[string]interface{}
-	Arguments    []interface{}          `wamp:"omitempty"`
-	ArgumentsKw  map[string]interface{} `wamp:"omitempty"`
+	Details      Dict
+	Arguments    List `wamp:"omitempty"`
+	ArgumentsKw  Dict `wamp:"omitempty"`
 }
 
 func (msg *Event) MessageType() MessageType { return EVENT }
@@ -291,7 +295,7 @@ func (msg *Event) MessageType() MessageType { return EVENT }
 // [REGISTER, Request|id, Options|dict, Procedure|uri]
 type Register struct {
 	Request   ID
-	Options   map[string]interface{}
+	Options   Dict
 	Procedure URI
 }
 
@@ -338,10 +342,10 @@ func (msg *Unregistered) MessageType() MessageType { return UNREGISTERED }
 //     ArgumentsKw|dict]
 type Call struct {
 	Request     ID
-	Options     map[string]interface{}
+	Options     Dict
 	Procedure   URI
-	Arguments   []interface{}          `wamp:"omitempty"`
-	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
+	Arguments   List `wamp:"omitempty"`
+	ArgumentsKw Dict `wamp:"omitempty"`
 }
 
 func (msg *Call) MessageType() MessageType { return CALL }
@@ -358,9 +362,9 @@ func (msg *Call) MessageType() MessageType { return CALL }
 type Invocation struct {
 	Request      ID
 	Registration ID
-	Details      map[string]interface{}
-	Arguments    []interface{}          `wamp:"omitempty"`
-	ArgumentsKw  map[string]interface{} `wamp:"omitempty"`
+	Details      Dict
+	Arguments    List `wamp:"omitempty"`
+	ArgumentsKw  Dict `wamp:"omitempty"`
 }
 
 func (msg *Invocation) MessageType() MessageType { return INVOCATION }
@@ -374,9 +378,9 @@ func (msg *Invocation) MessageType() MessageType { return INVOCATION }
 //     ArgumentsKw|dict]
 type Yield struct {
 	Request     ID
-	Options     map[string]interface{}
-	Arguments   []interface{}          `wamp:"omitempty"`
-	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
+	Options     Dict
+	Arguments   List `wamp:"omitempty"`
+	ArgumentsKw Dict `wamp:"omitempty"`
 }
 
 func (msg *Yield) MessageType() MessageType { return YIELD }
@@ -389,9 +393,9 @@ func (msg *Yield) MessageType() MessageType { return YIELD }
 //     YIELD.ArgumentsKw|dict]
 type Result struct {
 	Request     ID
-	Details     map[string]interface{}
-	Arguments   []interface{}          `wamp:"omitempty"`
-	ArgumentsKw map[string]interface{} `wamp:"omitempty"`
+	Details     Dict
+	Arguments   List `wamp:"omitempty"`
+	ArgumentsKw Dict `wamp:"omitempty"`
 }
 
 func (msg *Result) MessageType() MessageType { return RESULT }
@@ -404,7 +408,7 @@ func (msg *Result) MessageType() MessageType { return RESULT }
 // [CHALLENGE, AuthMethod|string, Extra|dict]
 type Challenge struct {
 	AuthMethod string
-	Extra      map[string]interface{}
+	Extra      Dict
 }
 
 func (msg *Challenge) MessageType() MessageType { return CHALLENGE }
@@ -416,7 +420,7 @@ func (msg *Challenge) MessageType() MessageType { return CHALLENGE }
 // [AUTHENTICATE, Signature|string, Extra|dict]
 type Authenticate struct {
 	Signature string
-	Extra     map[string]interface{}
+	Extra     Dict
 }
 
 func (msg *Authenticate) MessageType() MessageType { return AUTHENTICATE }
@@ -428,7 +432,7 @@ func (msg *Authenticate) MessageType() MessageType { return AUTHENTICATE }
 // [CANCEL, CALL.Request|id, Options|dict]
 type Cancel struct {
 	Request ID
-	Options map[string]interface{}
+	Options Dict
 }
 
 func (msg *Cancel) MessageType() MessageType { return CANCEL }
@@ -440,7 +444,7 @@ func (msg *Cancel) MessageType() MessageType { return CANCEL }
 // [INTERRUPT, INVOCATION.Request|id, Options|dict]
 type Interrupt struct {
 	Request ID
-	Options map[string]interface{}
+	Options Dict
 }
 
 func (msg *Interrupt) MessageType() MessageType { return INTERRUPT }


Test patch:
diff --git a/aat/auth_test.go b/aat/auth_test.go
index 0e7edf1..b7b4483 100644
--- a/aat/auth_test.go
+++ b/aat/auth_test.go
@@ -18,7 +18,7 @@ func TestJoinRealmWithCRAuth(t *testing.T) {
 		t.Fatal("Failed to connect client:", err)
 	}
 
-	details := map[string]interface{}{
+	details := wamp.Dict{
 		"username": "jdoe", "authmethods": []string{"testauth"}}
 	authMap := map[string]client.AuthFunc{"testauth": testAuthFunc}
 
@@ -39,7 +39,7 @@ func TestJoinRealmWithCRAuthBad(t *testing.T) {
 		t.Fatal("Failed to connect client:", err)
 	}
 
-	details := map[string]interface{}{
+	details := wamp.Dict{
 		"username": "malory", "authmethods": []string{"testauth"}}
 	authMap := map[string]client.AuthFunc{"testauth": testAuthFunc}
 
@@ -52,9 +52,9 @@ func TestJoinRealmWithCRAuthBad(t *testing.T) {
 	}
 }
 
-func testAuthFunc(d map[string]interface{}, c map[string]interface{}) (string, map[string]interface{}) {
+func testAuthFunc(d wamp.Dict, c wamp.Dict) (string, wamp.Dict) {
 	ch := c["challenge"].(string)
-	return testCRSign(ch), map[string]interface{}{}
+	return testCRSign(ch), wamp.Dict{}
 }
 
 type testCRAuthenticator struct{}
@@ -66,7 +66,7 @@ type pendingTestAuth struct {
 	role   string
 }
 
-func (t *testCRAuthenticator) Challenge(details map[string]interface{}) (auth.PendingCRAuth, error) {
+func (t *testCRAuthenticator) Challenge(details wamp.Dict) (auth.PendingCRAuth, error) {
 	var username string
 	_username, ok := details["username"]
 	if ok {
@@ -95,7 +95,7 @@ func testCRSign(uname string) string {
 func (p *pendingTestAuth) Msg() *wamp.Challenge {
 	return &wamp.Challenge{
 		AuthMethod: "testauth",
-		Extra:      map[string]interface{}{"challenge": p.authID},
+		Extra:      wamp.Dict{"challenge": p.authID},
 	}
 }
 
@@ -108,7 +108,7 @@ func (p *pendingTestAuth) Authenticate(msg *wamp.Authenticate) (*wamp.Welcome, e
 	}
 
 	// Create welcome details containing auth info.
-	details := map[string]interface{}{
+	details := wamp.Dict{
 		"authid":     p.authID,
 		"authmethod": "testauth",
 		"authrole":   p.role,
diff --git a/aat/pubsub_test.go b/aat/pubsub_test.go
index 8bc6979..474e94f 100644
--- a/aat/pubsub_test.go
+++ b/aat/pubsub_test.go
@@ -23,7 +23,7 @@ func TestPubSub(t *testing.T) {
 	}
 
 	errChan := make(chan error)
-	evtHandler := func(args []interface{}, kwargs map[string]interface{}, details map[string]interface{}) {
+	evtHandler := func(args wamp.List, kwargs wamp.Dict, details wamp.Dict) {
 		if args[0].(string) != "hello world" {
 			errChan <- errors.New("event missing or bad args")
 			return
@@ -43,7 +43,7 @@ func TestPubSub(t *testing.T) {
 		t.Fatal("Failed to connect client:", err)
 	}
 	// Publish an event to something that matches by wildcard.
-	publisher.Publish(testTopic, nil, []interface{}{"hello world"}, nil)
+	publisher.Publish(testTopic, nil, wamp.List{"hello world"}, nil)
 
 	// Make sure the event was received.
 	select {
@@ -79,7 +79,7 @@ func TestPubSubWildcard(t *testing.T) {
 	}
 
 	errChan := make(chan error)
-	evtHandler := func(args []interface{}, kwargs map[string]interface{}, details map[string]interface{}) {
+	evtHandler := func(args wamp.List, kwargs wamp.Dict, details wamp.Dict) {
 		if args[0].(string) != "hello world" {
 			errChan <- errors.New("event missing or bad args")
 			return
@@ -104,7 +104,7 @@ func TestPubSubWildcard(t *testing.T) {
 		t.Fatal("Failed to connect client:", err)
 	}
 	// Publish an event to something that matches by wildcard.
-	publisher.Publish(testTopic, nil, []interface{}{"hello world"}, nil)
+	publisher.Publish(testTopic, nil, wamp.List{"hello world"}, nil)
 
 	// Make sure the event was received.
 	select {
@@ -139,7 +139,7 @@ func TestUnsubscribeWrongTopic(t *testing.T) {
 		t.Fatal("Failed to connect client:", err)
 	}
 
-	evtHandler := func(args []interface{}, kwargs map[string]interface{}, details map[string]interface{}) {
+	evtHandler := func(args wamp.List, kwargs wamp.Dict, details wamp.Dict) {
 		return
 	}
 
@@ -201,7 +201,7 @@ func TestSubscribeBurst(t *testing.T) {
 		t.Fatal("Failed to connect client:", err)
 	}
 
-	evtHandler := func(args []interface{}, kwargs map[string]interface{}, details map[string]interface{}) {
+	evtHandler := func(args wamp.List, kwargs wamp.Dict, details wamp.Dict) {
 		return
 	}
 
diff --git a/aat/rpc_test.go b/aat/rpc_test.go
index 82097ff..8501eea 100644
--- a/aat/rpc_test.go
+++ b/aat/rpc_test.go
@@ -17,7 +17,7 @@ func TestRPCRegisterAndCall(t *testing.T) {
 	}
 
 	// Test registering a valid procedure.
-	handler := func(ctx context.Context, args []interface{}, kwargs, details map[string]interface{}) *client.InvokeResult {
+	handler := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
 		var sum int64
 		for i := range args {
 			n, ok := wamp.AsInt64(args[i])
@@ -25,7 +25,7 @@ func TestRPCRegisterAndCall(t *testing.T) {
 				sum += n
 			}
 		}
-		return &client.InvokeResult{Args: []interface{}{sum}}
+		return &client.InvokeResult{Args: wamp.List{sum}}
 	}
 
 	// Register procedure "sum"
@@ -41,7 +41,7 @@ func TestRPCRegisterAndCall(t *testing.T) {
 	}
 
 	// Test calling the procedure.
-	callArgs := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
+	callArgs := wamp.List{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
 	ctx := context.Background()
 	result, err := caller.Call(ctx, procName, nil, callArgs, nil, "")
 	if err != nil {
@@ -79,7 +79,7 @@ func TestRPCCallUnregistered(t *testing.T) {
 	}
 
 	// Test calling unregistered procedure.
-	callArgs := []interface{}{555}
+	callArgs := wamp.List{555}
 	ctx := context.Background()
 	result, err := caller.Call(ctx, "NotRegistered", nil, callArgs, nil, "")
 	if err == nil {
@@ -121,7 +121,7 @@ func TestRPCCancelCall(t *testing.T) {
 	}
 
 	// Register procedure that waits.
-	handler := func(ctx context.Context, args []interface{}, kwargs, details map[string]interface{}) *client.InvokeResult {
+	handler := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
 		<-ctx.Done() // handler will block forever until canceled.
 		return &client.InvokeResult{Err: wamp.ErrCanceled}
 	}
@@ -140,7 +140,7 @@ func TestRPCCancelCall(t *testing.T) {
 	ctx, cancel := context.WithCancel(context.Background())
 	// Calling the procedure, should block.
 	go func() {
-		callArgs := []interface{}{73}
+		callArgs := wamp.List{73}
 		_, err := caller.Call(ctx, procName, nil, callArgs, nil, "killnowait")
 		errChan <- err
 	}()
diff --git a/aat/rpcshared_test.go b/aat/rpcshared_test.go
index 20f3a56..bb0dc71 100644
--- a/aat/rpcshared_test.go
+++ b/aat/rpcshared_test.go
@@ -17,8 +17,8 @@ func TestRPCSharedRoundRobin(t *testing.T) {
 	if err != nil {
 		t.Fatal("Failed to connect client:", err)
 	}
-	testProc1 := func(ctx context.Context, args []interface{}, kwargs, details map[string]interface{}) *client.InvokeResult {
-		return &client.InvokeResult{Args: []interface{}{1}}
+	testProc1 := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
+		return &client.InvokeResult{Args: wamp.List{1}}
 	}
 	if err = callee1.Register(procName, testProc1, options); err != nil {
 		t.Fatal("failed to register procedure:", err)
@@ -29,8 +29,8 @@ func TestRPCSharedRoundRobin(t *testing.T) {
 	if err != nil {
 		t.Fatal("Failed to connect client:", err)
 	}
-	testProc2 := func(ctx context.Context, args []interface{}, kwargs, details map[string]interface{}) *client.InvokeResult {
-		return &client.InvokeResult{Args: []interface{}{2}}
+	testProc2 := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
+		return &client.InvokeResult{Args: wamp.List{2}}
 	}
 	if err = callee2.Register(procName, testProc2, options); err != nil {
 		t.Fatal("failed to register procedure:", err)
@@ -41,8 +41,8 @@ func TestRPCSharedRoundRobin(t *testing.T) {
 	if err != nil {
 		t.Fatal("Failed to connect client:", err)
 	}
-	testProc3 := func(ctx context.Context, args []interface{}, kwargs, details map[string]interface{}) *client.InvokeResult {
-		return &client.InvokeResult{Args: []interface{}{3}}
+	testProc3 := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
+		return &client.InvokeResult{Args: wamp.List{3}}
 	}
 	if err = callee3.Register(procName, testProc3, options); err != nil {
 		t.Fatal("failed to register procedure:", err)
@@ -133,8 +133,8 @@ func TestRPCSharedRandom(t *testing.T) {
 	if err != nil {
 		t.Fatal("Failed to connect client:", err)
 	}
-	testProc1 := func(ctx context.Context, args []interface{}, kwargs, details map[string]interface{}) *client.InvokeResult {
-		return &client.InvokeResult{Args: []interface{}{1}}
+	testProc1 := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
+		return &client.InvokeResult{Args: wamp.List{1}}
 	}
 	if err = callee1.Register(procName, testProc1, options); err != nil {
 		t.Fatal("failed to register procedure:", err)
@@ -145,8 +145,8 @@ func TestRPCSharedRandom(t *testing.T) {
 	if err != nil {
 		t.Fatal("Failed to connect client:", err)
 	}
-	testProc2 := func(ctx context.Context, args []interface{}, kwargs, details map[string]interface{}) *client.InvokeResult {
-		return &client.InvokeResult{Args: []interface{}{2}}
+	testProc2 := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
+		return &client.InvokeResult{Args: wamp.List{2}}
 	}
 	if err = callee2.Register(procName, testProc2, options); err != nil {
 		t.Fatal("failed to register procedure:", err)
@@ -157,8 +157,8 @@ func TestRPCSharedRandom(t *testing.T) {
 	if err != nil {
 		t.Fatal("Failed to connect client:", err)
 	}
-	testProc3 := func(ctx context.Context, args []interface{}, kwargs, details map[string]interface{}) *client.InvokeResult {
-		return &client.InvokeResult{Args: []interface{}{3}}
+	testProc3 := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *client.InvokeResult {
+		return &client.InvokeResult{Args: wamp.List{3}}
 	}
 	if err = callee3.Register(procName, testProc3, options); err != nil {
 		t.Fatal("failed to register procedure:", err)
diff --git a/auth/anonymous_test.go b/auth/anonymous_test.go
index cac037e..a42d76e 100644
--- a/auth/anonymous_test.go
+++ b/auth/anonymous_test.go
@@ -9,7 +9,7 @@ import (
 func TestAnonAuth(t *testing.T) {
 	anonAuth := AnonymousAuth
 
-	details := map[string]interface{}{
+	details := wamp.Dict{
 		"authid":      "someone",
 		"authmethods": []string{"anonymous"}}
 	welcome, err := anonAuth.Authenticate(details, nil)
diff --git a/auth/ticket_test.go b/auth/ticket_test.go
index 674f481..5c1c855 100644
--- a/auth/ticket_test.go
+++ b/auth/ticket_test.go
@@ -12,7 +12,7 @@ func TestTicketAuth(t *testing.T) {
 	ticketAuth = tAuth
 
 	// Test with missing authid
-	details := map[string]interface{}{"authmethod": "ticket"}
+	details := wamp.Dict{"authmethod": "ticket"}
 	pendingAuth, err := ticketAuth.Challenge(details)
 	if err == nil {
 		t.Fatal("expected error with missing authid")
diff --git a/client/client_test.go b/client/client_test.go
index 227278d..1d29187 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -128,7 +128,7 @@ func TestJoinRealmWithCRAuth(t *testing.T) {
 	peer := getTestPeer(r)
 	client := NewClient(peer, 0, log)
 
-	details := map[string]interface{}{
+	details := wamp.Dict{
 		"username": "jdoe", "authmethods": []string{"testauth"}}
 	authMap := map[string]AuthFunc{"testauth": testAuthFunc}
 	_, err = client.JoinRealm("nexus.test.auth", details, authMap)
@@ -146,7 +146,7 @@ func TestSubscribe(t *testing.T) {
 
 	testTopic := "nexus.test.topic"
 	errChan := make(chan error)
-	evtHandler := func(args []interface{}, kwargs map[string]interface{}, details map[string]interface{}) {
+	evtHandler := func(args wamp.List, kwargs wamp.Dict, details wamp.Dict) {
 		if args[0].(string) != "hello world" {
 			errChan <- errors.New("event missing or bad args")
 			return
@@ -173,7 +173,7 @@ func TestSubscribe(t *testing.T) {
 	}
 
 	// Publish an event to something that matches by wildcard.
-	pub.Publish(testTopic, nil, []interface{}{"hello world"}, nil)
+	pub.Publish(testTopic, nil, wamp.List{"hello world"}, nil)
 
 	// Make sure the event was received.
 	select {
@@ -203,8 +203,8 @@ func TestRemoteProcedureCall(t *testing.T) {
 	}
 
 	// Test registering a valid procedure.
-	handler := func(ctx context.Context, args []interface{}, kwargs, details map[string]interface{}) *InvokeResult {
-		return &InvokeResult{Args: []interface{}{args[0].(int) * 37}}
+	handler := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *InvokeResult {
+		return &InvokeResult{Args: wamp.List{args[0].(int) * 37}}
 	}
 	procName := "myproc"
 	if err = callee.Register(procName, handler, nil); err != nil {
@@ -212,7 +212,7 @@ func TestRemoteProcedureCall(t *testing.T) {
 	}
 
 	// Test calling the procedure.
-	callArgs := []interface{}{73}
+	callArgs := wamp.List{73}
 	ctx := context.Background()
 	result, err := caller.Call(ctx, procName, nil, callArgs, nil, "")
 	if err != nil {
@@ -228,7 +228,7 @@ func TestRemoteProcedureCall(t *testing.T) {
 	}
 
 	// Test calling unregistered procedure.
-	callArgs = []interface{}{555}
+	callArgs = wamp.List{555}
 	result, err = caller.Call(ctx, procName, nil, callArgs, nil, "")
 	if err == nil {
 		t.Fatal("expected error calling unregistered procedure")
@@ -246,7 +246,7 @@ func TestTimeoutCancelRemoteProcedureCall(t *testing.T) {
 	}
 
 	// Test registering a valid procedure.
-	handler := func(ctx context.Context, args []interface{}, kwargs, details map[string]interface{}) *InvokeResult {
+	handler := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *InvokeResult {
 		<-ctx.Done() // handler will block forever until canceled.
 		return &InvokeResult{Err: wamp.ErrCanceled}
 	}
@@ -260,7 +260,7 @@ func TestTimeoutCancelRemoteProcedureCall(t *testing.T) {
 	defer cancel()
 	// Calling the procedure, should block.
 	go func() {
-		callArgs := []interface{}{73}
+		callArgs := wamp.List{73}
 		_, err := caller.Call(ctx, procName, nil, callArgs, nil, "killnowait")
 		errChan <- err
 	}()
@@ -299,7 +299,7 @@ func TestCancelRemoteProcedureCall(t *testing.T) {
 	}
 
 	// Test registering a valid procedure.
-	handler := func(ctx context.Context, args []interface{}, kwargs, details map[string]interface{}) *InvokeResult {
+	handler := func(ctx context.Context, args wamp.List, kwargs, details wamp.Dict) *InvokeResult {
 		<-ctx.Done() // handler will block forever until canceled.
 		return &InvokeResult{Err: wamp.ErrCanceled}
 	}
@@ -312,7 +312,7 @@ func TestCancelRemoteProcedureCall(t *testing.T) {
 	ctx, cancel := context.WithCancel(context.Background())
 	// Calling the procedure, should block.
 	go func() {
-		callArgs := []interface{}{73}
+		callArgs := wamp.List{73}
 		_, err := caller.Call(ctx, procName, nil, callArgs, nil, "killnowait")
 		errChan <- err
 	}()
@@ -346,9 +346,9 @@ func TestCancelRemoteProcedureCall(t *testing.T) {
 }
 
 // ---- authentication test stuff ------
-func testAuthFunc(d map[string]interface{}, c map[string]interface{}) (string, map[string]interface{}) {
+func testAuthFunc(d wamp.Dict, c wamp.Dict) (string, wamp.Dict) {
 	ch := c["challenge"].(string)
-	return testCRSign(ch), map[string]interface{}{}
+	return testCRSign(ch), wamp.Dict{}
 }
 
 type testCRAuthenticator struct{}
@@ -360,7 +360,7 @@ type pendingTestAuth struct {
 	role   string
 }
 
-func (t *testCRAuthenticator) Challenge(details map[string]interface{}) (auth.PendingCRAuth, error) {
+func (t *testCRAuthenticator) Challenge(details wamp.Dict) (auth.PendingCRAuth, error) {
 	var username string
 	_username, ok := details["username"]
 	if ok {
@@ -387,7 +387,7 @@ func testCRSign(uname string) string {
 func (p *pendingTestAuth) Msg() *wamp.Challenge {
 	return &wamp.Challenge{
 		AuthMethod: "testauth",
-		Extra:      map[string]interface{}{"challenge": p.authID},
+		Extra:      wamp.Dict{"challenge": p.authID},
 	}
 }
 
@@ -400,7 +400,7 @@ func (p *pendingTestAuth) Authenticate(msg *wamp.Authenticate) (*wamp.Welcome, e
 	}
 
 	// Create welcome details containing auth info.
-	details := map[string]interface{}{
+	details := wamp.Dict{
 		"authid":     p.authID,
 		"authmethod": "testauth",
 		"authrole":   p.role,
diff --git a/router/broker_test.go b/router/broker_test.go
index 3f81192..6bafdc7 100644
--- a/router/broker_test.go
+++ b/router/broker_test.go
@@ -209,7 +209,7 @@ func TestBasicPubSub(t *testing.T) {
 	publisher := newTestPeer()
 	pubSess := &Session{Peer: publisher}
 	broker.Publish(pubSess, &wamp.Publish{Request: 124, Topic: testTopic,
-		Arguments: []interface{}{"hello world"}})
+		Arguments: wamp.List{"hello world"}})
 	rsp = <-sess.Recv()
 	evt, ok := rsp.(*wamp.Event)
 	if !ok {
@@ -235,7 +235,7 @@ func TestPrefxPatternBasedSubscription(t *testing.T) {
 	msg := &wamp.Subscribe{
 		Request: 123,
 		Topic:   testTopicPfx,
-		Options: map[string]interface{}{"match": "prefix"},
+		Options: wamp.Dict{"match": "prefix"},
 	}
 	broker.Subscribe(sess, msg)
 
@@ -295,7 +295,7 @@ func TestWildcardPatternBasedSubscription(t *testing.T) {
 	msg := &wamp.Subscribe{
 		Request: 123,
 		Topic:   testTopicWc,
-		Options: map[string]interface{}{"match": "wildcard"},
+		Options: wamp.Dict{"match": "wildcard"},
 	}
 	broker.Subscribe(sess, msg)
 
@@ -348,7 +348,7 @@ func TestWildcardPatternBasedSubscription(t *testing.T) {
 func TestSubscriberBlackwhiteListing(t *testing.T) {
 	broker := NewBroker(false, true).(*broker)
 	subscriber := newTestPeer()
-	details := map[string]interface{}{
+	details := wamp.Dict{
 		"authid":   "jdoe",
 		"authrole": "admin",
 	}
@@ -370,10 +370,10 @@ func TestSubscriberBlackwhiteListing(t *testing.T) {
 
 	publisher := newTestPeer()
 
-	details = map[string]interface{}{
-		"roles": map[string]interface{}{
-			"publisher": map[string]interface{}{
-				"features": map[string]interface{}{
+	details = wamp.Dict{
+		"roles": wamp.Dict{
+			"publisher": wamp.Dict{
+				"features": wamp.Dict{
 					"subscriber_blackwhite_listing": true,
 				},
 			},
@@ -385,7 +385,7 @@ func TestSubscriberBlackwhiteListing(t *testing.T) {
 	broker.Publish(pubSess, &wamp.Publish{
 		Request: 124,
 		Topic:   testTopic,
-		Options: map[string]interface{}{"eligible": []string{string(sess.ID)}},
+		Options: wamp.Dict{"eligible": []string{string(sess.ID)}},
 	})
 	rsp, err := wamp.RecvTimeout(sess, time.Second)
 	if err != nil {
@@ -395,7 +395,7 @@ func TestSubscriberBlackwhiteListing(t *testing.T) {
 	broker.Publish(pubSess, &wamp.Publish{
 		Request: 125,
 		Topic:   testTopic,
-		Options: map[string]interface{}{"eligible_authrole": []string{"admin"}},
+		Options: wamp.Dict{"eligible_authrole": []string{"admin"}},
 	})
 	rsp, err = wamp.RecvTimeout(sess, time.Second)
 	if err != nil {
@@ -405,7 +405,7 @@ func TestSubscriberBlackwhiteListing(t *testing.T) {
 	broker.Publish(pubSess, &wamp.Publish{
 		Request: 126,
 		Topic:   testTopic,
-		Options: map[string]interface{}{"eligible_authid": []string{"jdoe"}},
+		Options: wamp.Dict{"eligible_authid": []string{"jdoe"}},
 	})
 	rsp, err = wamp.RecvTimeout(sess, time.Second)
 	if err != nil {
@@ -416,7 +416,7 @@ func TestSubscriberBlackwhiteListing(t *testing.T) {
 	broker.Publish(pubSess, &wamp.Publish{
 		Request: 127,
 		Topic:   testTopic,
-		Options: map[string]interface{}{"exclude": []string{string(sess.ID)}},
+		Options: wamp.Dict{"exclude": []string{string(sess.ID)}},
 	})
 	rsp, err = wamp.RecvTimeout(sess, time.Second)
 	if err == nil {
@@ -426,7 +426,7 @@ func TestSubscriberBlackwhiteListing(t *testing.T) {
 	broker.Publish(pubSess, &wamp.Publish{
 		Request: 128,
 		Topic:   testTopic,
-		Options: map[string]interface{}{"exclude_authrole": []string{"admin"}},
+		Options: wamp.Dict{"exclude_authrole": []string{"admin"}},
 	})
 	rsp, err = wamp.RecvTimeout(sess, time.Second)
 	if err == nil {
@@ -436,7 +436,7 @@ func TestSubscriberBlackwhiteListing(t *testing.T) {
 	broker.Publish(pubSess, &wamp.Publish{
 		Request: 129,
 		Topic:   testTopic,
-		Options: map[string]interface{}{"exclude_authid": []string{"jdoe"}},
+		Options: wamp.Dict{"exclude_authid": []string{"jdoe"}},
 	})
 	rsp, err = wamp.RecvTimeout(sess, time.Second)
 	if err == nil {
@@ -447,7 +447,7 @@ func TestSubscriberBlackwhiteListing(t *testing.T) {
 	broker.Publish(pubSess, &wamp.Publish{
 		Request: 126,
 		Topic:   testTopic,
-		Options: map[string]interface{}{"eligible_authid": []string{"jdoe"},
+		Options: wamp.Dict{"eligible_authid": []string{"jdoe"},
 			"exclude_authid": []string{"jdoe"}},
 	})
 	rsp, err = wamp.RecvTimeout(sess, time.Second)
@@ -476,10 +476,10 @@ func TestPublisherExclusion(t *testing.T) {
 
 	publisher := newTestPeer()
 
-	details := map[string]interface{}{
-		"roles": map[string]interface{}{
-			"publisher": map[string]interface{}{
-				"features": map[string]interface{}{
+	details := wamp.Dict{
+		"roles": wamp.Dict{
+			"publisher": wamp.Dict{
+				"features": wamp.Dict{
 					"publisher_exclusion": true,
 				},
 			},
@@ -503,7 +503,7 @@ func TestPublisherExclusion(t *testing.T) {
 	broker.Publish(pubSess, &wamp.Publish{
 		Request: 124,
 		Topic:   testTopic,
-		Options: map[string]interface{}{"exclude_me": false},
+		Options: wamp.Dict{"exclude_me": false},
 	})
 	rsp, err = wamp.RecvTimeout(sess, time.Second)
 	if err != nil {
@@ -518,7 +518,7 @@ func TestPublisherExclusion(t *testing.T) {
 	broker.Publish(pubSess, &wamp.Publish{
 		Request: 124,
 		Topic:   testTopic,
-		Options: map[string]interface{}{"exclude_me": true},
+		Options: wamp.Dict{"exclude_me": true},
 	})
 	rsp, err = wamp.RecvTimeout(sess, time.Second)
 	if err != nil {
@@ -534,10 +534,10 @@ func TestPublisherIdentification(t *testing.T) {
 	broker := NewBroker(false, true).(*broker)
 	subscriber := newTestPeer()
 
-	details := map[string]interface{}{
-		"roles": map[string]interface{}{
-			"subscriber": map[string]interface{}{
-				"features": map[string]interface{}{
+	details := wamp.Dict{
+		"roles": wamp.Dict{
+			"subscriber": wamp.Dict{
+				"features": wamp.Dict{
 					"publisher_identification": true,
 				},
 			},
@@ -561,7 +561,7 @@ func TestPublisherIdentification(t *testing.T) {
 	broker.Publish(pubSess, &wamp.Publish{
 		Request: 124,
 		Topic:   testTopic,
-		Options: map[string]interface{}{"disclose_me": true},
+		Options: wamp.Dict{"disclose_me": true},
 	})
 	rsp = <-sess.Recv()
 	evt, ok := rsp.(*wamp.Event)
diff --git a/router/dealer_test.go b/router/dealer_test.go
index 1b21fd6..fbbdaed 100644
--- a/router/dealer_test.go
+++ b/router/dealer_test.go
@@ -280,10 +280,10 @@ func TestRemovePeer(t *testing.T) {
 func TestCancelCallModeKill(t *testing.T) {
 	dealer, metaClient := newTestDealer()
 
-	calleeRoles := map[string]interface{}{
-		"roles": map[string]interface{}{
-			"callee": map[string]interface{}{
-				"features": map[string]interface{}{
+	calleeRoles := wamp.Dict{
+		"roles": wamp.Dict{
+			"callee": wamp.Dict{
+				"features": wamp.Dict{
 					"call_canceling": true,
 				},
 			},
@@ -338,7 +338,7 @@ func TestCancelCallModeKill(t *testing.T) {
 		Type:    wamp.INVOCATION,
 		Request: inv.Request,
 		Error:   wamp.ErrCanceled,
-		Details: map[string]interface{}{"reason": "callee canceled"},
+		Details: wamp.Dict{"reason": "callee canceled"},
 	})
 
 	// Check that caller receives the ERROR message.
@@ -361,10 +361,10 @@ func TestCancelCallModeKill(t *testing.T) {
 func TestCancelCallModeKillNoWait(t *testing.T) {
 	dealer, metaClient := newTestDealer()
 
-	calleeRoles := map[string]interface{}{
-		"roles": map[string]interface{}{
-			"callee": map[string]interface{}{
-				"features": map[string]interface{}{
+	calleeRoles := wamp.Dict{
+		"roles": wamp.Dict{
+			"callee": wamp.Dict{
+				"features": wamp.Dict{
 					"call_canceling": true,
 				},
 			},
@@ -419,7 +419,7 @@ func TestCancelCallModeKillNoWait(t *testing.T) {
 		Type:    wamp.INVOCATION,
 		Request: inv.Request,
 		Error:   wamp.ErrCanceled,
-		Details: map[string]interface{}{"reason": "callee canceled"},
+		Details: wamp.Dict{"reason": "callee canceled"},
 	})
 
 	// Check that caller receives the ERROR message.
@@ -441,10 +441,10 @@ func TestCancelCallModeSkip(t *testing.T) {
 
 	// Register a procedure.
 	callee := newTestPeer()
-	calleeRoles := map[string]interface{}{
-		"roles": map[string]interface{}{
-			"callee": map[string]interface{}{
-				"features": map[string]interface{}{
+	calleeRoles := wamp.Dict{
+		"roles": wamp.Dict{
+			"callee": wamp.Dict{
+				"features": wamp.Dict{
 					"call_canceling": true,
 				},
 			},
@@ -503,10 +503,10 @@ func TestCancelCallModeSkip(t *testing.T) {
 func TestSharedRegistrationRoundRobin(t *testing.T) {
 	dealer, metaClient := newTestDealer()
 
-	calleeRoles := map[string]interface{}{
-		"roles": map[string]interface{}{
-			"callee": map[string]interface{}{
-				"features": map[string]interface{}{
+	calleeRoles := wamp.Dict{
+		"roles": wamp.Dict{
+			"callee": wamp.Dict{
+				"features": wamp.Dict{
 					"shared_registration": true,
 				},
 			},
@@ -622,10 +622,10 @@ func TestSharedRegistrationRoundRobin(t *testing.T) {
 func TestSharedRegistrationFirst(t *testing.T) {
 	dealer, metaClient := newTestDealer()
 
-	calleeRoles := map[string]interface{}{
-		"roles": map[string]interface{}{
-			"callee": map[string]interface{}{
-				"features": map[string]interface{}{
+	calleeRoles := wamp.Dict{
+		"roles": wamp.Dict{
+			"callee": wamp.Dict{
+				"features": wamp.Dict{
 					"shared_registration": true,
 				},
 			},
@@ -790,10 +790,10 @@ func TestSharedRegistrationFirst(t *testing.T) {
 func TestSharedRegistrationLast(t *testing.T) {
 	dealer, metaClient := newTestDealer()
 
-	calleeRoles := map[string]interface{}{
-		"roles": map[string]interface{}{
-			"callee": map[string]interface{}{
-				"features": map[string]interface{}{
+	calleeRoles := wamp.Dict{
+		"roles": wamp.Dict{
+			"callee": wamp.Dict{
+				"features": wamp.Dict{
 					"shared_registration": true,
 				},
 			},
diff --git a/router/router_test.go b/router/router_test.go
index 366ade9..5f03ac1 100644
--- a/router/router_test.go
+++ b/router/router_test.go
@@ -20,17 +20,17 @@ func init() {
 	DebugEnabled = true
 }
 
-var clientRoles = map[string]interface{}{
-	"roles": map[string]interface{}{
-		"subscriber": map[string]interface{}{
-			"features": map[string]interface{}{
+var clientRoles = wamp.Dict{
+	"roles": wamp.Dict{
+		"subscriber": wamp.Dict{
+			"features": wamp.Dict{
 				"publisher_identification": true,
 			},
 		},
 		"publisher": struct{}{},
 		"callee":    struct{}{},
-		"caller": map[string]interface{}{
-			"features": map[string]interface{}{
+		"caller": wamp.Dict{
+			"features": wamp.Dict{
 				"call_timeout": true,
 			},
 		},
@@ -191,7 +191,7 @@ func TestPublishAcknowledge(t *testing.T) {
 	id := wamp.GlobalID()
 	client.Send(&wamp.Publish{
 		Request: id,
-		Options: map[string]interface{}{"acknowledge": true},
+		Options: wamp.Dict{"acknowledge": true},
 		Topic:   "some.uri"})
 
 	select {
@@ -223,7 +223,7 @@ func TestPublishFalseAcknowledge(t *testing.T) {
 	id := wamp.GlobalID()
 	client.Send(&wamp.Publish{
 		Request: id,
-		Options: map[string]interface{}{"acknowledge": false},
+		Options: wamp.Dict{"acknowledge": false},
 		Topic:   "some.uri"})
 
 	select {
@@ -404,7 +404,7 @@ func TestSessionMetaProcedures(t *testing.T) {
 	caller.Send(&wamp.Call{
 		Request:   callID,
 		Procedure: wamp.MetaProcSessionGet,
-		Arguments: []interface{}{wamp.ID(123456789)},
+		Arguments: wamp.List{wamp.ID(123456789)},
 	})
 	select {
 	case <-time.After(time.Second):
@@ -424,7 +424,7 @@ func TestSessionMetaProcedures(t *testing.T) {
 	caller.Send(&wamp.Call{
 		Request:   callID,
 		Procedure: wamp.MetaProcSessionGet,
-		Arguments: []interface{}{sessID},
+		Arguments: wamp.List{sessID},
 	})
 	select {
 	case <-time.After(time.Second):
@@ -441,7 +441,7 @@ func TestSessionMetaProcedures(t *testing.T) {
 	if len(result.Arguments) == 0 {
 		t.Fatal("missing expected arguemnt")
 	}
-	dict, ok := result.Arguments[0].(map[string]interface{})
+	dict, ok := result.Arguments[0].(wamp.Dict)
 	if !ok {
 		t.Fatal("expected dict type arg")
 	}
@@ -484,9 +484,9 @@ func TestRegistrationMetaProcedures(t *testing.T) {
 	if len(result.Arguments) == 0 {
 		t.Fatal("missing expected arguemnt")
 	}
-	dict, ok := result.Arguments[0].(map[string]interface{})
+	dict, ok := result.Arguments[0].(wamp.Dict)
 	if !ok {
-		t.Fatal("expected map[string]interface{}")
+		t.Fatal("expected wamp.Dict")
 	}
 	exactPrev, ok := dict["exact"].([]wamp.ID)
 	if !ok {
@@ -529,7 +529,7 @@ func TestRegistrationMetaProcedures(t *testing.T) {
 	callee.Send(&wamp.Register{
 		Request:   wamp.GlobalID(),
 		Procedure: testProcedureWC,
-		Options:   map[string]interface{}{"match": "wildcard"},
+		Options:   wamp.Dict{"match": "wildcard"},
 	})
 	msg := <-callee.Recv()
 	if _, ok := msg.(*wamp.Registered); !ok {
@@ -554,9 +554,9 @@ func TestRegistrationMetaProcedures(t *testing.T) {
 	if len(result.Arguments) == 0 {
 		t.Fatal("missing expected arguemnt")
 	}
-	dict, ok = result.Arguments[0].(map[string]interface{})
+	dict, ok = result.Arguments[0].(wamp.Dict)
 	if !ok {
-		t.Fatal("expected map[string]interface{}")
+		t.Fatal("expected wamp.Dict")
 	}
 	exact := dict["exact"].([]wamp.ID)
 	prefix := dict["prefix"].([]wamp.ID)
@@ -588,7 +588,7 @@ func TestRegistrationMetaProcedures(t *testing.T) {
 	caller.Send(&wamp.Call{
 		Request:   callID,
 		Procedure: wamp.MetaProcRegLookup,
-		Arguments: []interface{}{testProcedure},
+		Arguments: wamp.List{testProcedure},
 	})
 	select {
 	case <-time.After(time.Second):
@@ -618,7 +618,7 @@ func TestRegistrationMetaProcedures(t *testing.T) {
 	caller.Send(&wamp.Call{
 		Request:   callID,
 		Procedure: wamp.MetaProcRegMatch,
-		Arguments: []interface{}{testProcedure},
+		Arguments: wamp.List{testProcedure},
 	})
 	select {
 	case <-time.After(time.Second):
@@ -648,7 +648,7 @@ func TestRegistrationMetaProcedures(t *testing.T) {
 	caller.Send(&wamp.Call{
 		Request:   callID,
 		Procedure: wamp.MetaProcRegGet,
-		Arguments: []interface{}{registrationID},
+		Arguments: wamp.List{registrationID},
 	})
 	select {
 	case <-time.After(time.Second):
@@ -665,9 +665,9 @@ func TestRegistrationMetaProcedures(t *testing.T) {
 	if len(result.Arguments) == 0 {
 		t.Fatal("missing expected arguemnt")
 	}
-	dict, ok = result.Arguments[0].(map[string]interface{})
+	dict, ok = result.Arguments[0].(wamp.Dict)
 	if !ok {
-		t.Fatal("expected map[string]interface{}")
+		t.Fatal("expected wamp.Dict")
 	}
 	regID = wamp.OptionID(dict, "id")
 	if regID != registrationID {
@@ -683,7 +683,7 @@ func TestRegistrationMetaProcedures(t *testing.T) {
 	caller.Send(&wamp.Call{
 		Request:   callID,
 		Procedure: wamp.MetaProcRegListCallees,
-		Arguments: []interface{}{registrationID},
+		Arguments: wamp.List{registrationID},
 	})
 	select {
 	case <-time.After(time.Second):
@@ -716,7 +716,7 @@ func TestRegistrationMetaProcedures(t *testing.T) {
 	caller.Send(&wamp.Call{
 		Request:   callID,
 		Procedure: wamp.MetaProcRegCountCallees,
-		Arguments: []interface{}{registrationID},
+		Arguments: wamp.List{registrationID},
 	})
 	select {
 	case <-time.After(time.Second):
diff --git a/server/websocketserver_test.go b/server/websocketserver_test.go
index 4eb1ec2..debabeb 100644
--- a/server/websocketserver_test.go
+++ b/server/websocketserver_test.go
@@ -30,23 +30,24 @@ var (
 	}
 )
 
-func clientRoles() map[string]interface{} {
-	return map[string]interface{}{
-		"roles": map[string]interface{}{
-			"publisher": map[string]interface{}{
-				"features": map[string]interface{}{
+func clientRoles() wamp.Dict {
+	return wamp.Dict{
+		"roles": wamp.Dict{
+			"publisher": wamp.Dict{
+				"features": wamp.Dict{
 					"subscriber_blackwhite_listing": true,
 				},
 			},
-			"subscriber": map[string]interface{}{},
-			"callee":     map[string]interface{}{},
-			"caller":     map[string]interface{}{},
+			"subscriber": wamp.Dict{},
+			"callee":     wamp.Dict{},
+			"caller":     wamp.Dict{},
 		},
 	}
 }
 
-func newTestWebsocketServer(t *testing.T) (int, router.Router, io.Closer) {
+func newTestWebsocketServer(t *testing.T) (int, io.Closer) {
 	r, err := router.NewRouter(routerConfig)
+	router.DebugEnabled = true
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -62,11 +63,11 @@ func newTestWebsocketServer(t *testing.T) (int, router.Router, io.Closer) {
 		t.Fatal(err)
 	}
 	go server.Serve(l)
-	return l.Addr().(*net.TCPAddr).Port, r, l
+	return l.Addr().(*net.TCPAddr).Port, l
 }
 
 func TestWSHandshakeJSON(t *testing.T) {
-	port, r, closer := newTestWebsocketServer(t)
+	port, closer := newTestWebsocketServer(t)
 	defer closer.Close()
 
 	client, err := transport.ConnectWebsocketPeer(
@@ -76,8 +77,6 @@ func TestWSHandshakeJSON(t *testing.T) {
 	}
 
 	client.Send(&wamp.Hello{Realm: testRealm, Details: clientRoles()})
-	go r.Attach(client)
-
 	msg, ok := <-client.Recv()
 	if !ok {
 		t.Fatal("recv chan closed")
@@ -86,10 +85,11 @@ func TestWSHandshakeJSON(t *testing.T) {
 	if _, ok = msg.(*wamp.Welcome); !ok {
 		t.Fatal("expected WELCOME, got", msg.MessageType())
 	}
+	client.Close()
 }
 
 func TestWSHandshakeMsgpack(t *testing.T) {
-	port, r, closer := newTestWebsocketServer(t)
+	port, closer := newTestWebsocketServer(t)
 	defer closer.Close()
 
 	client, err := transport.ConnectWebsocketPeer(
@@ -99,8 +99,6 @@ func TestWSHandshakeMsgpack(t *testing.T) {
 	}
 
 	client.Send(&wamp.Hello{Realm: testRealm, Details: clientRoles()})
-	go r.Attach(client)
-
 	msg, ok := <-client.Recv()
 	if !ok {
 		t.Fatal("Receive buffer closed")
@@ -109,4 +107,5 @@ func TestWSHandshakeMsgpack(t *testing.T) {
 	if _, ok = msg.(*wamp.Welcome); !ok {
 		t.Fatalf("expected WELCOME, got %s: %+v", msg.MessageType(), msg)
 	}
+	client.Close()
 }
diff --git a/transport/serialize/serialize_test.go b/transport/serialize/serialize_test.go
index 6124317..1261389 100644
--- a/transport/serialize/serialize_test.go
+++ b/transport/serialize/serialize_test.go
@@ -11,27 +11,27 @@ import (
 	"github.com/gammazero/nexus/wamp"
 )
 
-func hasRole(details map[string]interface{}, role string) bool {
+func hasRole(details wamp.Dict, role string) bool {
 	_, err := wamp.DictValue(details, []string{"roles", role})
 	return err == nil
 }
 
-func hasFeature(details map[string]interface{}, role, feature string) bool {
+func hasFeature(details wamp.Dict, role, feature string) bool {
 	b, _ := wamp.DictFlag(details, []string{"roles", role, "features", feature})
 	return b
 }
 
-func detailRolesFeatures() map[string]interface{} {
-	return map[string]interface{}{
-		"roles": map[string]interface{}{
-			"publisher": map[string]interface{}{
-				"features": map[string]interface{}{
+func detailRolesFeatures() wamp.Dict {
+	return wamp.Dict{
+		"roles": wamp.Dict{
+			"publisher": wamp.Dict{
+				"features": wamp.Dict{
 					"subscriber_blackwhite_listing": true,
 				},
 			},
-			"subscriber": map[string]interface{}{},
-			"callee":     map[string]interface{}{},
-			"caller":     map[string]interface{}{},
+			"subscriber": wamp.Dict{},
+			"callee":     wamp.Dict{},
+			"caller":     wamp.Dict{},
 		},
 	}
 }
@@ -65,7 +65,7 @@ func TestJSONDeserialize(t *testing.T) {
 	s := &JSONSerializer{}
 
 	data := `[1,"nexus.realm",{}]`
-	expect := &wamp.Hello{Realm: "nexus.realm", Details: map[string]interface{}{}}
+	expect := &wamp.Hello{Realm: "nexus.realm", Details: wamp.Dict{}}
 	msg, err := s.Deserialize([]byte(data))
 	if err != nil {
 		t.Fatalf("Error decoding good data: %s, %s", err, data)
@@ -106,7 +106,7 @@ func TestMessagePackDeserialize(t *testing.T) {
 	s := &MessagePackSerializer{}
 
 	data := []byte{0x93, 0x01, 0xab, 0x6e, 0x65, 0x78, 0x75, 0x73, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x80}
-	expect := &wamp.Hello{Realm: "nexus.realm", Details: map[string]interface{}{}}
+	expect := &wamp.Hello{Realm: "nexus.realm", Details: wamp.Dict{}}
 	msg, err := s.Deserialize(data)
 	if err != nil {
 		t.Fatalf("Error decoding good data: %s, %x", err, data)
@@ -150,7 +150,7 @@ func TestAssignSlice(t *testing.T) {
 	pubArgs := []string{"hello", "nexus", "wamp", "router"}
 
 	// Deserializing a slice into a message.
-	elems := []interface{}{msgType, 123, map[string]interface{}{},
+	elems := wamp.List{msgType, 123, wamp.Dict{},
 		"some.valid.topic", pubArgs}
 	msg, err := listToMsg(msgType, elems)
 	if err != nil {
@@ -175,7 +175,7 @@ func TestAssignSlice(t *testing.T) {
 }
 
 func TestMsgToList(t *testing.T) {
-	testMsgToList := func(args []interface{}, kwArgs map[string]interface{}, omit int, message string) error {
+	testMsgToList := func(args wamp.List, kwArgs wamp.Dict, omit int, message string) error {
 		msg := &wamp.Event{Subscription: 0, Publication: 0, Details: nil, Arguments: args, ArgumentsKw: kwArgs}
 		numField := reflect.ValueOf(msg).Elem().NumField() + 1 // +1 for type
 		expect := numField - omit
@@ -193,36 +193,36 @@ func TestMsgToList(t *testing.T) {
 		t.Error(err.Error())
 	}
 
-	err = testMsgToList([]interface{}{}, make(map[string]interface{}), 2,
+	err = testMsgToList(wamp.List{}, make(wamp.Dict), 2,
 		"empty args, empty kwArgs")
 	if err != nil {
 		t.Error(err.Error())
 	}
 
-	err = testMsgToList([]interface{}{1}, nil, 1, "non-empty args, nil kwArgs")
+	err = testMsgToList(wamp.List{1}, nil, 1, "non-empty args, nil kwArgs")
 	if err != nil {
 		t.Error(err.Error())
 	}
 
-	err = testMsgToList(nil, map[string]interface{}{"a": nil}, 0,
+	err = testMsgToList(nil, wamp.Dict{"a": nil}, 0,
 		"nil args, non-empty kwArgs")
 	if err != nil {
 		t.Error(err.Error())
 	}
 
-	err = testMsgToList([]interface{}{1}, make(map[string]interface{}), 1,
+	err = testMsgToList(wamp.List{1}, make(wamp.Dict), 1,
 		"non-empty args, empty kwArgs")
 	if err != nil {
 		t.Error(err.Error())
 	}
 
-	err = testMsgToList([]interface{}{}, map[string]interface{}{"a": nil}, 0,
+	err = testMsgToList(wamp.List{}, wamp.Dict{"a": nil}, 0,
 		"empty args, non-empty kwArgs")
 	if err != nil {
 		t.Error(err.Error())
 	}
 
-	err = testMsgToList([]interface{}{1}, map[string]interface{}{"a": nil}, 0,
+	err = testMsgToList(wamp.List{1}, wamp.Dict{"a": nil}, 0,
 		"test message one")
 	if err != nil {
 		t.Error(err.Error())
diff --git a/wamp/dict_test.go b/wamp/dict_test.go
index 18dccbc..e878a5c 100644
--- a/wamp/dict_test.go
+++ b/wamp/dict_test.go
@@ -5,17 +5,17 @@ import (
 	"testing"
 )
 
-func hasRole(d map[string]interface{}, role string) bool {
+func hasRole(d Dict, role string) bool {
 	_, err := DictValue(d, []string{"roles", role})
 	return err == nil
 }
 
-func hasFeature(d map[string]interface{}, role, feature string) bool {
+func hasFeature(d Dict, role, feature string) bool {
 	b, _ := DictFlag(d, []string{"roles", role, "features", feature})
 	return b
 }
 
-func checkRoles(dict map[string]interface{}) error {
+func checkRoles(dict Dict) error {
 	if !hasRole(dict, "caller") {
 		return errors.New("session does not have caller role")
 	}
@@ -47,15 +47,15 @@ func recognizeRole(roleName string) bool {
 }
 
 func TestHasRoleFeatureLookup(t *testing.T) {
-	dict := map[string]interface{}{}
-	clientRoles := map[string]map[string]interface{}{
-		"publisher": map[string]interface{}{},
-		"subscriber": map[string]interface{}{
+	dict := Dict{}
+	clientRoles := map[string]Dict{
+		"publisher": Dict{},
+		"subscriber": Dict{
 			"junk": struct{}{}},
-		"callee": map[string]interface{}{
+		"callee": Dict{
 			"Hello": "world"},
-		"caller":  map[string]interface{}{},
-		"badrole": map[string]interface{}{},
+		"caller":  Dict{},
+		"badrole": Dict{},
 	}
 	boolMap := map[string]bool{"call_timeout": true}
 	clientRoles["caller"]["features"] = boolMap
@@ -71,7 +71,7 @@ func TestHasRoleFeatureLookup(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	for k, _ := range roleVals.(map[string]interface{}) {
+	for k, _ := range roleVals.(Dict) {
 		if k == "badrole" {
 			if recognizeRole(k) {
 				t.Fatal("badrole is recognized")
@@ -88,17 +88,17 @@ func TestHasRoleFeatureLookup(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	dict = map[string]interface{}{
-		"roles": map[string]interface{}{
-			"subscriber": map[string]interface{}{
-				"features": map[string]interface{}{
+	dict = Dict{
+		"roles": Dict{
+			"subscriber": Dict{
+				"features": Dict{
 					"publisher_identification": true,
 				},
 			},
 			"publisher": struct{}{},
 			"callee":    struct{}{},
-			"caller": map[string]interface{}{
-				"features": map[string]interface{}{
+			"caller": Dict{
+				"features": Dict{
 					"call_timeout": true,
 				},
 			},
@@ -111,7 +111,7 @@ func TestHasRoleFeatureLookup(t *testing.T) {
 }
 
 func TestOptions(t *testing.T) {
-	options := map[string]interface{}{
+	options := Dict{
 		"disclose_me":  true,
 		"call_timeout": 5000,
 		"mode":         "killnowait",
@@ -157,14 +157,14 @@ func TestOptions(t *testing.T) {
 }
 
 func BenchmarkNormalized(b *testing.B) {
-	dict := map[string]interface{}{}
-	clientRoles := map[string]map[string]interface{}{
-		"publisher": map[string]interface{}{},
-		"subscriber": map[string]interface{}{
+	dict := Dict{}
+	clientRoles := map[string]Dict{
+		"publisher": Dict{},
+		"subscriber": Dict{
 			"junk": struct{}{}},
-		"callee": map[string]interface{}{
+		"callee": Dict{
 			"Hello": "world"},
-		"caller": map[string]interface{}{},
+		"caller": Dict{},
 	}
 	boolMap := map[string]bool{"call_timeout": true}
 	clientRoles["caller"]["features"] = boolMap
@@ -178,14 +178,14 @@ func BenchmarkNormalized(b *testing.B) {
 }
 
 func BenchmarkNotNormalized(b *testing.B) {
-	dict := map[string]interface{}{}
-	clientRoles := map[string]map[string]interface{}{
-		"publisher": map[string]interface{}{},
-		"subscriber": map[string]interface{}{
+	dict := Dict{}
+	clientRoles := map[string]Dict{
+		"publisher": Dict{},
+		"subscriber": Dict{
 			"junk": struct{}{}},
-		"callee": map[string]interface{}{
+		"callee": Dict{
 			"Hello": "world"},
-		"caller": map[string]interface{}{},
+		"caller": Dict{},
 	}
 	boolMap := map[string]bool{"call_timeout": true}
 	clientRoles["caller"]["features"] = boolMap