Skip to content

Commit

Permalink
Refactor and add unit tests (#422)
Browse files Browse the repository at this point in the history
  • Loading branch information
Danielius1922 authored Jan 19, 2024
1 parent 7299a45 commit 2a55444
Show file tree
Hide file tree
Showing 13 changed files with 644 additions and 136 deletions.
63 changes: 63 additions & 0 deletions bridge/net/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/****************************************************************************
*
* Copyright (c) 2024 plgn.dev s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License"),
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implien. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************/

package net

import (
"fmt"
gonet "net"
"strconv"
)

type Config struct {
ExternalAddress string `yaml:"externalAddress"`
MaxMessageSize uint32 `yaml:"maxMessageSize"`
externalAddressPort string `yaml:"-"`
}

const DefaultMaxMessageSize = 2 * 1024 * 1024

func (cfg *Config) ExternalAddressPort() string {
return cfg.externalAddressPort
}

func (cfg *Config) Validate() error {
if cfg.ExternalAddress == "" {
return fmt.Errorf("externalAddress is required")
}
host, portStr, err := gonet.SplitHostPort(cfg.ExternalAddress)
if err != nil {
return fmt.Errorf("invalid externalAddress: %w", err)
}
if host == "" {
return fmt.Errorf("invalid externalAddress: host cannot be empty")
}
port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return fmt.Errorf("invalid externalAddress: %w", err)
}
if port == 0 {
return fmt.Errorf("invalid externalAddress: port cannot be 0")
}
if cfg.MaxMessageSize == 0 {
cfg.MaxMessageSize = DefaultMaxMessageSize
}

cfg.externalAddressPort = portStr
return nil
}
103 changes: 103 additions & 0 deletions bridge/net/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/****************************************************************************
*
* Copyright (c) 2024 plgn.dev s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License"),
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implien. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************/

package net_test

import (
"testing"

"github.com/plgd-dev/device/v2/bridge/net"
"github.com/stretchr/testify/require"
)

func TestConfigValidate(t *testing.T) {
type data struct {
maxMsgSize uint32
externalAddress string
externalAddressPort string
}
tests := []struct {
name string
config *net.Config
wantErr bool
want data
}{
{
name: "ValidConfig",
config: &net.Config{ExternalAddress: "localhost:12345", MaxMessageSize: 1024},
want: data{
maxMsgSize: 1024,
externalAddress: "localhost:12345",
externalAddressPort: "12345",
},
},
{
name: "ValidConfigWithDefaultMaxMessageSize",
config: &net.Config{ExternalAddress: "localhost:12345"},
want: data{
maxMsgSize: net.DefaultMaxMessageSize,
externalAddress: "localhost:12345",
externalAddressPort: "12345",
},
},
{
name: "MissingExternalAddress",
config: &net.Config{},
wantErr: true,
},
{
name: "InvalidExternalAddress",
config: &net.Config{ExternalAddress: "invalid-address"},
wantErr: true,
},
{
name: "EmptyHostInExternalAddress",
config: &net.Config{ExternalAddress: ":12345"},
wantErr: true,
},
{
name: "ZeroPortInExternalAddress",
config: &net.Config{ExternalAddress: "localhost:0"},
wantErr: true,
},
{
name: "InvalidPortInExternalAddress",
config: &net.Config{ExternalAddress: "localhost:invalid"},
wantErr: true,
},
{
name: "PortGreaterThanMaxUint16",
config: &net.Config{ExternalAddress: "localhost:65536"},
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.config.Validate()
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tt.want.maxMsgSize, tt.config.MaxMessageSize)
require.Equal(t, tt.want.externalAddress, tt.config.ExternalAddress)
require.Equal(t, tt.want.externalAddressPort, tt.config.ExternalAddressPort())
})
}
}
98 changes: 0 additions & 98 deletions bridge/net/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ import (
"io"
"log"
gonet "net"
"strconv"
"strings"

"github.com/google/uuid"
"github.com/plgd-dev/device/v2/pkg/codec/cbor"
"github.com/plgd-dev/device/v2/pkg/codec/json"
"github.com/plgd-dev/device/v2/schema"
Expand All @@ -43,14 +40,6 @@ import (
"github.com/plgd-dev/go-coap/v3/udp/server"
)

type Config struct {
ExternalAddress string `yaml:"externalAddress"`
MaxMessageSize uint32 `yaml:"maxMessageSize"`
externalAddressPort string `yaml:"-"`
}

type RequestHandler func(req *Request) (*pool.Message, error)

type Net struct {
cfg Config
listener *net.UDPConn
Expand All @@ -62,34 +51,6 @@ type Net struct {
mux *mux.Router
}

const DefaultMaxMessageSize = 2 * 1024 * 1024

func (cfg *Config) Validate() error {
if cfg.ExternalAddress == "" {
return fmt.Errorf("externalAddress is required")
}
host, portStr, err := gonet.SplitHostPort(cfg.ExternalAddress)
if err != nil {
return fmt.Errorf("invalid externalAddress: %w", err)
}
if host == "" {
return fmt.Errorf("invalid externalAddress: host cannot be empty")
}
port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return fmt.Errorf("invalid externalAddress: %w", err)
}
if port == 0 {
return fmt.Errorf("invalid externalAddress: port cannot be 0")
}
if cfg.MaxMessageSize == 0 {
cfg.MaxMessageSize = DefaultMaxMessageSize
}

cfg.externalAddressPort = portStr
return nil
}

// TODO: ipv6 + ipv6 multicast addresses
func initConnectivity(listenAddress string) (*net.UDPConn, *net.UDPConn, error) {
multicastAddr := "224.0.1.187:5683"
Expand Down Expand Up @@ -213,65 +174,6 @@ func LoggingMiddleware(next mux.Handler) mux.Handler {
})
}

