Skip to content

Commit

Permalink
Pakcage AWS:
Browse files Browse the repository at this point in the history
- add subpackage to sign http.request with AWS V4 signature and parse response based on http.response and given model

Other:
- bump dependencies
  • Loading branch information
nabbar committed Nov 24, 2023
1 parent a82b4db commit 82be8c0
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 4 deletions.
2 changes: 2 additions & 0 deletions aws/aws_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ func WaitMinio(host string) bool {
}
}()

time.Sleep(5 * time.Second)

return err == nil
}

Expand Down
4 changes: 4 additions & 0 deletions aws/configAws/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ func (c *awsModel) GetAccessKey() string {
return c.AccessKey
}

func (c *awsModel) GetSecretKey() string {
return c.SecretKey
}

func (c *awsModel) SetCredentials(accessKey, secretKey string) {
c.AccessKey = accessKey
c.SecretKey = secretKey
Expand Down
4 changes: 4 additions & 0 deletions aws/configCustom/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ func (c *awsModel) GetAccessKey() string {
return c.AccessKey
}

func (c *awsModel) GetSecretKey() string {
return c.SecretKey
}

func (c *awsModel) SetCredentials(accessKey, secretKey string) {
c.AccessKey = accessKey
c.SecretKey = secretKey
Expand Down
78 changes: 78 additions & 0 deletions aws/http/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

package http

import (
"bytes"
"io"
"net/http"
"time"

sdkcrd "github.com/aws/aws-sdk-go/aws/credentials"
sdksv4 "github.com/aws/aws-sdk-go/aws/signer/v4"
)

func CopyReader(r io.Reader) (io.ReadSeekCloser, error) {
var tmp = bytes.NewBuffer(make([]byte, 0))

if _, err := io.Copy(tmp, r); err != nil {
return nil, err
} else {
return &readerCloser{bytes.NewReader(tmp.Bytes())}, nil
}
}

func NewReader(p []byte) io.ReadSeekCloser {
return &readerCloser{bytes.NewReader(p)}
}

type readerCloser struct {
io.ReadSeeker
}

func (r *readerCloser) Close() error {
return nil
}

func Request(req *http.Request, cfg Config, service string) error {
var (
err error
sig = sdksv4.NewSigner(sdkcrd.NewStaticCredentials(cfg.GetAccessKey(), cfg.GetSecretKey(), ""))
)

if req.Body == nil {
_, err = sig.Sign(req, nil, service, cfg.GetRegion(), time.Now())
} else if r, k := req.Body.(io.ReadSeekCloser); k {
_, err = sig.Sign(req, r, service, cfg.GetRegion(), time.Now())
} else if r, err = CopyReader(req.Body); err != nil {
return err
} else {
req.Body = r
_, err = sig.Sign(req, r, service, cfg.GetRegion(), time.Now())
}

return err
}
156 changes: 156 additions & 0 deletions aws/http/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

package http

import (
"bytes"
"encoding/json"
"encoding/xml"
"fmt"
"io"
"mime"
"net/http"
)

var (
ErrInvalidResponse = fmt.Errorf("invalid response")
)

type Config interface {
GetRegion() string
GetAccessKey() string
GetSecretKey() string
}

type ErrorResponse struct {
XMLName xml.Name `xml:"Error"`
Code string `xml:"Code" json:"code"`
Message string `xml:"Message" json:"message"`
RequestID string `xml:"RequestId" json:"requestId"`
}

func (e ErrorResponse) Error() string {
return fmt.Sprintf("request ID %s occure an aws response error %s: %s", e.RequestID, e.Code, e.Message)
}

type ErrorStatus struct {
Status string `xml:"Code" json:"code"`
Message string `xml:"Message" json:"message"`
}

func (e ErrorStatus) Error() string {
return fmt.Sprintf("invalid response status code (%s): %s", e.Status, e.Message)
}

func Response(rsp *http.Response, model any) error {
defer func() {
if rsp != nil && rsp.Body != nil {
_ = rsp.Body.Close()
}
}()

var (
err error
buf = bytes.NewBuffer(make([]byte, 0))
cnj = mime.TypeByExtension(".json")
cnx = mime.TypeByExtension(".xml")
)

if rsp == nil {
return ErrInvalidResponse
} else if rsp.Body != nil {
if _, e := io.Copy(buf, rsp.Body); e != nil {
return e
}
}

if tp := rsp.Header.Get("Content-Type"); tp == cnj {
err = responseJson(buf, model)
} else if tp != cnx {
err = responseXml(buf, model)
} else {
return ErrInvalidResponse
}

if rsp.StatusCode < 200 || rsp.StatusCode >= 300 {
if err != nil {
return err
} else {
return &ErrorStatus{
Status: rsp.Status,
Message: truncateBuf(buf),
}
}
} else if err != nil {
return err
}

return nil
}

func truncateBuf(buf *bytes.Buffer) string {
if buf.Len() > 255 {
return buf.String()[:255]
} else {
return buf.String()
}
}

func responseJson(buf *bytes.Buffer, model any) error {
if e := json.Unmarshal(buf.Bytes(), model); e != nil {
return responseJsonError(buf)
}

return nil
}

func responseJsonError(buf *bytes.Buffer) error {
var err = ErrorResponse{}

if e := json.Unmarshal(buf.Bytes(), &err); e != nil {
return e
} else {
return err
}
}

func responseXml(buf *bytes.Buffer, model any) error {
if e := xml.Unmarshal(buf.Bytes(), model); e != nil {
return responseXmlError(buf)
}

return nil
}

func responseXmlError(buf *bytes.Buffer) error {
var err = ErrorResponse{}

if e := xml.Unmarshal(buf.Bytes(), &err); e != nil {
return e
} else {
return err
}
}
1 change: 1 addition & 0 deletions aws/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type Config interface {
Validate() error

GetAccessKey() string
GetSecretKey() string
SetCredentials(accessKey, secretKey string)
ResetRegionEndpoint()
RegisterRegionEndpoint(region string, endpoint *url.URL) error
Expand Down
10 changes: 6 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ module github.com/nabbar/golib

go 1.21

toolchain go1.21.3
toolchain go1.21.4

require (
github.com/aws/aws-sdk-go v1.48.3
github.com/aws/aws-sdk-go-v2 v1.23.1
github.com/aws/aws-sdk-go-v2/config v1.25.4
github.com/aws/aws-sdk-go-v2/credentials v1.16.3
github.com/aws/aws-sdk-go-v2/config v1.25.5
github.com/aws/aws-sdk-go-v2/credentials v1.16.4
github.com/aws/aws-sdk-go-v2/service/iam v1.27.3
github.com/aws/aws-sdk-go-v2/service/s3 v1.43.1
github.com/aws/aws-sdk-go-v2/service/s3 v1.44.0
github.com/aws/smithy-go v1.17.0
github.com/bits-and-blooms/bitset v1.11.0
github.com/c-bata/go-prompt v0.2.6
Expand Down Expand Up @@ -146,6 +147,7 @@ require (
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/juju/ratelimit v1.0.2 // indirect
github.com/klauspost/compress v1.17.3 // indirect
Expand Down

0 comments on commit 82be8c0

Please sign in to comment.