Skip to content

Commit

Permalink
Wrap errors (#167)
Browse files Browse the repository at this point in the history
* Wrap errors
* Add error type checks
  • Loading branch information
at-wat authored Nov 6, 2020
1 parent 452aa6e commit 167c650
Show file tree
Hide file tree
Showing 25 changed files with 1,455 additions and 294 deletions.
4 changes: 3 additions & 1 deletion awsiotdev.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package awsiotdev

import (
"github.com/at-wat/mqtt-go"

"github.com/seqsense/aws-iot-device-sdk-go/v4/internal/ioterr"
)

// Device is an AWS IoT device.
Expand All @@ -34,7 +36,7 @@ type device struct {
func New(thingName string, dialer mqtt.Dialer, opts ...mqtt.ReconnectOption) (Device, error) {
cli, err := mqtt.NewReconnectClient(dialer, opts...)
if err != nil {
return nil, err
return nil, ioterr.New(err, "creating MQTT connection")
}
return &device{
Client: cli,
Expand Down
11 changes: 6 additions & 5 deletions dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"github.com/aws/aws-sdk-go/aws/client"

"github.com/at-wat/mqtt-go"

"github.com/seqsense/aws-iot-device-sdk-go/v4/internal/ioterr"
presigner "github.com/seqsense/aws-iot-device-sdk-go/v4/presigner"
)

Expand All @@ -39,14 +41,13 @@ func NewPresignDialer(sess client.ConfigProvider, endpoint string, opts ...mqtt.
}

func (d *presignDialer) Dial() (mqtt.ClientCloser, error) {
// Presign URL here.
url, err := d.signer.PresignWssNow(d.endpoint)
if err != nil {
return nil, err
return nil, ioterr.New(err, "presigning wss URL")
}
cli, err := mqtt.Dial(url, d.opts...)
if err != nil {
return nil, err
return nil, ioterr.New(err, "dialing")
}
return cli, nil
}
Expand All @@ -56,14 +57,14 @@ func (d *presignDialer) Dial() (mqtt.ClientCloser, error) {
func NewDialer(sess client.ConfigProvider, urlStr string, opts ...mqtt.DialOption) (mqtt.Dialer, error) {
u, err := url.Parse(urlStr)
if err != nil {
return nil, err
return nil, ioterr.New(err, "parsing server URL")
}
switch u.Scheme {
case "mqtts":
return &mqtt.URLDialer{URL: urlStr, Options: opts}, nil
case "wss":
return NewPresignDialer(sess, u.Host)
default:
return nil, mqtt.ErrUnsupportedProtocol
return nil, ioterr.New(mqtt.ErrUnsupportedProtocol, "new dialer")
}
}
201 changes: 172 additions & 29 deletions dialer_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
package awsiotdev

import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"io/ioutil"
"math/big"
"net"
"net/http"
"net/url"
"os"
"reflect"
"testing"
"time"

"golang.org/x/net/websocket"

"github.com/at-wat/mqtt-go"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/session"

"github.com/seqsense/aws-iot-device-sdk-go/v4/internal/ioterr"
"github.com/seqsense/aws-iot-device-sdk-go/v4/presigner"
)

Expand All @@ -18,37 +37,161 @@ func (*dummyConfigProvider) ClientConfig(serviceName string, cfgs ...*aws.Config
}

func TestNewDialer(t *testing.T) {
cases := map[string]struct {
url string
dialer interface{}
err error
}{
"MQTTs": {
url: "mqtts://hoge.foo:1234",
dialer: &mqtt.URLDialer{URL: "mqtts://hoge.foo:1234"},
},
"WebSockets": {
url: "wss://hoge.foo:1234/ep",
dialer: &presignDialer{
signer: &presigner.Presigner{},
endpoint: "hoge.foo:1234",
t.Run("ValidURL", func(t *testing.T) {
cases := map[string]struct {
url string
dialer interface{}
err error
}{
"MQTTs": {
url: "mqtts://hoge.foo:1234",
dialer: &mqtt.URLDialer{URL: "mqtts://hoge.foo:1234"},
},
},
"UnknownProtocol": {
url: "unknown://hoge.foo:1234",
err: mqtt.ErrUnsupportedProtocol,
},
"WebSockets": {
url: "wss://hoge.foo:1234/ep",
dialer: &presignDialer{
signer: &presigner.Presigner{},
endpoint: "hoge.foo:1234",
},
},
"UnknownProtocol": {
url: "unknown://hoge.foo:1234",
err: mqtt.ErrUnsupportedProtocol,
},
}
for name, c := range cases {
c := c
t.Run(name, func(t *testing.T) {
d, err := NewDialer(&dummyConfigProvider{}, c.url)
if !errors.Is(err, c.err) {
var ie *ioterr.Error
if !errors.As(err, &ie) {
t.Errorf("Expected error type: ioterr.Error, actual: %T", err)
}
t.Fatalf("Expected error: %v, got: %v", c.err, err)
}
if !reflect.DeepEqual(c.dialer, d) {
t.Errorf("Expected dialer: %v, got: %v", c.dialer, d)
}
})
}
})
t.Run("InvalidURL", func(t *testing.T) {
_, err := NewDialer(&dummyConfigProvider{}, ":aaa")
var ie *ioterr.Error
if !errors.As(err, &ie) {
t.Errorf("Expected error type: %T, actual: %T", ie, err)
}
var ue *url.Error
if !errors.As(err, &ue) {
t.Errorf("Expected error type: %T, actual: %T", ue, err)
}
})
}

func TestPresignDialer(t *testing.T) {
os.Clearenv()
os.Setenv("AWS_ACCESS_KEY_ID", "AKAAAAAAAAAAAAAAAAAA")
os.Setenv("AWS_SECRET_ACCESS_KEY", "1111111111111111111111111111111111111111")
os.Setenv("AWS_REGION", "world-1")

sess := session.Must(session.NewSession())
ps := presigner.New(sess)

ln, err := net.Listen("tcp", ":0")
if err != nil {
t.Fatal(err)
}
for name, c := range cases {
c := c
t.Run(name, func(t *testing.T) {
d, err := NewDialer(&dummyConfigProvider{}, c.url)
if err != c.err {
t.Fatalf("Expected error: %v, got: %v", c.err, err)
}
if !reflect.DeepEqual(c.dialer, d) {
t.Errorf("Expected dialer: %v, got: %v", c.dialer, d)
wsConfig, err := websocket.NewConfig("wss://", "wss://")
if err != nil {
t.Fatal(err)
}
wsSrv := websocket.Server{
Config: *wsConfig,
Handler: func(c *websocket.Conn) {},
}
chAccept := make(chan bool, 1)
srv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !<-chAccept {
http.NotFound(w, r)
return
}
})
wsSrv.ServeHTTP(w, r)
}),
}
cert, priv, err := generateSelfSignedCert()
if err != nil {
t.Fatal(err)
}
go srv.ServeTLS(ln, cert, priv)
defer srv.Shutdown(context.Background())

d := &presignDialer{
signer: ps,
endpoint: ln.Addr().String(),
opts: []mqtt.DialOption{mqtt.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})},
}

t.Run("Success", func(t *testing.T) {
chAccept <- true
conn, err := d.Dial()
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
_ = conn.Close()
})
t.Run("Error", func(t *testing.T) {
chAccept <- false
conn, err := d.Dial()
if err == nil {
_ = conn.Close()
t.Fatalf("Dial should fail")
}
var ie *ioterr.Error
if !errors.As(err, &ie) {
t.Fatalf("Expected error type: %T, actual: %T", ie, err)
}
})
}

func generateSelfSignedCert() (string, string, error) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return "", "", err
}
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Organization: []string{"Foo"},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Hour),
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
der, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
return "", "", err
}
certFile, err := ioutil.TempFile("", "*.pub")
if err != nil {
return "", "", err
}
privFile, err := ioutil.TempFile("", "*.key")
if err != nil {
return "", "", err
}
if err := pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: der}); err != nil {
return "", "", err
}
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
if err != nil {
return "", "", err
}
if err := pem.Encode(privFile, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil {
return "", "", err
}
return certFile.Name(), privFile.Name(), nil
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/seqsense/aws-iot-device-sdk-go/v4
require (
github.com/at-wat/mqtt-go v0.11.0
github.com/aws/aws-sdk-go v1.35.19
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.4.3
github.com/google/uuid v1.1.2
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102
Expand Down
10 changes: 2 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
Expand All @@ -19,17 +21,14 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0 h1:aRz0NBceriICVtjhCgKkDvl+RudKu1CT6h0ZvUTrNfE=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
Expand All @@ -55,9 +54,7 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
Expand All @@ -70,7 +67,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
Expand All @@ -93,10 +89,8 @@ google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLY
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
Expand Down
Loading

0 comments on commit 167c650

Please sign in to comment.