Skip to content

Commit

Permalink
Merge pull request #211 from 88labs/feat-awss3-global-http-dialer-opt…
Browse files Browse the repository at this point in the history
…ions

feat: awss3 add global http dialer options
  • Loading branch information
tomtwinkle committed Jun 20, 2023
2 parents 6d8922b + 2bc87ed commit c0841da
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 1 deletion.
40 changes: 40 additions & 0 deletions aws/awss3/awss3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"testing"
"time"

"github.com/88labs/go-utils/aws/awss3/options/global/s3dialer"

"github.com/88labs/go-utils/aws/awss3/options/s3list"

"github.com/88labs/go-utils/aws/awss3/options/s3head"
Expand Down Expand Up @@ -830,3 +832,41 @@ func TestSelectCSVHeaders(t *testing.T) {
assert.Error(t, err)
})
}

func TestGlobalOptionWithHeadObject(t *testing.T) {
ctx := ctxawslocal.WithContext(
context.Background(),
ctxawslocal.WithS3Endpoint("http://127.0.0.1:29000"), // use Minio
ctxawslocal.WithAccessKey("DUMMYACCESSKEYEXAMPLE"),
ctxawslocal.WithSecretAccessKey("DUMMYSECRETKEYEXAMPLE"),
)
s3Client, err := awss3.GetClient(ctx, TestRegion)
assert.NoError(t, err)

createFixture := func(fileSize int) awss3.Key {
key := fmt.Sprintf("awstest/%s.txt", ulid.MustNew())
uploader := manager.NewUploader(s3Client)
input := s3.PutObjectInput{
Body: bytes.NewReader(bytes.Repeat([]byte{1}, fileSize)),
Bucket: aws.String(TestBucket),
Key: aws.String(key),
Expires: aws.Time(time.Now().Add(10 * time.Minute)),
}
if _, err := uploader.Upload(ctx, &input); err != nil {
assert.NoError(t, err)
}
return awss3.Key(key)
}

t.Run("If the option is specified", func(t *testing.T) {
key := createFixture(100)
dialer := s3dialer.NewConfGlobalDialer()
dialer.WithTimeout(time.Second)
dialer.WithKeepAlive(2 * time.Second)
dialer.WithDeadline(time.Now().Add(time.Second))
awss3.GlobalDialer = dialer
res, err := awss3.HeadObject(ctx, TestRegion, TestBucket, key)
assert.NoError(t, err)
assert.Equal(t, int64(100), res.ContentLength)
})
}
42 changes: 41 additions & 1 deletion aws/awss3/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ package awss3
import (
"context"
"fmt"
"net"

"github.com/88labs/go-utils/aws/awss3/options/global/s3dialer"

"github.com/aws/aws-sdk-go-v2/aws"
awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http"
awsConfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
Expand All @@ -13,15 +17,36 @@ import (
"github.com/88labs/go-utils/aws/ctxawslocal"
)

// GlobalDialer Global http dialer settings for awss3 library
var GlobalDialer *s3dialer.ConfGlobalDialer

// GetClient
// Get s3 client for aws-sdk-go v2.
// Using ctxawslocal.WithContext, you can make requests for local mocks
func GetClient(ctx context.Context, region awsconfig.Region) (*s3.Client, error) {
if localProfile, ok := getLocalEndpoint(ctx); ok {
return getClientLocal(ctx, *localProfile)
}
awsHttpClient := awshttp.NewBuildableClient()
if GlobalDialer != nil {
awsHttpClient.WithDialerOptions(func(dialer *net.Dialer) {
if GlobalDialer.Timeout != 0 {
dialer.Timeout = GlobalDialer.Timeout
}
if GlobalDialer.Deadline != nil {
dialer.Deadline = *GlobalDialer.Deadline
}
if GlobalDialer.KeepAlive != 0 {
dialer.KeepAlive = GlobalDialer.KeepAlive
}
})
}
// S3 Client
awsCfg, err := awsConfig.LoadDefaultConfig(ctx, awsConfig.WithRegion(region.String()))
awsCfg, err := awsConfig.LoadDefaultConfig(
ctx,
awsConfig.WithRegion(region.String()),
awsConfig.WithHTTPClient(awsHttpClient),
)
if err != nil {
return nil, fmt.Errorf("unable to load SDK config, %w", err)
}
Expand All @@ -42,7 +67,22 @@ func getClientLocal(ctx context.Context, localProfile LocalProfile) (*s3.Client,
// returning EndpointNotFoundError will allow the service to fallback to it's default resolution
return aws.Endpoint{}, &aws.EndpointNotFoundError{}
})
awsHttpClient := awshttp.NewBuildableClient()
if GlobalDialer != nil {
awsHttpClient.WithDialerOptions(func(dialer *net.Dialer) {
if GlobalDialer.Timeout != 0 {
dialer.Timeout = GlobalDialer.Timeout
}
if GlobalDialer.Deadline != nil {
dialer.Deadline = *GlobalDialer.Deadline
}
if GlobalDialer.KeepAlive != 0 {
dialer.KeepAlive = GlobalDialer.KeepAlive
}
})
}
awsCfg, err := awsConfig.LoadDefaultConfig(ctx,
awsConfig.WithHTTPClient(awsHttpClient),
awsConfig.WithEndpointResolverWithOptions(customResolver),
awsConfig.WithCredentialsProvider(credentials.StaticCredentialsProvider{
Value: aws.Credentials{
Expand Down
52 changes: 52 additions & 0 deletions aws/awss3/options/global/s3dialer/s3dialer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package s3dialer

import (
"time"
)

type ConfGlobalDialer struct {
// Timeout is the maximum amount of time a dial will wait for
// a connect to complete. If Deadline is also set, it may fail
// earlier.
//
// The default is no timeout.
//
// When using TCP and dialing a host name with multiple IP
// addresses, the timeout may be divided between them.
//
// With or without a timeout, the operating system may impose
// its own earlier timeout. For instance, TCP timeouts are
// often around 3 minutes.
Timeout time.Duration

// Deadline is the absolute point in time after which dials
// will fail. If Timeout is set, it may fail earlier.
// Zero means no deadline, or dependent on the operating system
// as with the Timeout option.
Deadline *time.Time

// KeepAlive specifies the interval between keep-alive
// probes for an active network connection.
// If zero, keep-alive probes are sent with a default value
// (currently 15 seconds), if supported by the protocol and operating
// system. Network protocols or operating systems that do
// not support keep-alives ignore this field.
// If negative, keep-alive probes are disabled.
KeepAlive time.Duration
}

func NewConfGlobalDialer() *ConfGlobalDialer {
return &ConfGlobalDialer{}
}

func (c *ConfGlobalDialer) WithTimeout(timeout time.Duration) {
c.Timeout = timeout
}

func (c *ConfGlobalDialer) WithDeadline(deadline time.Time) {
c.Deadline = &deadline
}

func (c *ConfGlobalDialer) WithKeepAlive(keepAlive time.Duration) {
c.KeepAlive = keepAlive
}

0 comments on commit c0841da

Please sign in to comment.