type Request struct {
*pool.Message
Conn mux.Conn
Endpoints schema.Endpoints
}

func (r *Request) Interface() string {
q, err := r.Queries()
if err != nil {
return ""
}
for _, query := range q {
if strings.HasPrefix(query, "if=") {
return strings.TrimPrefix(query, "if=")
}
}
return ""
}

func (r *Request) URIPath() string {
p, err := r.Message.Options().Path()
if err != nil {
return ""
}
return p
}

func (r *Request) DeviceID() uuid.UUID {
q, err := r.Queries()
if err != nil {
return uuid.Nil
}
for _, query := range q {
if strings.HasPrefix(query, "di=") {
deviceID := strings.TrimPrefix(query, "di=")
di, err := uuid.Parse(deviceID)
if err != nil {
return uuid.Nil
}
return di
}
}
return uuid.Nil
}

func (r *Request) ResourceTypes() []string {
q, err := r.Queries()
if err != nil {
return nil
}
resourceTypes := make([]string, 0, len(q))
for _, query := range q {
if strings.HasPrefix(query, "rt=") {
resourceTypes = append(resourceTypes, strings.TrimPrefix(query, "rt="))
}
}
return resourceTypes
}

func (n *Net) ServeCOAP(w mux.ResponseWriter, request *mux.Message) {
request.Hijack()
go func(w mux.ResponseWriter, request *mux.Message) {
Expand Down
95 changes: 95 additions & 0 deletions bridge/net/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/****************************************************************************
*
* Copyright (c) 2023 plgn.dev s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License"),
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implien. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************/

package net

import (
"errors"
"strings"

"github.com/google/uuid"
"github.com/plgd-dev/device/v2/schema"
"github.com/plgd-dev/go-coap/v3/message/pool"
"github.com/plgd-dev/go-coap/v3/mux"
)

type Request struct {
*pool.Message
Conn mux.Conn
Endpoints schema.Endpoints
}

type RequestHandler func(req *Request) (*pool.Message, error)

var ErrKeyNotFound = errors.New("key not found")

func (r *Request) GetValueFromQuery(key string) (string, error) {
q, err := r.Queries()
if err != nil {
return "", err
}
prefix := key + "="
for _, query := range q {
if strings.HasPrefix(query, prefix) {
return strings.TrimPrefix(query, prefix), nil
}
}
return "", ErrKeyNotFound
}

func (r *Request) URIPath() string {
p, err := r.Message.Options().Path()
if err != nil {
return ""
}
return p
}

func (r *Request) Interface() string {
v, err := r.GetValueFromQuery("if")
if err != nil {
return ""
}
return v
}

func (r *Request) DeviceID() uuid.UUID {
v, err := r.GetValueFromQuery("di")
if err != nil {
return uuid.Nil
}
di, err := uuid.Parse(v)
if err != nil {
return uuid.Nil
}
return di
}

func (r *Request) ResourceTypes() []string {
q, err := r.Queries()
if err != nil {
return nil
}
resourceTypes := make([]string, 0, len(q))
for _, query := range q {
if strings.HasPrefix(query, "rt=") {
resourceTypes = append(resourceTypes, strings.TrimPrefix(query, "rt="))
}
}
return resourceTypes
}
Loading

0 comments on commit 2a55444

Please sign in to comment.