Skip to content
This repository has been archived by the owner on May 1, 2020. It is now read-only.

Commit

Permalink
Define webwire client implementation interface
Browse files Browse the repository at this point in the history
Define an abstract implementation interface for a webwire client.
Adjust tests and examples to use the new client interface.
  • Loading branch information
romshark committed Apr 7, 2018
1 parent 64cd600 commit 6c4011d
Show file tree
Hide file tree
Showing 57 changed files with 743 additions and 504 deletions.
16 changes: 13 additions & 3 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ const (
// Client represents an instance of one of the servers clients
type Client struct {
serverAddr string
impl Implementation
status Status
defaultReqTimeout time.Duration
reconnInterval time.Duration
autoconnect int32
hooks Hooks

sessionLock sync.RWMutex
session *webwire.Session
Expand Down Expand Up @@ -75,7 +75,17 @@ type Client struct {
}

// NewClient creates a new client instance.
func NewClient(serverAddress string, opts Options) *Client {
func NewClient(
serverAddress string,
implementation Implementation,
opts Options,
) *Client {
if implementation == nil {
panic(fmt.Errorf(
"A webwire client requires a client implementation, got nil",
))
}

// Prepare configuration
opts.SetDefaults()

Expand All @@ -87,11 +97,11 @@ func NewClient(serverAddress string, opts Options) *Client {
// Initialize new client
newClt := &Client{
serverAddress,
implementation,
StatDisconnected,
opts.DefaultRequestTimeout,
opts.ReconnectionInterval,
autoconnect,
opts.Hooks,

sync.RWMutex{},
nil,
Expand Down
2 changes: 1 addition & 1 deletion client/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (clt *Client) connect() error {
}

// Call hook
clt.hooks.OnDisconnected()
clt.impl.OnDisconnected()

// Try to reconnect if autoconn wasn't disabled.
// reconnect in another goroutine to let this one die
Expand Down
12 changes: 7 additions & 5 deletions client/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (clt *Client) handleSessionCreated(msgPayload webwire.Payload) {
clt.sessionLock.Lock()
clt.session = &session
clt.sessionLock.Unlock()
clt.hooks.OnSessionCreated(&session)
clt.impl.OnSessionCreated(&session)
}

func (clt *Client) handleSessionClosed() {
Expand All @@ -27,7 +27,7 @@ func (clt *Client) handleSessionClosed() {
clt.session = nil
clt.sessionLock.Unlock()

clt.hooks.OnSessionClosed()
clt.impl.OnSessionClosed()
}

func (clt *Client) handleFailure(
Expand Down Expand Up @@ -103,12 +103,14 @@ func (clt *Client) handleMessage(message []byte) error {
)
case webwire.MsgInternalError:
clt.handleInternalError(msg.Identifier())

case webwire.MsgSignalBinary:
clt.hooks.OnServerSignal(msg.Payload)
fallthrough
case webwire.MsgSignalUtf8:
clt.hooks.OnServerSignal(msg.Payload)
fallthrough
case webwire.MsgSignalUtf16:
clt.hooks.OnServerSignal(msg.Payload)
clt.impl.OnSignal(msg.Payload)

case webwire.MsgSessionCreated:
clt.handleSessionCreated(msg.Payload)
case webwire.MsgSessionClosed:
Expand Down
42 changes: 0 additions & 42 deletions client/hooks.go

This file was deleted.

21 changes: 21 additions & 0 deletions client/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package client

import webwire "github.com/qbeon/webwire-go"

// Implementation defines a webwire client implementation interface
type Implementation interface {
// OnDisconnected is invoked when the client is disconnected
// from the server for any reason.
OnDisconnected()

// OnSignal is invoked when the client receives a signal
// from the server
OnSignal(payload webwire.Payload)

// OnSessionCreated is invoked when the client was assigned a new session
OnSessionCreated(*webwire.Session)

// OnSessionClosed is invoked when the client's session was closed
// either by the server or the client itself
OnSessionClosed()
}
40 changes: 22 additions & 18 deletions client/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,39 +22,43 @@ const (

// Options represents the options used during the creation a new client instance
type Options struct {
// Hooks define the callback hook functions provided by the user to define behavior
// on certain events
Hooks Hooks

// DefaultRequestTimeout defines the default request timeout duration used in client.Request
// DefaultRequestTimeout defines the default request timeout duration
// used by client.Request and client.RestoreSession
DefaultRequestTimeout time.Duration

// ReconnectionInterval defines the interval at which autoconnect should poll for a connection.
// If undefined then the default value of 2 seconds is applied
ReconnectionInterval time.Duration

// If autoconnect is enabled, client.Request, client.TimedRequest and client.RestoreSession
// won't immediately return a disconnected error if there's no active connection to the server,
// Autoconnect defines whether the autoconnect feature is to be enabled.
//
// If autoconnect is enabled then client.Request, client.TimedRequest and
// client.RestoreSession won't immediately return a disconnected error
// if there's no active connection to the server,
// instead they will automatically try to reestablish the connection
// before the timeout is triggered and a timeout error is returned.
//
// Autoconnect is enabled by default
Autoconnect OptionToggle
WarnLog io.Writer
ErrorLog io.Writer

// ReconnectionInterval defines the interval at which autoconnect
// should retry connection establishment.
// If undefined then the default value of 2 seconds is applied
ReconnectionInterval time.Duration

// WarnLog defines the warn logging output target
WarnLog io.Writer

// ErrorLog defines the error logging output target
ErrorLog io.Writer
}

// SetDefaults sets default values for undefined required options
func (opts *Options) SetDefaults() {
opts.Hooks.SetDefaults()
if opts.DefaultRequestTimeout < 1 {
opts.DefaultRequestTimeout = 60 * time.Second
}

if opts.Autoconnect == OptUnset {
opts.Autoconnect = OptEnabled
}

if opts.DefaultRequestTimeout < 1 {
opts.DefaultRequestTimeout = 60 * time.Second
}

if opts.ReconnectionInterval < 1 {
opts.ReconnectionInterval = 2 * time.Second
}
Expand Down
13 changes: 8 additions & 5 deletions examples/chatroom/client/authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@ import (
"github.com/qbeon/webwire-go/examples/chatroom/shared"
)

// authenticate tries to login using the password and name from the CLI
func authenticate() {
// Authenticate tries to login using the password and name from the CLI
func (clt *ChatroomClient) Authenticate(login, password string) {
encodedCreds, err := json.Marshal(shared.AuthenticationCredentials{
Name: *username,
Password: *password,
Name: login,
Password: password,
})
if err != nil {
panic(fmt.Errorf("Couldn't marshal credentials: %s", err))
}

_, reqErr := client.Request("auth", webwire.Payload{Data: encodedCreds})
_, reqErr := clt.connection.Request(
"auth",
webwire.Payload{Data: encodedCreds},
)
switch err := reqErr.(type) {
case nil:
break
Expand Down
42 changes: 42 additions & 0 deletions examples/chatroom/client/implementation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"encoding/json"
"fmt"
"log"

webwire "github.com/qbeon/webwire-go"
"github.com/qbeon/webwire-go/examples/chatroom/shared"
)

// OnSessionCreated implements the webwireClient.Implementation interface
// it's invoked when a new session is assigned to the client
func (clt *ChatroomClient) OnSessionCreated(newSession *webwire.Session) {
username := newSession.Info["username"].(string)
log.Printf("Authenticated as %s", username)
}

// OnSignal implements the webwireClient.Implementation interface.
// it's invoked when the client receives a signal from the server
// containing a chatroom message
func (clt *ChatroomClient) OnSignal(message webwire.Payload) {
var msg shared.ChatMessage

// Interpret the message as UTF8 encoded JSON
jsonString, err := message.Utf8()
if err != nil {
log.Printf("Couldn't decode incoming message: %s\n", err)
}

if err := json.Unmarshal([]byte(jsonString), &msg); err != nil {
panic(fmt.Errorf("Failed parsing chat message: %s", err))
}

log.Printf("%s: %s\n", msg.User, msg.Msg)
}

// OnDisconnected implements the wwrclt.Implementation interface
func (clt *ChatroomClient) OnDisconnected() {}

// OnSessionClosed implements the wwrclt.Implementation interface
func (clt *ChatroomClient) OnSessionClosed() {}
51 changes: 30 additions & 21 deletions examples/chatroom/client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,32 @@ import (
"flag"
"time"

webwireClient "github.com/qbeon/webwire-go/client"
wwrclt "github.com/qbeon/webwire-go/client"
)

var client *webwireClient.Client
// ChatroomClient implements the wwrclt.Implementation interface
type ChatroomClient struct {
connection *wwrclt.Client
}

// NewChatroomClient constructs and returns a new chatroom client instance
func NewChatroomClient(serverAddr string) *ChatroomClient {
newChatroomClient := &ChatroomClient{}

// Initialize connection
newChatroomClient.connection = wwrclt.NewClient(
serverAddr,
newChatroomClient,
wwrclt.Options{
// Address of the webwire server
// Default timeout for timed requests
DefaultRequestTimeout: 10 * time.Second,
ReconnectionInterval: 2 * time.Second,
},
)

return newChatroomClient
}

var serverAddr = flag.String("addr", ":9090", "server address")
var password = flag.String("pass", "", "password")
Expand All @@ -17,27 +39,14 @@ func main() {
// Parse command line arguments
flag.Parse()

// Initialize client
client = webwireClient.NewClient(
*serverAddr,
webwireClient.Options{
// Address of the webwire server
Hooks: webwireClient.Hooks{
OnServerSignal: onSignal,
OnSessionCreated: onSessionCreated,
},
// Default timeout for timed requests
DefaultRequestTimeout: 10 * time.Second,
ReconnectionInterval: 2 * time.Second,
},
)
defer client.Close()
// Create a new chatroom client instance including its connection
chatroomClient := NewChatroomClient(*serverAddr)

// Authenticate
// Authenticate if credentials are already provided from the CLI
if *username != "" && *password != "" {
authenticate()
chatroomClient.Authenticate(*username, *password)
}

// Main input loop
mainLoop()
// Start the main loop
chatroomClient.Start()
}
13 changes: 0 additions & 13 deletions examples/chatroom/client/onSessionCreated.go

This file was deleted.

21 changes: 0 additions & 21 deletions examples/chatroom/client/onSignal.go

This file was deleted.

Loading

0 comments on commit 6c4011d

Please sign in to comment.