Skip to content

Commit

Permalink
feat(core): add bridge impl
Browse files Browse the repository at this point in the history
  • Loading branch information
shivanshkc committed Jul 2, 2022
1 parent 7ed8fbc commit 1235dd5
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.17
require (
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0
github.com/spf13/viper v1.12.0
go.uber.org/zap v1.21.0
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
Expand Down
119 changes: 119 additions & 0 deletions src/bridges/bridge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package bridges

import (
"context"
"encoding/json"
"fmt"

"github.com/shivanshkc/rosenbridge/src/core"
"github.com/shivanshkc/rosenbridge/src/logger"
"github.com/shivanshkc/rosenbridge/src/utils/errutils"
"github.com/shivanshkc/rosenbridge/src/utils/miscutils"

"github.com/gorilla/websocket"
)

// bridge implements the core.Bridge interface.
type bridge struct {
// identity is for the identification of the bridge.
identity *core.BridgeIdentity

// underlyingConnection is the low-level websocket connection object.
underlyingConnection *websocket.Conn

// closeHandler handles connection closures.
closeHandler func(err error)
// errorHandler handles any errors in connection.
errorHandler func(err error)
// messageHandler handles messages from client.
messageHandler func(message *core.BridgeMessage)
}

func (b *bridge) Identify() *core.BridgeIdentity {
return b.identity
}

func (b *bridge) SendMessage(ctx context.Context, message *core.BridgeMessage) error {
// Converting the message to byte slice.
messageBytes, err := miscutils.Interface2Bytes(message)
if err != nil {
return fmt.Errorf("error in miscutils.Interface2Bytes call: %w", err)
}

// Writing the message byte slice to the websocket.
if err := b.underlyingConnection.WriteMessage(websocket.TextMessage, messageBytes); err != nil {
return fmt.Errorf("error in underlyingConnection.WriteMessage call: %w", err)
}

return nil
}

func (b *bridge) SetMessageHandler(handler func(message *core.BridgeMessage)) {
// This nil check makes sure that the handler is never nil and can be called without a tedious nil check.
if handler != nil {
b.messageHandler = handler
}
}

func (b *bridge) SetCloseHandler(handler func(err error)) {
// This nil check makes sure that the handler is never nil and can be called without a tedious nil check.
if handler != nil {
b.closeHandler = handler
}
}

func (b *bridge) SetErrorHandler(handler func(err error)) {
// This nil check makes sure that the handler is never nil and can be called without a tedious nil check.
if handler != nil {
b.errorHandler = handler
}
}

func (b *bridge) Close() error {
if err := b.underlyingConnection.Close(); err != nil {
return fmt.Errorf("error in underlyingConnection.Close() call: %w", err)
}
return nil
}

// listen makes the bridge start listening to messages from the client.
func (b *bridge) listen() {
// Prerequisites.
ctx, log := context.Background(), logger.Get()

for {
wsMessageType, messageBytes, err := b.underlyingConnection.ReadMessage()
if err != nil {
// This log is kept at info level because it logs every time a user disconnects.
log.Info(ctx, &logger.Entry{Payload: fmt.Errorf("ReadMessage error: %w", err)})
// Invoking the closeHandler upon connection closure.
b.closeHandler(err)
return
}

// Handling different websocket message types.
switch wsMessageType {
case websocket.CloseMessage:
// Invoking the closeHandler upon connection closure.
b.closeHandler(err)
return
case websocket.TextMessage:
bMessage := &core.BridgeMessage{}
// Converting the message byte slice into a bridge message.
if err := json.Unmarshal(messageBytes, bMessage); err != nil {
// Creating a bad request error for the client.
err = errutils.BadRequest().WithReasonError(fmt.Errorf("invalid bridge message: %w", err))
// Invoking the error handler.
b.errorHandler(err)
// Continue with the main loop.
continue
}
// Invoking the message handler.
b.messageHandler(bMessage)
case websocket.BinaryMessage:
case websocket.PingMessage:
case websocket.PongMessage:
default:
}
}
}
23 changes: 23 additions & 0 deletions src/utils/miscutils/misc_utils.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package miscutils

import (
"encoding/json"
"fmt"
)

// StringSliceContains returns true if "value" is in "slice".
func StringSliceContains(slice []string, value string) bool {
for _, element := range slice {
Expand All @@ -9,3 +14,21 @@ func StringSliceContains(slice []string, value string) bool {
}
return false
}

// Interface2Bytes converts the provided input to a byte slice.
//
// If the conversion is not possible, it returns a non-nil error.
func Interface2Bytes(input interface{}) ([]byte, error) {
switch asserted := input.(type) {
case []byte:
return asserted, nil
case string:
return []byte(asserted), nil
default:
inputBytes, err := json.Marshal(input)
if err != nil {
return nil, fmt.Errorf("error in json.Marshal call: %w", err)
}
return inputBytes, nil
}
}

0 comments on commit 1235dd5

Please sign in to comment.