commit
						c3c5277007
					
				| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
{
 | 
			
		||||
	"ImportPath": "github.com/docker/distribution",
 | 
			
		||||
	"GoVersion": "go1.6",
 | 
			
		||||
	"GodepVersion": "v70",
 | 
			
		||||
	"GodepVersion": "v74",
 | 
			
		||||
	"Packages": [
 | 
			
		||||
		"./..."
 | 
			
		||||
	],
 | 
			
		||||
| 
						 | 
				
			
			@ -23,118 +23,128 @@
 | 
			
		|||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/aws",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/aws/awserr",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/aws/awsutil",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/aws/client",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/aws/client/metadata",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/aws/corehandlers",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/aws/defaults",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/aws/ec2metadata",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/aws/request",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/aws/session",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/aws/signer/v4",
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/private/endpoints",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/private/protocol",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/restxml",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/private/signer/v4",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/private/waiter",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/service/cloudfront/sign",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/service/s3",
 | 
			
		||||
			"Comment": "v1.1.0-14-g49c3892",
 | 
			
		||||
			"Rev": "49c3892b61af1d4996292a3025f36e4dfa25eaee"
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/vendor/github.com/go-ini/ini",
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/aws/aws-sdk-go/vendor/github.com/jmespath/go-jmespath",
 | 
			
		||||
			"Comment": "v1.2.4",
 | 
			
		||||
			"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/bugsnag/bugsnag-go",
 | 
			
		||||
| 
						 | 
				
			
			@ -187,11 +197,6 @@
 | 
			
		|||
			"ImportPath": "github.com/garyburd/redigo/redis",
 | 
			
		||||
			"Rev": "535138d7bcd717d6531c701ef5933d98b1866257"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/go-ini/ini",
 | 
			
		||||
			"Comment": "v1.8.6",
 | 
			
		||||
			"Rev": "afbd495e5aaea13597b5e14fe514ddeaa4d76fc3"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/golang/protobuf/proto",
 | 
			
		||||
			"Rev": "8d92cf5fc15a4382f8964b08e1f42a75c0591aa3"
 | 
			
		||||
| 
						 | 
				
			
			@ -212,11 +217,6 @@
 | 
			
		|||
			"ImportPath": "github.com/inconshreveable/mousetrap",
 | 
			
		||||
			"Rev": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/jmespath/go-jmespath",
 | 
			
		||||
			"Comment": "0.2.2-12-g0b12d6b",
 | 
			
		||||
			"Rev": "0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/mitchellh/mapstructure",
 | 
			
		||||
			"Rev": "482a9fd5fa83e8c4e7817413b80f3eb8feec03ef"
 | 
			
		||||
| 
						 | 
				
			
			@ -437,6 +437,22 @@
 | 
			
		|||
		{
 | 
			
		||||
			"ImportPath": "rsc.io/letsencrypt",
 | 
			
		||||
			"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme",
 | 
			
		||||
			"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1",
 | 
			
		||||
			"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher",
 | 
			
		||||
			"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json",
 | 
			
		||||
			"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,9 +42,12 @@ type Error interface {
 | 
			
		|||
	OrigErr() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BatchError is a batch of errors which also wraps lower level errors with code, message,
 | 
			
		||||
// and original errors. Calling Error() will only return the error that is at the end
 | 
			
		||||
// of the list.
 | 
			
		||||
// BatchError is a batch of errors which also wraps lower level errors with
 | 
			
		||||
// code, message, and original errors. Calling Error() will include all errors
 | 
			
		||||
// that occured in the batch.
 | 
			
		||||
//
 | 
			
		||||
// Deprecated: Replaced with BatchedErrors. Only defined for backwards
 | 
			
		||||
// compatibility.
 | 
			
		||||
type BatchError interface {
 | 
			
		||||
	// Satisfy the generic error interface.
 | 
			
		||||
	error
 | 
			
		||||
| 
						 | 
				
			
			@ -59,20 +62,35 @@ type BatchError interface {
 | 
			
		|||
	OrigErrs() []error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BatchedErrors is a batch of errors which also wraps lower level errors with
 | 
			
		||||
// code, message, and original errors. Calling Error() will include all errors
 | 
			
		||||
// that occured in the batch.
 | 
			
		||||
//
 | 
			
		||||
// Replaces BatchError
 | 
			
		||||
type BatchedErrors interface {
 | 
			
		||||
	// Satisfy the base Error interface.
 | 
			
		||||
	Error
 | 
			
		||||
 | 
			
		||||
	// Returns the original error if one was set.  Nil is returned if not set.
 | 
			
		||||
	OrigErrs() []error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New returns an Error object described by the code, message, and origErr.
 | 
			
		||||
//
 | 
			
		||||
// If origErr satisfies the Error interface it will not be wrapped within a new
 | 
			
		||||
// Error object and will instead be returned.
 | 
			
		||||
func New(code, message string, origErr error) Error {
 | 
			
		||||
	if e, ok := origErr.(Error); ok && e != nil {
 | 
			
		||||
		return e
 | 
			
		||||
	var errs []error
 | 
			
		||||
	if origErr != nil {
 | 
			
		||||
		errs = append(errs, origErr)
 | 
			
		||||
	}
 | 
			
		||||
	return newBaseError(code, message, origErr)
 | 
			
		||||
	return newBaseError(code, message, errs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBatchError returns an baseError with an expectation of an array of errors
 | 
			
		||||
func NewBatchError(code, message string, errs []error) BatchError {
 | 
			
		||||
	return newBaseErrors(code, message, errs)
 | 
			
		||||
// NewBatchError returns an BatchedErrors with a collection of errors as an
 | 
			
		||||
// array of errors.
 | 
			
		||||
func NewBatchError(code, message string, errs []error) BatchedErrors {
 | 
			
		||||
	return newBaseError(code, message, errs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A RequestFailure is an interface to extract request failure information from
 | 
			
		||||
| 
						 | 
				
			
			@ -85,9 +103,9 @@ func NewBatchError(code, message string, errs []error) BatchError {
 | 
			
		|||
//     output, err := s3manage.Upload(svc, input, opts)
 | 
			
		||||
//     if err != nil {
 | 
			
		||||
//         if reqerr, ok := err.(RequestFailure); ok {
 | 
			
		||||
//             log.Printf("Request failed", reqerr.Code(), reqerr.Message(), reqerr.RequestID())
 | 
			
		||||
//             log.Println("Request failed", reqerr.Code(), reqerr.Message(), reqerr.RequestID())
 | 
			
		||||
//         } else {
 | 
			
		||||
//             log.Printf("Error:", err.Error()
 | 
			
		||||
//             log.Println("Error:", err.Error())
 | 
			
		||||
//         }
 | 
			
		||||
//     }
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,36 +34,17 @@ type baseError struct {
 | 
			
		|||
	errs []error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newBaseError returns an error object for the code, message, and err.
 | 
			
		||||
// newBaseError returns an error object for the code, message, and errors.
 | 
			
		||||
//
 | 
			
		||||
// code is a short no whitespace phrase depicting the classification of
 | 
			
		||||
// the error that is being created.
 | 
			
		||||
//
 | 
			
		||||
// message is the free flow string containing detailed information about the error.
 | 
			
		||||
// message is the free flow string containing detailed information about the
 | 
			
		||||
// error.
 | 
			
		||||
//
 | 
			
		||||
// origErr is the error object which will be nested under the new error to be returned.
 | 
			
		||||
func newBaseError(code, message string, origErr error) *baseError {
 | 
			
		||||
	b := &baseError{
 | 
			
		||||
		code:    code,
 | 
			
		||||
		message: message,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if origErr != nil {
 | 
			
		||||
		b.errs = append(b.errs, origErr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newBaseErrors returns an error object for the code, message, and errors.
 | 
			
		||||
//
 | 
			
		||||
// code is a short no whitespace phrase depicting the classification of
 | 
			
		||||
// the error that is being created.
 | 
			
		||||
//
 | 
			
		||||
// message is the free flow string containing detailed information about the error.
 | 
			
		||||
//
 | 
			
		||||
// origErrs is the error objects which will be nested under the new errors to be returned.
 | 
			
		||||
func newBaseErrors(code, message string, origErrs []error) *baseError {
 | 
			
		||||
// origErrs is the error objects which will be nested under the new errors to
 | 
			
		||||
// be returned.
 | 
			
		||||
func newBaseError(code, message string, origErrs []error) *baseError {
 | 
			
		||||
	b := &baseError{
 | 
			
		||||
		code:    code,
 | 
			
		||||
		message: message,
 | 
			
		||||
| 
						 | 
				
			
			@ -103,19 +84,26 @@ func (b baseError) Message() string {
 | 
			
		|||
	return b.message
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OrigErr returns the original error if one was set. Nil is returned if no error
 | 
			
		||||
// was set. This only returns the first element in the list. If the full list is
 | 
			
		||||
// needed, use BatchError
 | 
			
		||||
// OrigErr returns the original error if one was set. Nil is returned if no
 | 
			
		||||
// error was set. This only returns the first element in the list. If the full
 | 
			
		||||
// list is needed, use BatchedErrors.
 | 
			
		||||
func (b baseError) OrigErr() error {
 | 
			
		||||
	if size := len(b.errs); size > 0 {
 | 
			
		||||
	switch len(b.errs) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		return nil
 | 
			
		||||
	case 1:
 | 
			
		||||
		return b.errs[0]
 | 
			
		||||
	default:
 | 
			
		||||
		if err, ok := b.errs[0].(Error); ok {
 | 
			
		||||
			return NewBatchError(err.Code(), err.Message(), b.errs[1:])
 | 
			
		||||
		}
 | 
			
		||||
		return NewBatchError("BatchedErrors",
 | 
			
		||||
			"multiple errors occured", b.errs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OrigErrs returns the original errors if one was set. An empty slice is returned if
 | 
			
		||||
// no error was set:w
 | 
			
		||||
// OrigErrs returns the original errors if one was set. An empty slice is
 | 
			
		||||
// returned if no error was set.
 | 
			
		||||
func (b baseError) OrigErrs() []error {
 | 
			
		||||
	return b.errs
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -133,8 +121,8 @@ type requestError struct {
 | 
			
		|||
	requestID  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newRequestError returns a wrapped error with additional information for request
 | 
			
		||||
// status code, and service requestID.
 | 
			
		||||
// newRequestError returns a wrapped error with additional information for
 | 
			
		||||
// request status code, and service requestID.
 | 
			
		||||
//
 | 
			
		||||
// Should be used to wrap all request which involve service requests. Even if
 | 
			
		||||
// the request failed without a service response, but had an HTTP status code
 | 
			
		||||
| 
						 | 
				
			
			@ -173,6 +161,15 @@ func (r requestError) RequestID() string {
 | 
			
		|||
	return r.requestID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OrigErrs returns the original errors if one was set. An empty slice is
 | 
			
		||||
// returned if no error was set.
 | 
			
		||||
func (r requestError) OrigErrs() []error {
 | 
			
		||||
	if b, ok := r.awsError.(BatchedErrors); ok {
 | 
			
		||||
		return b.OrigErrs()
 | 
			
		||||
	}
 | 
			
		||||
	return []error{r.OrigErr()}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An error list that satisfies the golang interface
 | 
			
		||||
type errorList []error
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -91,6 +91,10 @@ func prettify(v reflect.Value, indent int, buf *bytes.Buffer) {
 | 
			
		|||
 | 
			
		||||
		buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
 | 
			
		||||
	default:
 | 
			
		||||
		if !v.IsValid() {
 | 
			
		||||
			fmt.Fprint(buf, "<invalid value>")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		format := "%v"
 | 
			
		||||
		switch v.Interface().(type) {
 | 
			
		||||
		case string:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"math"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/request"
 | 
			
		||||
| 
						 | 
				
			
			@ -30,16 +30,61 @@ func (d DefaultRetryer) MaxRetries() int {
 | 
			
		|||
	return d.NumMaxRetries
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var seededRand = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())})
 | 
			
		||||
 | 
			
		||||
// RetryRules returns the delay duration before retrying this request again
 | 
			
		||||
func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration {
 | 
			
		||||
	delay := int(math.Pow(2, float64(r.RetryCount))) * (rand.Intn(30) + 30)
 | 
			
		||||
	// Set the upper limit of delay in retrying at ~five minutes
 | 
			
		||||
	minTime := 30
 | 
			
		||||
	throttle := d.shouldThrottle(r)
 | 
			
		||||
	if throttle {
 | 
			
		||||
		minTime = 500
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	retryCount := r.RetryCount
 | 
			
		||||
	if retryCount > 13 {
 | 
			
		||||
		retryCount = 13
 | 
			
		||||
	} else if throttle && retryCount > 8 {
 | 
			
		||||
		retryCount = 8
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delay := (1 << uint(retryCount)) * (seededRand.Intn(minTime) + minTime)
 | 
			
		||||
	return time.Duration(delay) * time.Millisecond
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ShouldRetry returns if the request should be retried.
 | 
			
		||||
// ShouldRetry returns true if the request should be retried.
 | 
			
		||||
func (d DefaultRetryer) ShouldRetry(r *request.Request) bool {
 | 
			
		||||
	if r.HTTPResponse.StatusCode >= 500 {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return r.IsErrorRetryable()
 | 
			
		||||
	return r.IsErrorRetryable() || d.shouldThrottle(r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ShouldThrottle returns true if the request should be throttled.
 | 
			
		||||
func (d DefaultRetryer) shouldThrottle(r *request.Request) bool {
 | 
			
		||||
	if r.HTTPResponse.StatusCode == 502 ||
 | 
			
		||||
		r.HTTPResponse.StatusCode == 503 ||
 | 
			
		||||
		r.HTTPResponse.StatusCode == 504 {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return r.IsErrorThrottle()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// lockedSource is a thread-safe implementation of rand.Source
 | 
			
		||||
type lockedSource struct {
 | 
			
		||||
	lk  sync.Mutex
 | 
			
		||||
	src rand.Source
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *lockedSource) Int63() (n int64) {
 | 
			
		||||
	r.lk.Lock()
 | 
			
		||||
	n = r.src.Int63()
 | 
			
		||||
	r.lk.Unlock()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *lockedSource) Seed(seed int64) {
 | 
			
		||||
	r.lk.Lock()
 | 
			
		||||
	r.src.Seed(seed)
 | 
			
		||||
	r.lk.Unlock()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,6 +100,31 @@ type Config struct {
 | 
			
		|||
	//   Amazon S3: Virtual Hosting of Buckets
 | 
			
		||||
	S3ForcePathStyle *bool
 | 
			
		||||
 | 
			
		||||
	// Set this to `true` to disable the SDK adding the `Expect: 100-Continue`
 | 
			
		||||
	// header to PUT requests over 2MB of content. 100-Continue instructs the
 | 
			
		||||
	// HTTP client not to send the body until the service responds with a
 | 
			
		||||
	// `continue` status. This is useful to prevent sending the request body
 | 
			
		||||
	// until after the request is authenticated, and validated.
 | 
			
		||||
	//
 | 
			
		||||
	// http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html
 | 
			
		||||
	//
 | 
			
		||||
	// 100-Continue is only enabled for Go 1.6 and above. See `http.Transport`'s
 | 
			
		||||
	// `ExpectContinueTimeout` for information on adjusting the continue wait timeout.
 | 
			
		||||
	// https://golang.org/pkg/net/http/#Transport
 | 
			
		||||
	//
 | 
			
		||||
	// You should use this flag to disble 100-Continue if you experiance issues
 | 
			
		||||
	// with proxies or thrid party S3 compatible services.
 | 
			
		||||
	S3Disable100Continue *bool
 | 
			
		||||
 | 
			
		||||
	// Set this to `true` to enable S3 Accelerate feature. For all operations compatible
 | 
			
		||||
	// with S3 Accelerate will use the accelerate endpoint for requests. Requests not compatible
 | 
			
		||||
	// will fall back to normal S3 requests.
 | 
			
		||||
	//
 | 
			
		||||
	// The bucket must be enable for accelerate to be used with S3 client with accelerate
 | 
			
		||||
	// enabled. If the bucket is not enabled for accelerate an error will be returned.
 | 
			
		||||
	// The bucket name must be DNS compatible to also work with accelerate.
 | 
			
		||||
	S3UseAccelerate *bool
 | 
			
		||||
 | 
			
		||||
	// Set this to `true` to disable the EC2Metadata client from overriding the
 | 
			
		||||
	// default http.Client's Timeout. This is helpful if you do not want the EC2Metadata
 | 
			
		||||
	// client to create a new http.Client. This options is only meaningful if you're not
 | 
			
		||||
| 
						 | 
				
			
			@ -114,13 +139,18 @@ type Config struct {
 | 
			
		|||
	//
 | 
			
		||||
	EC2MetadataDisableTimeoutOverride *bool
 | 
			
		||||
 | 
			
		||||
	// SleepDelay is an override for the func the SDK will call when sleeping
 | 
			
		||||
	// during the lifecycle of a request. Specifically this will be used for
 | 
			
		||||
	// request delays. This value should only be used for testing. To adjust
 | 
			
		||||
	// the delay of a request see the aws/client.DefaultRetryer and
 | 
			
		||||
	// aws/request.Retryer.
 | 
			
		||||
	SleepDelay func(time.Duration)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewConfig returns a new Config pointer that can be chained with builder methods to
 | 
			
		||||
// set multiple configuration values inline without using pointers.
 | 
			
		||||
//
 | 
			
		||||
//     svc := s3.New(aws.NewConfig().WithRegion("us-west-2").WithMaxRetries(10))
 | 
			
		||||
//     sess := session.New(aws.NewConfig().WithRegion("us-west-2").WithMaxRetries(10))
 | 
			
		||||
//
 | 
			
		||||
func NewConfig() *Config {
 | 
			
		||||
	return &Config{}
 | 
			
		||||
| 
						 | 
				
			
			@ -210,6 +240,20 @@ func (c *Config) WithS3ForcePathStyle(force bool) *Config {
 | 
			
		|||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithS3Disable100Continue sets a config S3Disable100Continue value returning
 | 
			
		||||
// a Config pointer for chaining.
 | 
			
		||||
func (c *Config) WithS3Disable100Continue(disable bool) *Config {
 | 
			
		||||
	c.S3Disable100Continue = &disable
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithS3UseAccelerate sets a config S3UseAccelerate value returning a Config
 | 
			
		||||
// pointer for chaining.
 | 
			
		||||
func (c *Config) WithS3UseAccelerate(enable bool) *Config {
 | 
			
		||||
	c.S3UseAccelerate = &enable
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithEC2MetadataDisableTimeoutOverride sets a config EC2MetadataDisableTimeoutOverride value
 | 
			
		||||
// returning a Config pointer for chaining.
 | 
			
		||||
func (c *Config) WithEC2MetadataDisableTimeoutOverride(enable bool) *Config {
 | 
			
		||||
| 
						 | 
				
			
			@ -288,6 +332,14 @@ func mergeInConfig(dst *Config, other *Config) {
 | 
			
		|||
		dst.S3ForcePathStyle = other.S3ForcePathStyle
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if other.S3Disable100Continue != nil {
 | 
			
		||||
		dst.S3Disable100Continue = other.S3Disable100Continue
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if other.S3UseAccelerate != nil {
 | 
			
		||||
		dst.S3UseAccelerate = other.S3UseAccelerate
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if other.EC2MetadataDisableTimeoutOverride != nil {
 | 
			
		||||
		dst.EC2MetadataDisableTimeoutOverride = other.EC2MetadataDisableTimeoutOverride
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ package aws
 | 
			
		|||
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
// String returns a pointer to of the string value passed in.
 | 
			
		||||
// String returns a pointer to the string value passed in.
 | 
			
		||||
func String(v string) *string {
 | 
			
		||||
	return &v
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +61,7 @@ func StringValueMap(src map[string]*string) map[string]string {
 | 
			
		|||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Bool returns a pointer to of the bool value passed in.
 | 
			
		||||
// Bool returns a pointer to the bool value passed in.
 | 
			
		||||
func Bool(v bool) *bool {
 | 
			
		||||
	return &v
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +120,7 @@ func BoolValueMap(src map[string]*bool) map[string]bool {
 | 
			
		|||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Int returns a pointer to of the int value passed in.
 | 
			
		||||
// Int returns a pointer to the int value passed in.
 | 
			
		||||
func Int(v int) *int {
 | 
			
		||||
	return &v
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -179,7 +179,7 @@ func IntValueMap(src map[string]*int) map[string]int {
 | 
			
		|||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Int64 returns a pointer to of the int64 value passed in.
 | 
			
		||||
// Int64 returns a pointer to the int64 value passed in.
 | 
			
		||||
func Int64(v int64) *int64 {
 | 
			
		||||
	return &v
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -238,7 +238,7 @@ func Int64ValueMap(src map[string]*int64) map[string]int64 {
 | 
			
		|||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Float64 returns a pointer to of the float64 value passed in.
 | 
			
		||||
// Float64 returns a pointer to the float64 value passed in.
 | 
			
		||||
func Float64(v float64) *float64 {
 | 
			
		||||
	return &v
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -297,7 +297,7 @@ func Float64ValueMap(src map[string]*float64) map[string]float64 {
 | 
			
		|||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Time returns a pointer to of the time.Time value passed in.
 | 
			
		||||
// Time returns a pointer to the time.Time value passed in.
 | 
			
		||||
func Time(v time.Time) *time.Time {
 | 
			
		||||
	return &v
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -311,6 +311,18 @@ func TimeValue(v *time.Time) time.Time {
 | 
			
		|||
	return time.Time{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TimeUnixMilli returns a Unix timestamp in milliseconds from "January 1, 1970 UTC".
 | 
			
		||||
// The result is undefined if the Unix time cannot be represented by an int64.
 | 
			
		||||
// Which includes calling TimeUnixMilli on a zero Time is undefined.
 | 
			
		||||
//
 | 
			
		||||
// This utility is useful for service API's such as CloudWatch Logs which require
 | 
			
		||||
// their unix time values to be in milliseconds.
 | 
			
		||||
//
 | 
			
		||||
// See Go stdlib https://golang.org/pkg/time/#Time.UnixNano for more information.
 | 
			
		||||
func TimeUnixMilli(t time.Time) int64 {
 | 
			
		||||
	return t.UnixNano() / int64(time.Millisecond/time.Nanosecond)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TimeSlice converts a slice of time.Time values into a slice of
 | 
			
		||||
// time.Time pointers
 | 
			
		||||
func TimeSlice(src []time.Time) []*time.Time {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,30 +24,38 @@ type lener interface {
 | 
			
		|||
// BuildContentLengthHandler builds the content length of a request based on the body,
 | 
			
		||||
// or will use the HTTPRequest.Header's "Content-Length" if defined. If unable
 | 
			
		||||
// to determine request body length and no "Content-Length" was specified it will panic.
 | 
			
		||||
//
 | 
			
		||||
// The Content-Length will only be aded to the request if the length of the body
 | 
			
		||||
// is greater than 0. If the body is empty or the current `Content-Length`
 | 
			
		||||
// header is <= 0, the header will also be stripped.
 | 
			
		||||
var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLengthHandler", Fn: func(r *request.Request) {
 | 
			
		||||
	if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" {
 | 
			
		||||
		length, _ := strconv.ParseInt(slength, 10, 64)
 | 
			
		||||
		r.HTTPRequest.ContentLength = length
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var length int64
 | 
			
		||||
	switch body := r.Body.(type) {
 | 
			
		||||
	case nil:
 | 
			
		||||
		length = 0
 | 
			
		||||
	case lener:
 | 
			
		||||
		length = int64(body.Len())
 | 
			
		||||
	case io.Seeker:
 | 
			
		||||
		r.BodyStart, _ = body.Seek(0, 1)
 | 
			
		||||
		end, _ := body.Seek(0, 2)
 | 
			
		||||
		body.Seek(r.BodyStart, 0) // make sure to seek back to original location
 | 
			
		||||
		length = end - r.BodyStart
 | 
			
		||||
	default:
 | 
			
		||||
		panic("Cannot get length of body, must provide `ContentLength`")
 | 
			
		||||
 | 
			
		||||
	if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" {
 | 
			
		||||
		length, _ = strconv.ParseInt(slength, 10, 64)
 | 
			
		||||
	} else {
 | 
			
		||||
		switch body := r.Body.(type) {
 | 
			
		||||
		case nil:
 | 
			
		||||
			length = 0
 | 
			
		||||
		case lener:
 | 
			
		||||
			length = int64(body.Len())
 | 
			
		||||
		case io.Seeker:
 | 
			
		||||
			r.BodyStart, _ = body.Seek(0, 1)
 | 
			
		||||
			end, _ := body.Seek(0, 2)
 | 
			
		||||
			body.Seek(r.BodyStart, 0) // make sure to seek back to original location
 | 
			
		||||
			length = end - r.BodyStart
 | 
			
		||||
		default:
 | 
			
		||||
			panic("Cannot get length of body, must provide `ContentLength`")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.HTTPRequest.ContentLength = length
 | 
			
		||||
	r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length))
 | 
			
		||||
	if length > 0 {
 | 
			
		||||
		r.HTTPRequest.ContentLength = length
 | 
			
		||||
		r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length))
 | 
			
		||||
	} else {
 | 
			
		||||
		r.HTTPRequest.ContentLength = 0
 | 
			
		||||
		r.HTTPRequest.Header.Del("Content-Length")
 | 
			
		||||
	}
 | 
			
		||||
}}
 | 
			
		||||
 | 
			
		||||
// SDKVersionUserAgentHandler is a request handler for adding the SDK Version to the user agent.
 | 
			
		||||
| 
						 | 
				
			
			@ -64,6 +72,11 @@ var SendHandler = request.NamedHandler{Name: "core.SendHandler", Fn: func(r *req
 | 
			
		|||
	var err error
 | 
			
		||||
	r.HTTPResponse, err = r.Config.HTTPClient.Do(r.HTTPRequest)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// Prevent leaking if an HTTPResponse was returned. Clean up
 | 
			
		||||
		// the body.
 | 
			
		||||
		if r.HTTPResponse != nil {
 | 
			
		||||
			r.HTTPResponse.Body.Close()
 | 
			
		||||
		}
 | 
			
		||||
		// Capture the case where url.Error is returned for error processing
 | 
			
		||||
		// response. e.g. 301 without location header comes back as string
 | 
			
		||||
		// error and r.HTTPResponse is nil. Other url redirect errors will
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,144 +1,17 @@
 | 
			
		|||
package corehandlers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/awserr"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/request"
 | 
			
		||||
)
 | 
			
		||||
import "github.com/aws/aws-sdk-go/aws/request"
 | 
			
		||||
 | 
			
		||||
// ValidateParametersHandler is a request handler to validate the input parameters.
 | 
			
		||||
// Validating parameters only has meaning if done prior to the request being sent.
 | 
			
		||||
var ValidateParametersHandler = request.NamedHandler{Name: "core.ValidateParametersHandler", Fn: func(r *request.Request) {
 | 
			
		||||
	if r.ParamsFilled() {
 | 
			
		||||
		v := validator{errors: []string{}}
 | 
			
		||||
		v.validateAny(reflect.ValueOf(r.Params), "")
 | 
			
		||||
 | 
			
		||||
		if count := len(v.errors); count > 0 {
 | 
			
		||||
			format := "%d validation errors:\n- %s"
 | 
			
		||||
			msg := fmt.Sprintf(format, count, strings.Join(v.errors, "\n- "))
 | 
			
		||||
			r.Error = awserr.New("InvalidParameter", msg, nil)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}}
 | 
			
		||||
 | 
			
		||||
// A validator validates values. Collects validations errors which occurs.
 | 
			
		||||
type validator struct {
 | 
			
		||||
	errors []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// validateAny will validate any struct, slice or map type. All validations
 | 
			
		||||
// are also performed recursively for nested types.
 | 
			
		||||
func (v *validator) validateAny(value reflect.Value, path string) {
 | 
			
		||||
	value = reflect.Indirect(value)
 | 
			
		||||
	if !value.IsValid() {
 | 
			
		||||
	if !r.ParamsFilled() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value.Kind() {
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		v.validateStruct(value, path)
 | 
			
		||||
	case reflect.Slice:
 | 
			
		||||
		for i := 0; i < value.Len(); i++ {
 | 
			
		||||
			v.validateAny(value.Index(i), path+fmt.Sprintf("[%d]", i))
 | 
			
		||||
		}
 | 
			
		||||
	case reflect.Map:
 | 
			
		||||
		for _, n := range value.MapKeys() {
 | 
			
		||||
			v.validateAny(value.MapIndex(n), path+fmt.Sprintf("[%q]", n.String()))
 | 
			
		||||
	if v, ok := r.Params.(request.Validator); ok {
 | 
			
		||||
		if err := v.Validate(); err != nil {
 | 
			
		||||
			r.Error = err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// validateStruct will validate the struct value's fields. If the structure has
 | 
			
		||||
// nested types those types will be validated also.
 | 
			
		||||
func (v *validator) validateStruct(value reflect.Value, path string) {
 | 
			
		||||
	prefix := "."
 | 
			
		||||
	if path == "" {
 | 
			
		||||
		prefix = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < value.Type().NumField(); i++ {
 | 
			
		||||
		f := value.Type().Field(i)
 | 
			
		||||
		if strings.ToLower(f.Name[0:1]) == f.Name[0:1] {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		fvalue := value.FieldByName(f.Name)
 | 
			
		||||
 | 
			
		||||
		err := validateField(f, fvalue, validateFieldRequired, validateFieldMin)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			v.errors = append(v.errors, fmt.Sprintf("%s: %s", err.Error(), path+prefix+f.Name))
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		v.validateAny(fvalue, path+prefix+f.Name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type validatorFunc func(f reflect.StructField, fvalue reflect.Value) error
 | 
			
		||||
 | 
			
		||||
func validateField(f reflect.StructField, fvalue reflect.Value, funcs ...validatorFunc) error {
 | 
			
		||||
	for _, fn := range funcs {
 | 
			
		||||
		if err := fn(f, fvalue); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validates that a field has a valid value provided for required fields.
 | 
			
		||||
func validateFieldRequired(f reflect.StructField, fvalue reflect.Value) error {
 | 
			
		||||
	if f.Tag.Get("required") == "" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch fvalue.Kind() {
 | 
			
		||||
	case reflect.Ptr, reflect.Slice, reflect.Map:
 | 
			
		||||
		if fvalue.IsNil() {
 | 
			
		||||
			return fmt.Errorf("missing required parameter")
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		if !fvalue.IsValid() {
 | 
			
		||||
			return fmt.Errorf("missing required parameter")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validates that if a value is provided for a field, that value must be at
 | 
			
		||||
// least a minimum length.
 | 
			
		||||
func validateFieldMin(f reflect.StructField, fvalue reflect.Value) error {
 | 
			
		||||
	minStr := f.Tag.Get("min")
 | 
			
		||||
	if minStr == "" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	min, _ := strconv.ParseInt(minStr, 10, 64)
 | 
			
		||||
 | 
			
		||||
	kind := fvalue.Kind()
 | 
			
		||||
	if kind == reflect.Ptr {
 | 
			
		||||
		if fvalue.IsNil() {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		fvalue = fvalue.Elem()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch fvalue.Kind() {
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		if int64(fvalue.Len()) < min {
 | 
			
		||||
			return fmt.Errorf("field too short, minimum length %d", min)
 | 
			
		||||
		}
 | 
			
		||||
	case reflect.Slice, reflect.Map:
 | 
			
		||||
		if fvalue.IsNil() {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		if int64(fvalue.Len()) < min {
 | 
			
		||||
			return fmt.Errorf("field too short, minimum length %d", min)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// TODO min can also apply to number minimum value.
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
}}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										8
									
								
								vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										8
									
								
								vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
								
								
									generated
								
								
									vendored
								
								
							| 
						 | 
				
			
			@ -132,7 +132,7 @@ const iamSecurityCredsPath = "/iam/security-credentials"
 | 
			
		|||
func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) {
 | 
			
		||||
	resp, err := client.GetMetadata(iamSecurityCredsPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, awserr.New("EC2RoleRequestError", "failed to list EC2 Roles", err)
 | 
			
		||||
		return nil, awserr.New("EC2RoleRequestError", "no EC2 instance role found", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	credsList := []string{}
 | 
			
		||||
| 
						 | 
				
			
			@ -142,7 +142,7 @@ func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if err := s.Err(); err != nil {
 | 
			
		||||
		return nil, awserr.New("SerializationError", "failed to read list of EC2 Roles", err)
 | 
			
		||||
		return nil, awserr.New("SerializationError", "failed to read EC2 instance role from metadata service", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return credsList, nil
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +157,7 @@ func requestCred(client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCred
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return ec2RoleCredRespBody{},
 | 
			
		||||
			awserr.New("EC2RoleRequestError",
 | 
			
		||||
				fmt.Sprintf("failed to get %s EC2 Role credentials", credsName),
 | 
			
		||||
				fmt.Sprintf("failed to get %s EC2 instance role credentials", credsName),
 | 
			
		||||
				err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +165,7 @@ func requestCred(client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCred
 | 
			
		|||
	if err := json.NewDecoder(strings.NewReader(resp)).Decode(&respCreds); err != nil {
 | 
			
		||||
		return ec2RoleCredRespBody{},
 | 
			
		||||
			awserr.New("SerializationError",
 | 
			
		||||
				fmt.Sprintf("failed to decode %s EC2 Role credentials", credsName),
 | 
			
		||||
				fmt.Sprintf("failed to decode %s EC2 instance role credentials", credsName),
 | 
			
		||||
				err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ var (
 | 
			
		|||
	ErrStaticCredentialsEmpty = awserr.New("EmptyStaticCreds", "static credentials are empty", nil)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A StaticProvider is a set of credentials which are set pragmatically,
 | 
			
		||||
// A StaticProvider is a set of credentials which are set programmatically,
 | 
			
		||||
// and will never expire.
 | 
			
		||||
type StaticProvider struct {
 | 
			
		||||
	Value
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,7 +66,9 @@ func Handlers() request.Handlers {
 | 
			
		|||
	var handlers request.Handlers
 | 
			
		||||
 | 
			
		||||
	handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler)
 | 
			
		||||
	handlers.Validate.AfterEachFn = request.HandlerListStopOnError
 | 
			
		||||
	handlers.Build.PushBackNamed(corehandlers.SDKVersionUserAgentHandler)
 | 
			
		||||
	handlers.Build.AfterEachFn = request.HandlerListStopOnError
 | 
			
		||||
	handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
 | 
			
		||||
	handlers.Send.PushBackNamed(corehandlers.SendHandler)
 | 
			
		||||
	handlers.AfterRetry.PushBackNamed(corehandlers.AfterRetryHandler)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,19 @@
 | 
			
		|||
package ec2metadata
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/awserr"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/request"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetMetadata uses the path provided to request
 | 
			
		||||
// GetMetadata uses the path provided to request information from the EC2
 | 
			
		||||
// instance metdata service. The content will be returned as a string, or
 | 
			
		||||
// error if the request failed.
 | 
			
		||||
func (c *EC2Metadata) GetMetadata(p string) (string, error) {
 | 
			
		||||
	op := &request.Operation{
 | 
			
		||||
		Name:       "GetMetadata",
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +27,68 @@ func (c *EC2Metadata) GetMetadata(p string) (string, error) {
 | 
			
		|||
	return output.Content, req.Send()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDynamicData uses the path provided to request information from the EC2
 | 
			
		||||
// instance metadata service for dynamic data. The content will be returned
 | 
			
		||||
// as a string, or error if the request failed.
 | 
			
		||||
func (c *EC2Metadata) GetDynamicData(p string) (string, error) {
 | 
			
		||||
	op := &request.Operation{
 | 
			
		||||
		Name:       "GetDynamicData",
 | 
			
		||||
		HTTPMethod: "GET",
 | 
			
		||||
		HTTPPath:   path.Join("/", "dynamic", p),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	output := &metadataOutput{}
 | 
			
		||||
	req := c.NewRequest(op, nil, output)
 | 
			
		||||
 | 
			
		||||
	return output.Content, req.Send()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetInstanceIdentityDocument retrieves an identity document describing an
 | 
			
		||||
// instance. Error is returned if the request fails or is unable to parse
 | 
			
		||||
// the response.
 | 
			
		||||
func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument, error) {
 | 
			
		||||
	resp, err := c.GetDynamicData("instance-identity/document")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return EC2InstanceIdentityDocument{},
 | 
			
		||||
			awserr.New("EC2MetadataRequestError",
 | 
			
		||||
				"failed to get EC2 instance identity document", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	doc := EC2InstanceIdentityDocument{}
 | 
			
		||||
	if err := json.NewDecoder(strings.NewReader(resp)).Decode(&doc); err != nil {
 | 
			
		||||
		return EC2InstanceIdentityDocument{},
 | 
			
		||||
			awserr.New("SerializationError",
 | 
			
		||||
				"failed to decode EC2 instance identity document", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return doc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IAMInfo retrieves IAM info from the metadata API
 | 
			
		||||
func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) {
 | 
			
		||||
	resp, err := c.GetMetadata("iam/info")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return EC2IAMInfo{},
 | 
			
		||||
			awserr.New("EC2MetadataRequestError",
 | 
			
		||||
				"failed to get EC2 IAM info", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	info := EC2IAMInfo{}
 | 
			
		||||
	if err := json.NewDecoder(strings.NewReader(resp)).Decode(&info); err != nil {
 | 
			
		||||
		return EC2IAMInfo{},
 | 
			
		||||
			awserr.New("SerializationError",
 | 
			
		||||
				"failed to decode EC2 IAM info", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if info.Code != "Success" {
 | 
			
		||||
		errMsg := fmt.Sprintf("failed to get EC2 IAM Info (%s)", info.Code)
 | 
			
		||||
		return EC2IAMInfo{},
 | 
			
		||||
			awserr.New("EC2MetadataError", errMsg, nil)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return info, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Region returns the region the instance is running in.
 | 
			
		||||
func (c *EC2Metadata) Region() (string, error) {
 | 
			
		||||
	resp, err := c.GetMetadata("placement/availability-zone")
 | 
			
		||||
| 
						 | 
				
			
			@ -41,3 +110,31 @@ func (c *EC2Metadata) Available() bool {
 | 
			
		|||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An EC2IAMInfo provides the shape for unmarshalling
 | 
			
		||||
// an IAM info from the metadata API
 | 
			
		||||
type EC2IAMInfo struct {
 | 
			
		||||
	Code               string
 | 
			
		||||
	LastUpdated        time.Time
 | 
			
		||||
	InstanceProfileArn string
 | 
			
		||||
	InstanceProfileID  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An EC2InstanceIdentityDocument provides the shape for unmarshalling
 | 
			
		||||
// an instance identity document
 | 
			
		||||
type EC2InstanceIdentityDocument struct {
 | 
			
		||||
	DevpayProductCodes []string  `json:"devpayProductCodes"`
 | 
			
		||||
	AvailabilityZone   string    `json:"availabilityZone"`
 | 
			
		||||
	PrivateIP          string    `json:"privateIp"`
 | 
			
		||||
	Version            string    `json:"version"`
 | 
			
		||||
	Region             string    `json:"region"`
 | 
			
		||||
	InstanceID         string    `json:"instanceId"`
 | 
			
		||||
	BillingProducts    []string  `json:"billingProducts"`
 | 
			
		||||
	InstanceType       string    `json:"instanceType"`
 | 
			
		||||
	AccountID          string    `json:"accountId"`
 | 
			
		||||
	PendingTime        time.Time `json:"pendingTime"`
 | 
			
		||||
	ImageID            string    `json:"imageId"`
 | 
			
		||||
	KernelID           string    `json:"kernelId"`
 | 
			
		||||
	RamdiskID          string    `json:"ramdiskId"`
 | 
			
		||||
	Architecture       string    `json:"architecture"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,9 @@
 | 
			
		|||
package ec2metadata
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -91,23 +93,28 @@ type metadataOutput struct {
 | 
			
		|||
 | 
			
		||||
func unmarshalHandler(r *request.Request) {
 | 
			
		||||
	defer r.HTTPResponse.Body.Close()
 | 
			
		||||
	b, err := ioutil.ReadAll(r.HTTPResponse.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	b := &bytes.Buffer{}
 | 
			
		||||
	if _, err := io.Copy(b, r.HTTPResponse.Body); err != nil {
 | 
			
		||||
		r.Error = awserr.New("SerializationError", "unable to unmarshal EC2 metadata respose", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data := r.Data.(*metadataOutput)
 | 
			
		||||
	data.Content = string(b)
 | 
			
		||||
	if data, ok := r.Data.(*metadataOutput); ok {
 | 
			
		||||
		data.Content = b.String()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unmarshalError(r *request.Request) {
 | 
			
		||||
	defer r.HTTPResponse.Body.Close()
 | 
			
		||||
	_, err := ioutil.ReadAll(r.HTTPResponse.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	b := &bytes.Buffer{}
 | 
			
		||||
	if _, err := io.Copy(b, r.HTTPResponse.Body); err != nil {
 | 
			
		||||
		r.Error = awserr.New("SerializationError", "unable to unmarshal EC2 metadata error respose", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO extract the error...
 | 
			
		||||
	// Response body format is not consistent between metadata endpoints.
 | 
			
		||||
	// Grab the error message as a string and include that as the source error
 | 
			
		||||
	r.Error = awserr.New("EC2MetadataError", "failed to make EC2Metadata request", errors.New(b.String()))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateEndpointHandler(r *request.Request) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,6 +79,20 @@ type Logger interface {
 | 
			
		|||
	Log(...interface{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A LoggerFunc is a convenience type to convert a function taking a variadic
 | 
			
		||||
// list of arguments and wrap it so the Logger interface can be used.
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//     s3.New(sess, &aws.Config{Logger: aws.LoggerFunc(func(args ...interface{}) {
 | 
			
		||||
//         fmt.Fprintln(os.Stdout, args...)
 | 
			
		||||
//     })})
 | 
			
		||||
type LoggerFunc func(...interface{})
 | 
			
		||||
 | 
			
		||||
// Log calls the wrapped function with the arguments provided
 | 
			
		||||
func (f LoggerFunc) Log(args ...interface{}) {
 | 
			
		||||
	f(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDefaultLogger returns a Logger which will write log messages to stdout, and
 | 
			
		||||
// use same formatting runes as the stdlib log.Logger
 | 
			
		||||
func NewDefaultLogger() Logger {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,9 +50,28 @@ func (h *Handlers) Clear() {
 | 
			
		|||
	h.AfterRetry.Clear()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A HandlerListRunItem represents an entry in the HandlerList which
 | 
			
		||||
// is being run.
 | 
			
		||||
type HandlerListRunItem struct {
 | 
			
		||||
	Index   int
 | 
			
		||||
	Handler NamedHandler
 | 
			
		||||
	Request *Request
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A HandlerList manages zero or more handlers in a list.
 | 
			
		||||
type HandlerList struct {
 | 
			
		||||
	list []NamedHandler
 | 
			
		||||
 | 
			
		||||
	// Called after each request handler in the list is called. If set
 | 
			
		||||
	// and the func returns true the HandlerList will continue to iterate
 | 
			
		||||
	// over the request handlers. If false is returned the HandlerList
 | 
			
		||||
	// will stop iterating.
 | 
			
		||||
	//
 | 
			
		||||
	// Should be used if extra logic to be performed between each handler
 | 
			
		||||
	// in the list. This can be used to terminate a list's iteration
 | 
			
		||||
	// based on a condition such as error like, HandlerListStopOnError.
 | 
			
		||||
	// Or for logging like HandlerListLogItem.
 | 
			
		||||
	AfterEachFn func(item HandlerListRunItem) bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A NamedHandler is a struct that contains a name and function callback.
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +82,9 @@ type NamedHandler struct {
 | 
			
		|||
 | 
			
		||||
// copy creates a copy of the handler list.
 | 
			
		||||
func (l *HandlerList) copy() HandlerList {
 | 
			
		||||
	var n HandlerList
 | 
			
		||||
	n := HandlerList{
 | 
			
		||||
		AfterEachFn: l.AfterEachFn,
 | 
			
		||||
	}
 | 
			
		||||
	n.list = append([]NamedHandler{}, l.list...)
 | 
			
		||||
	return n
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -111,11 +132,37 @@ func (l *HandlerList) Remove(n NamedHandler) {
 | 
			
		|||
 | 
			
		||||
// Run executes all handlers in the list with a given request object.
 | 
			
		||||
func (l *HandlerList) Run(r *Request) {
 | 
			
		||||
	for _, f := range l.list {
 | 
			
		||||
		f.Fn(r)
 | 
			
		||||
	for i, h := range l.list {
 | 
			
		||||
		h.Fn(r)
 | 
			
		||||
		item := HandlerListRunItem{
 | 
			
		||||
			Index: i, Handler: h, Request: r,
 | 
			
		||||
		}
 | 
			
		||||
		if l.AfterEachFn != nil && !l.AfterEachFn(item) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandlerListLogItem logs the request handler and the state of the
 | 
			
		||||
// request's Error value. Always returns true to continue iterating
 | 
			
		||||
// request handlers in a HandlerList.
 | 
			
		||||
func HandlerListLogItem(item HandlerListRunItem) bool {
 | 
			
		||||
	if item.Request.Config.Logger == nil {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	item.Request.Config.Logger.Log("DEBUG: RequestHandler",
 | 
			
		||||
		item.Index, item.Handler.Name, item.Request.Error)
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandlerListStopOnError returns false to stop the HandlerList iterating
 | 
			
		||||
// over request handlers if Request.Error is not nil. True otherwise
 | 
			
		||||
// to continue iterating.
 | 
			
		||||
func HandlerListStopOnError(item HandlerListRunItem) bool {
 | 
			
		||||
	return item.Request.Error == nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MakeAddToUserAgentHandler will add the name/version pair to the User-Agent request
 | 
			
		||||
// header. If the extra parameters are provided they will be added as metadata to the
 | 
			
		||||
// name/version pair resulting in the following format.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
// +build go1.5
 | 
			
		||||
 | 
			
		||||
package request
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func copyHTTPRequest(r *http.Request, body io.ReadCloser) *http.Request {
 | 
			
		||||
	req := &http.Request{
 | 
			
		||||
		URL:           &url.URL{},
 | 
			
		||||
		Header:        http.Header{},
 | 
			
		||||
		Close:         r.Close,
 | 
			
		||||
		Body:          body,
 | 
			
		||||
		Host:          r.Host,
 | 
			
		||||
		Method:        r.Method,
 | 
			
		||||
		Proto:         r.Proto,
 | 
			
		||||
		ContentLength: r.ContentLength,
 | 
			
		||||
		// Cancel will be deprecated in 1.7 and will be replaced with Context
 | 
			
		||||
		Cancel: r.Cancel,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*req.URL = *r.URL
 | 
			
		||||
	for k, v := range r.Header {
 | 
			
		||||
		for _, vv := range v {
 | 
			
		||||
			req.Header.Add(k, vv)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return req
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
// +build !go1.5
 | 
			
		||||
 | 
			
		||||
package request
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func copyHTTPRequest(r *http.Request, body io.ReadCloser) *http.Request {
 | 
			
		||||
	req := &http.Request{
 | 
			
		||||
		URL:           &url.URL{},
 | 
			
		||||
		Header:        http.Header{},
 | 
			
		||||
		Close:         r.Close,
 | 
			
		||||
		Body:          body,
 | 
			
		||||
		Host:          r.Host,
 | 
			
		||||
		Method:        r.Method,
 | 
			
		||||
		Proto:         r.Proto,
 | 
			
		||||
		ContentLength: r.ContentLength,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*req.URL = *r.URL
 | 
			
		||||
	for k, v := range r.Header {
 | 
			
		||||
		for _, vv := range v {
 | 
			
		||||
			req.Header.Add(k, vv)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return req
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
package request
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// offsetReader is a thread-safe io.ReadCloser to prevent racing
 | 
			
		||||
// with retrying requests
 | 
			
		||||
type offsetReader struct {
 | 
			
		||||
	buf    io.ReadSeeker
 | 
			
		||||
	lock   sync.RWMutex
 | 
			
		||||
	closed bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newOffsetReader(buf io.ReadSeeker, offset int64) *offsetReader {
 | 
			
		||||
	reader := &offsetReader{}
 | 
			
		||||
	buf.Seek(offset, 0)
 | 
			
		||||
 | 
			
		||||
	reader.buf = buf
 | 
			
		||||
	return reader
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close is a thread-safe close. Uses the write lock.
 | 
			
		||||
func (o *offsetReader) Close() error {
 | 
			
		||||
	o.lock.Lock()
 | 
			
		||||
	defer o.lock.Unlock()
 | 
			
		||||
	o.closed = true
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read is a thread-safe read using a read lock.
 | 
			
		||||
func (o *offsetReader) Read(p []byte) (int, error) {
 | 
			
		||||
	o.lock.RLock()
 | 
			
		||||
	defer o.lock.RUnlock()
 | 
			
		||||
 | 
			
		||||
	if o.closed {
 | 
			
		||||
		return 0, io.EOF
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return o.buf.Read(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CloseAndCopy will return a new offsetReader with a copy of the old buffer
 | 
			
		||||
// and close the old buffer.
 | 
			
		||||
func (o *offsetReader) CloseAndCopy(offset int64) *offsetReader {
 | 
			
		||||
	o.Close()
 | 
			
		||||
	return newOffsetReader(o.buf, offset)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -12,6 +12,7 @@ import (
 | 
			
		|||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/awserr"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/client/metadata"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +39,7 @@ type Request struct {
 | 
			
		|||
	RetryDelay       time.Duration
 | 
			
		||||
	NotHoist         bool
 | 
			
		||||
	SignedHeaderVals http.Header
 | 
			
		||||
	LastSignedAt     time.Time
 | 
			
		||||
 | 
			
		||||
	built bool
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +79,13 @@ func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	httpReq, _ := http.NewRequest(method, "", nil)
 | 
			
		||||
	httpReq.URL, _ = url.Parse(clientInfo.Endpoint + p)
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	httpReq.URL, err = url.Parse(clientInfo.Endpoint + p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		httpReq.URL = &url.URL{}
 | 
			
		||||
		err = awserr.New("InvalidEndpointURL", "invalid endpoint uri", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := &Request{
 | 
			
		||||
		Config:     cfg,
 | 
			
		||||
| 
						 | 
				
			
			@ -91,7 +99,7 @@ func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
 | 
			
		|||
		HTTPRequest: httpReq,
 | 
			
		||||
		Body:        nil,
 | 
			
		||||
		Params:      params,
 | 
			
		||||
		Error:       nil,
 | 
			
		||||
		Error:       err,
 | 
			
		||||
		Data:        data,
 | 
			
		||||
	}
 | 
			
		||||
	r.SetBufferBody([]byte{})
 | 
			
		||||
| 
						 | 
				
			
			@ -131,7 +139,7 @@ func (r *Request) SetStringBody(s string) {
 | 
			
		|||
 | 
			
		||||
// SetReaderBody will set the request's body reader.
 | 
			
		||||
func (r *Request) SetReaderBody(reader io.ReadSeeker) {
 | 
			
		||||
	r.HTTPRequest.Body = ioutil.NopCloser(reader)
 | 
			
		||||
	r.HTTPRequest.Body = newOffsetReader(reader, 0)
 | 
			
		||||
	r.Body = reader
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -185,20 +193,23 @@ func debugLogReqError(r *Request, stage string, retrying bool, err error) {
 | 
			
		|||
// which occurred will be returned.
 | 
			
		||||
func (r *Request) Build() error {
 | 
			
		||||
	if !r.built {
 | 
			
		||||
		r.Error = nil
 | 
			
		||||
		r.Handlers.Validate.Run(r)
 | 
			
		||||
		if r.Error != nil {
 | 
			
		||||
			debugLogReqError(r, "Validate Request", false, r.Error)
 | 
			
		||||
			return r.Error
 | 
			
		||||
		}
 | 
			
		||||
		r.Handlers.Build.Run(r)
 | 
			
		||||
		if r.Error != nil {
 | 
			
		||||
			debugLogReqError(r, "Build Request", false, r.Error)
 | 
			
		||||
			return r.Error
 | 
			
		||||
		}
 | 
			
		||||
		r.built = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r.Error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sign will sign the request retuning error if errors are encountered.
 | 
			
		||||
// Sign will sign the request returning error if errors are encountered.
 | 
			
		||||
//
 | 
			
		||||
// Send will build the request prior to signing. All Sign Handlers will
 | 
			
		||||
// be executed in the order they were set.
 | 
			
		||||
| 
						 | 
				
			
			@ -217,28 +228,53 @@ func (r *Request) Sign() error {
 | 
			
		|||
//
 | 
			
		||||
// Send will sign the request prior to sending. All Send Handlers will
 | 
			
		||||
// be executed in the order they were set.
 | 
			
		||||
//
 | 
			
		||||
// Canceling a request is non-deterministic. If a request has been canceled,
 | 
			
		||||
// then the transport will choose, randomly, one of the state channels during
 | 
			
		||||
// reads or getting the connection.
 | 
			
		||||
//
 | 
			
		||||
// readLoop() and getConn(req *Request, cm connectMethod)
 | 
			
		||||
// https://github.com/golang/go/blob/master/src/net/http/transport.go
 | 
			
		||||
func (r *Request) Send() error {
 | 
			
		||||
	for {
 | 
			
		||||
		r.Sign()
 | 
			
		||||
		if r.Error != nil {
 | 
			
		||||
			return r.Error
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if aws.BoolValue(r.Retryable) {
 | 
			
		||||
			if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
 | 
			
		||||
				r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d",
 | 
			
		||||
					r.ClientInfo.ServiceName, r.Operation.Name, r.RetryCount))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Re-seek the body back to the original point in for a retry so that
 | 
			
		||||
			// send will send the body's contents again in the upcoming request.
 | 
			
		||||
			r.Body.Seek(r.BodyStart, 0)
 | 
			
		||||
			r.HTTPRequest.Body = ioutil.NopCloser(r.Body)
 | 
			
		||||
			var body io.ReadCloser
 | 
			
		||||
			if reader, ok := r.HTTPRequest.Body.(*offsetReader); ok {
 | 
			
		||||
				body = reader.CloseAndCopy(r.BodyStart)
 | 
			
		||||
			} else {
 | 
			
		||||
				if r.Config.Logger != nil {
 | 
			
		||||
					r.Config.Logger.Log("Request body type has been overwritten. May cause race conditions")
 | 
			
		||||
				}
 | 
			
		||||
				r.Body.Seek(r.BodyStart, 0)
 | 
			
		||||
				body = ioutil.NopCloser(r.Body)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			r.HTTPRequest = copyHTTPRequest(r.HTTPRequest, body)
 | 
			
		||||
			if r.HTTPResponse != nil && r.HTTPResponse.Body != nil {
 | 
			
		||||
				// Closing response body. Since we are setting a new request to send off, this
 | 
			
		||||
				// response will get squashed and leaked.
 | 
			
		||||
				r.HTTPResponse.Body.Close()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		r.Sign()
 | 
			
		||||
		if r.Error != nil {
 | 
			
		||||
			return r.Error
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		r.Retryable = nil
 | 
			
		||||
 | 
			
		||||
		r.Handlers.Send.Run(r)
 | 
			
		||||
		if r.Error != nil {
 | 
			
		||||
			if strings.Contains(r.Error.Error(), "net/http: request canceled") {
 | 
			
		||||
				return r.Error
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			err := r.Error
 | 
			
		||||
			r.Handlers.Retry.Run(r)
 | 
			
		||||
			r.Handlers.AfterRetry.Run(r)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,8 +26,11 @@ func WithRetryer(cfg *aws.Config, retryer Retryer) *aws.Config {
 | 
			
		|||
// retryableCodes is a collection of service response codes which are retry-able
 | 
			
		||||
// without any further action.
 | 
			
		||||
var retryableCodes = map[string]struct{}{
 | 
			
		||||
	"RequestError":                           {},
 | 
			
		||||
	"RequestTimeout":                         {},
 | 
			
		||||
	"RequestError":   {},
 | 
			
		||||
	"RequestTimeout": {},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var throttleCodes = map[string]struct{}{
 | 
			
		||||
	"ProvisionedThroughputExceededException": {},
 | 
			
		||||
	"Throttling":                             {},
 | 
			
		||||
	"ThrottlingException":                    {},
 | 
			
		||||
| 
						 | 
				
			
			@ -46,6 +49,11 @@ var credsExpiredCodes = map[string]struct{}{
 | 
			
		|||
	"RequestExpired":        {}, // EC2 Only
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isCodeThrottle(code string) bool {
 | 
			
		||||
	_, ok := throttleCodes[code]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isCodeRetryable(code string) bool {
 | 
			
		||||
	if _, ok := retryableCodes[code]; ok {
 | 
			
		||||
		return true
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +78,17 @@ func (r *Request) IsErrorRetryable() bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsErrorThrottle returns whether the error is to be throttled based on its code.
 | 
			
		||||
// Returns false if the request has no Error set
 | 
			
		||||
func (r *Request) IsErrorThrottle() bool {
 | 
			
		||||
	if r.Error != nil {
 | 
			
		||||
		if err, ok := r.Error.(awserr.Error); ok {
 | 
			
		||||
			return isCodeThrottle(err.Code())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsErrorExpired returns whether the error code is a credential expiry error.
 | 
			
		||||
// Returns false if the request has no Error set.
 | 
			
		||||
func (r *Request) IsErrorExpired() bool {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,234 @@
 | 
			
		|||
package request
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/awserr"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// InvalidParameterErrCode is the error code for invalid parameters errors
 | 
			
		||||
	InvalidParameterErrCode = "InvalidParameter"
 | 
			
		||||
	// ParamRequiredErrCode is the error code for required parameter errors
 | 
			
		||||
	ParamRequiredErrCode = "ParamRequiredError"
 | 
			
		||||
	// ParamMinValueErrCode is the error code for fields with too low of a
 | 
			
		||||
	// number value.
 | 
			
		||||
	ParamMinValueErrCode = "ParamMinValueError"
 | 
			
		||||
	// ParamMinLenErrCode is the error code for fields without enough elements.
 | 
			
		||||
	ParamMinLenErrCode = "ParamMinLenError"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Validator provides a way for types to perform validation logic on their
 | 
			
		||||
// input values that external code can use to determine if a type's values
 | 
			
		||||
// are valid.
 | 
			
		||||
type Validator interface {
 | 
			
		||||
	Validate() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An ErrInvalidParams provides wrapping of invalid parameter errors found when
 | 
			
		||||
// validating API operation input parameters.
 | 
			
		||||
type ErrInvalidParams struct {
 | 
			
		||||
	// Context is the base context of the invalid parameter group.
 | 
			
		||||
	Context string
 | 
			
		||||
	errs    []ErrInvalidParam
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add adds a new invalid parameter error to the collection of invalid
 | 
			
		||||
// parameters. The context of the invalid parameter will be updated to reflect
 | 
			
		||||
// this collection.
 | 
			
		||||
func (e *ErrInvalidParams) Add(err ErrInvalidParam) {
 | 
			
		||||
	err.SetContext(e.Context)
 | 
			
		||||
	e.errs = append(e.errs, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddNested adds the invalid parameter errors from another ErrInvalidParams
 | 
			
		||||
// value into this collection. The nested errors will have their nested context
 | 
			
		||||
// updated and base context to reflect the merging.
 | 
			
		||||
//
 | 
			
		||||
// Use for nested validations errors.
 | 
			
		||||
func (e *ErrInvalidParams) AddNested(nestedCtx string, nested ErrInvalidParams) {
 | 
			
		||||
	for _, err := range nested.errs {
 | 
			
		||||
		err.SetContext(e.Context)
 | 
			
		||||
		err.AddNestedContext(nestedCtx)
 | 
			
		||||
		e.errs = append(e.errs, err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Len returns the number of invalid parameter errors
 | 
			
		||||
func (e ErrInvalidParams) Len() int {
 | 
			
		||||
	return len(e.errs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Code returns the code of the error
 | 
			
		||||
func (e ErrInvalidParams) Code() string {
 | 
			
		||||
	return InvalidParameterErrCode
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Message returns the message of the error
 | 
			
		||||
func (e ErrInvalidParams) Message() string {
 | 
			
		||||
	return fmt.Sprintf("%d validation error(s) found.", len(e.errs))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error returns the string formatted form of the invalid parameters.
 | 
			
		||||
func (e ErrInvalidParams) Error() string {
 | 
			
		||||
	w := &bytes.Buffer{}
 | 
			
		||||
	fmt.Fprintf(w, "%s: %s\n", e.Code(), e.Message())
 | 
			
		||||
 | 
			
		||||
	for _, err := range e.errs {
 | 
			
		||||
		fmt.Fprintf(w, "- %s\n", err.Message())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return w.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OrigErr returns the invalid parameters as a awserr.BatchedErrors value
 | 
			
		||||
func (e ErrInvalidParams) OrigErr() error {
 | 
			
		||||
	return awserr.NewBatchError(
 | 
			
		||||
		InvalidParameterErrCode, e.Message(), e.OrigErrs())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OrigErrs returns a slice of the invalid parameters
 | 
			
		||||
func (e ErrInvalidParams) OrigErrs() []error {
 | 
			
		||||
	errs := make([]error, len(e.errs))
 | 
			
		||||
	for i := 0; i < len(errs); i++ {
 | 
			
		||||
		errs[i] = e.errs[i]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return errs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An ErrInvalidParam represents an invalid parameter error type.
 | 
			
		||||
type ErrInvalidParam interface {
 | 
			
		||||
	awserr.Error
 | 
			
		||||
 | 
			
		||||
	// Field name the error occurred on.
 | 
			
		||||
	Field() string
 | 
			
		||||
 | 
			
		||||
	// SetContext updates the context of the error.
 | 
			
		||||
	SetContext(string)
 | 
			
		||||
 | 
			
		||||
	// AddNestedContext updates the error's context to include a nested level.
 | 
			
		||||
	AddNestedContext(string)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type errInvalidParam struct {
 | 
			
		||||
	context       string
 | 
			
		||||
	nestedContext string
 | 
			
		||||
	field         string
 | 
			
		||||
	code          string
 | 
			
		||||
	msg           string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Code returns the error code for the type of invalid parameter.
 | 
			
		||||
func (e *errInvalidParam) Code() string {
 | 
			
		||||
	return e.code
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Message returns the reason the parameter was invalid, and its context.
 | 
			
		||||
func (e *errInvalidParam) Message() string {
 | 
			
		||||
	return fmt.Sprintf("%s, %s.", e.msg, e.Field())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error returns the string version of the invalid parameter error.
 | 
			
		||||
func (e *errInvalidParam) Error() string {
 | 
			
		||||
	return fmt.Sprintf("%s: %s", e.code, e.Message())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OrigErr returns nil, Implemented for awserr.Error interface.
 | 
			
		||||
func (e *errInvalidParam) OrigErr() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Field Returns the field and context the error occurred.
 | 
			
		||||
func (e *errInvalidParam) Field() string {
 | 
			
		||||
	field := e.context
 | 
			
		||||
	if len(field) > 0 {
 | 
			
		||||
		field += "."
 | 
			
		||||
	}
 | 
			
		||||
	if len(e.nestedContext) > 0 {
 | 
			
		||||
		field += fmt.Sprintf("%s.", e.nestedContext)
 | 
			
		||||
	}
 | 
			
		||||
	field += e.field
 | 
			
		||||
 | 
			
		||||
	return field
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetContext updates the base context of the error.
 | 
			
		||||
func (e *errInvalidParam) SetContext(ctx string) {
 | 
			
		||||
	e.context = ctx
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddNestedContext prepends a context to the field's path.
 | 
			
		||||
func (e *errInvalidParam) AddNestedContext(ctx string) {
 | 
			
		||||
	if len(e.nestedContext) == 0 {
 | 
			
		||||
		e.nestedContext = ctx
 | 
			
		||||
	} else {
 | 
			
		||||
		e.nestedContext = fmt.Sprintf("%s.%s", ctx, e.nestedContext)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An ErrParamRequired represents an required parameter error.
 | 
			
		||||
type ErrParamRequired struct {
 | 
			
		||||
	errInvalidParam
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewErrParamRequired creates a new required parameter error.
 | 
			
		||||
func NewErrParamRequired(field string) *ErrParamRequired {
 | 
			
		||||
	return &ErrParamRequired{
 | 
			
		||||
		errInvalidParam{
 | 
			
		||||
			code:  ParamRequiredErrCode,
 | 
			
		||||
			field: field,
 | 
			
		||||
			msg:   fmt.Sprintf("missing required field"),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An ErrParamMinValue represents a minimum value parameter error.
 | 
			
		||||
type ErrParamMinValue struct {
 | 
			
		||||
	errInvalidParam
 | 
			
		||||
	min float64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewErrParamMinValue creates a new minimum value parameter error.
 | 
			
		||||
func NewErrParamMinValue(field string, min float64) *ErrParamMinValue {
 | 
			
		||||
	return &ErrParamMinValue{
 | 
			
		||||
		errInvalidParam: errInvalidParam{
 | 
			
		||||
			code:  ParamMinValueErrCode,
 | 
			
		||||
			field: field,
 | 
			
		||||
			msg:   fmt.Sprintf("minimum field value of %v", min),
 | 
			
		||||
		},
 | 
			
		||||
		min: min,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MinValue returns the field's require minimum value.
 | 
			
		||||
//
 | 
			
		||||
// float64 is returned for both int and float min values.
 | 
			
		||||
func (e *ErrParamMinValue) MinValue() float64 {
 | 
			
		||||
	return e.min
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An ErrParamMinLen represents a minimum length parameter error.
 | 
			
		||||
type ErrParamMinLen struct {
 | 
			
		||||
	errInvalidParam
 | 
			
		||||
	min int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewErrParamMinLen creates a new minimum length parameter error.
 | 
			
		||||
func NewErrParamMinLen(field string, min int) *ErrParamMinLen {
 | 
			
		||||
	return &ErrParamMinLen{
 | 
			
		||||
		errInvalidParam: errInvalidParam{
 | 
			
		||||
			code:  ParamMinValueErrCode,
 | 
			
		||||
			field: field,
 | 
			
		||||
			msg:   fmt.Sprintf("minimum field size of %v", min),
 | 
			
		||||
		},
 | 
			
		||||
		min: min,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MinLen returns the field's required minimum length.
 | 
			
		||||
func (e *ErrParamMinLen) MinLen() int {
 | 
			
		||||
	return e.min
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +86,7 @@ func initHandlers(s *Session) {
 | 
			
		|||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//     // Create a copy of the current session, configured for the us-west-2 region.
 | 
			
		||||
//     sess.Copy(&aws.Config{Region: aws.String("us-west-2"})
 | 
			
		||||
//     sess.Copy(&aws.Config{Region: aws.String("us-west-2")})
 | 
			
		||||
func (s *Session) Copy(cfgs ...*aws.Config) *Session {
 | 
			
		||||
	newSession := &Session{
 | 
			
		||||
		Config:   s.Config.Copy(cfgs...),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,644 @@
 | 
			
		|||
// Package v4 implements signing for AWS V4 signer
 | 
			
		||||
//
 | 
			
		||||
// Provides request signing for request that need to be signed with
 | 
			
		||||
// AWS V4 Signatures.
 | 
			
		||||
package v4
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/hmac"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/credentials"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/request"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/private/protocol/rest"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	authHeaderPrefix = "AWS4-HMAC-SHA256"
 | 
			
		||||
	timeFormat       = "20060102T150405Z"
 | 
			
		||||
	shortTimeFormat  = "20060102"
 | 
			
		||||
 | 
			
		||||
	// emptyStringSHA256 is a SHA256 of an empty string
 | 
			
		||||
	emptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ignoredHeaders = rules{
 | 
			
		||||
	blacklist{
 | 
			
		||||
		mapRule{
 | 
			
		||||
			"Authorization": struct{}{},
 | 
			
		||||
			"User-Agent":    struct{}{},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// requiredSignedHeaders is a whitelist for build canonical headers.
 | 
			
		||||
var requiredSignedHeaders = rules{
 | 
			
		||||
	whitelist{
 | 
			
		||||
		mapRule{
 | 
			
		||||
			"Cache-Control":                                               struct{}{},
 | 
			
		||||
			"Content-Disposition":                                         struct{}{},
 | 
			
		||||
			"Content-Encoding":                                            struct{}{},
 | 
			
		||||
			"Content-Language":                                            struct{}{},
 | 
			
		||||
			"Content-Md5":                                                 struct{}{},
 | 
			
		||||
			"Content-Type":                                                struct{}{},
 | 
			
		||||
			"Expires":                                                     struct{}{},
 | 
			
		||||
			"If-Match":                                                    struct{}{},
 | 
			
		||||
			"If-Modified-Since":                                           struct{}{},
 | 
			
		||||
			"If-None-Match":                                               struct{}{},
 | 
			
		||||
			"If-Unmodified-Since":                                         struct{}{},
 | 
			
		||||
			"Range":                                                       struct{}{},
 | 
			
		||||
			"X-Amz-Acl":                                                   struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source":                                           struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-If-Match":                                  struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-If-Modified-Since":                         struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-If-None-Match":                             struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-If-Unmodified-Since":                       struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-Range":                                     struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key":       struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5":   struct{}{},
 | 
			
		||||
			"X-Amz-Grant-Full-control":                                    struct{}{},
 | 
			
		||||
			"X-Amz-Grant-Read":                                            struct{}{},
 | 
			
		||||
			"X-Amz-Grant-Read-Acp":                                        struct{}{},
 | 
			
		||||
			"X-Amz-Grant-Write":                                           struct{}{},
 | 
			
		||||
			"X-Amz-Grant-Write-Acp":                                       struct{}{},
 | 
			
		||||
			"X-Amz-Metadata-Directive":                                    struct{}{},
 | 
			
		||||
			"X-Amz-Mfa":                                                   struct{}{},
 | 
			
		||||
			"X-Amz-Request-Payer":                                         struct{}{},
 | 
			
		||||
			"X-Amz-Server-Side-Encryption":                                struct{}{},
 | 
			
		||||
			"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id":                 struct{}{},
 | 
			
		||||
			"X-Amz-Server-Side-Encryption-Customer-Algorithm":             struct{}{},
 | 
			
		||||
			"X-Amz-Server-Side-Encryption-Customer-Key":                   struct{}{},
 | 
			
		||||
			"X-Amz-Server-Side-Encryption-Customer-Key-Md5":               struct{}{},
 | 
			
		||||
			"X-Amz-Storage-Class":                                         struct{}{},
 | 
			
		||||
			"X-Amz-Website-Redirect-Location":                             struct{}{},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	patterns{"X-Amz-Meta-"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// allowedHoisting is a whitelist for build query headers. The boolean value
 | 
			
		||||
// represents whether or not it is a pattern.
 | 
			
		||||
var allowedQueryHoisting = inclusiveRules{
 | 
			
		||||
	blacklist{requiredSignedHeaders},
 | 
			
		||||
	patterns{"X-Amz-"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Signer applies AWS v4 signing to given request. Use this to sign requests
 | 
			
		||||
// that need to be signed with AWS V4 Signatures.
 | 
			
		||||
type Signer struct {
 | 
			
		||||
	// The authentication credentials the request will be signed against.
 | 
			
		||||
	// This value must be set to sign requests.
 | 
			
		||||
	Credentials *credentials.Credentials
 | 
			
		||||
 | 
			
		||||
	// Sets the log level the signer should use when reporting information to
 | 
			
		||||
	// the logger. If the logger is nil nothing will be logged. See
 | 
			
		||||
	// aws.LogLevelType for more information on available logging levels
 | 
			
		||||
	//
 | 
			
		||||
	// By default nothing will be logged.
 | 
			
		||||
	Debug aws.LogLevelType
 | 
			
		||||
 | 
			
		||||
	// The logger loging information will be written to. If there the logger
 | 
			
		||||
	// is nil, nothing will be logged.
 | 
			
		||||
	Logger aws.Logger
 | 
			
		||||
 | 
			
		||||
	// Disables the Signer's moving HTTP header key/value pairs from the HTTP
 | 
			
		||||
	// request header to the request's query string. This is most commonly used
 | 
			
		||||
	// with pre-signed requests preventing headers from being added to the
 | 
			
		||||
	// request's query string.
 | 
			
		||||
	DisableHeaderHoisting bool
 | 
			
		||||
 | 
			
		||||
	// currentTimeFn returns the time value which represents the current time.
 | 
			
		||||
	// This value should only be used for testing. If it is nil the default
 | 
			
		||||
	// time.Now will be used.
 | 
			
		||||
	currentTimeFn func() time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSigner returns a Signer pointer configured with the credentials and optional
 | 
			
		||||
// option values provided. If not options are provided the Signer will use its
 | 
			
		||||
// default configuration.
 | 
			
		||||
func NewSigner(credentials *credentials.Credentials, options ...func(*Signer)) *Signer {
 | 
			
		||||
	v4 := &Signer{
 | 
			
		||||
		Credentials: credentials,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, option := range options {
 | 
			
		||||
		option(v4)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return v4
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type signingCtx struct {
 | 
			
		||||
	ServiceName      string
 | 
			
		||||
	Region           string
 | 
			
		||||
	Request          *http.Request
 | 
			
		||||
	Body             io.ReadSeeker
 | 
			
		||||
	Query            url.Values
 | 
			
		||||
	Time             time.Time
 | 
			
		||||
	ExpireTime       time.Duration
 | 
			
		||||
	SignedHeaderVals http.Header
 | 
			
		||||
 | 
			
		||||
	credValues         credentials.Value
 | 
			
		||||
	isPresign          bool
 | 
			
		||||
	formattedTime      string
 | 
			
		||||
	formattedShortTime string
 | 
			
		||||
 | 
			
		||||
	bodyDigest       string
 | 
			
		||||
	signedHeaders    string
 | 
			
		||||
	canonicalHeaders string
 | 
			
		||||
	canonicalString  string
 | 
			
		||||
	credentialString string
 | 
			
		||||
	stringToSign     string
 | 
			
		||||
	signature        string
 | 
			
		||||
	authorization    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sign signs AWS v4 requests with the provided body, service name, region the
 | 
			
		||||
// request is made to, and time the request is signed at. The signTime allows
 | 
			
		||||
// you to specify that a request is signed for the future, and cannot be
 | 
			
		||||
// used until then.
 | 
			
		||||
//
 | 
			
		||||
// Returns a list of HTTP headers that were included in the signature or an
 | 
			
		||||
// error if signing the request failed. Generally for signed requests this value
 | 
			
		||||
// is not needed as the full request context will be captured by the http.Request
 | 
			
		||||
// value. It is included for reference though.
 | 
			
		||||
//
 | 
			
		||||
// Sign differs from Presign in that it will sign the request using HTTP
 | 
			
		||||
// header values. This type of signing is intended for http.Request values that
 | 
			
		||||
// will not be shared, or are shared in a way the header values on the request
 | 
			
		||||
// will not be lost.
 | 
			
		||||
//
 | 
			
		||||
// The requests body is an io.ReadSeeker so the SHA256 of the body can be
 | 
			
		||||
// generated. To bypass the signer computing the hash you can set the
 | 
			
		||||
// "X-Amz-Content-Sha256" header with a precomputed value. The signer will
 | 
			
		||||
// only compute the hash if the request header value is empty.
 | 
			
		||||
func (v4 Signer) Sign(r *http.Request, body io.ReadSeeker, service, region string, signTime time.Time) (http.Header, error) {
 | 
			
		||||
	return v4.signWithBody(r, body, service, region, 0, signTime)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Presign signs AWS v4 requests with the provided body, service name, region
 | 
			
		||||
// the request is made to, and time the request is signed at. The signTime
 | 
			
		||||
// allows you to specify that a request is signed for the future, and cannot
 | 
			
		||||
// be used until then.
 | 
			
		||||
//
 | 
			
		||||
// Returns a list of HTTP headers that were included in the signature or an
 | 
			
		||||
// error if signing the request failed. For presigned requests these headers
 | 
			
		||||
// and their values must be included on the HTTP request when it is made. This
 | 
			
		||||
// is helpful to know what header values need to be shared with the party the
 | 
			
		||||
// presigned request will be distributed to.
 | 
			
		||||
//
 | 
			
		||||
// Presign differs from Sign in that it will sign the request using query string
 | 
			
		||||
// instead of header values. This allows you to share the Presigned Request's
 | 
			
		||||
// URL with third parties, or distribute it throughout your system with minimal
 | 
			
		||||
// dependencies.
 | 
			
		||||
//
 | 
			
		||||
// Presign also takes an exp value which is the duration the
 | 
			
		||||
// signed request will be valid after the signing time. This is allows you to
 | 
			
		||||
// set when the request will expire.
 | 
			
		||||
//
 | 
			
		||||
// The requests body is an io.ReadSeeker so the SHA256 of the body can be
 | 
			
		||||
// generated. To bypass the signer computing the hash you can set the
 | 
			
		||||
// "X-Amz-Content-Sha256" header with a precomputed value. The signer will
 | 
			
		||||
// only compute the hash if the request header value is empty.
 | 
			
		||||
//
 | 
			
		||||
// Presigning a S3 request will not compute the body's SHA256 hash by default.
 | 
			
		||||
// This is done due to the general use case for S3 presigned URLs is to share
 | 
			
		||||
// PUT/GET capabilities. If you would like to include the body's SHA256 in the
 | 
			
		||||
// presigned request's signature you can set the "X-Amz-Content-Sha256"
 | 
			
		||||
// HTTP header and that will be included in the request's signature.
 | 
			
		||||
func (v4 Signer) Presign(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, signTime time.Time) (http.Header, error) {
 | 
			
		||||
	return v4.signWithBody(r, body, service, region, exp, signTime)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, signTime time.Time) (http.Header, error) {
 | 
			
		||||
	currentTimeFn := v4.currentTimeFn
 | 
			
		||||
	if currentTimeFn == nil {
 | 
			
		||||
		currentTimeFn = time.Now
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx := &signingCtx{
 | 
			
		||||
		Request:     r,
 | 
			
		||||
		Body:        body,
 | 
			
		||||
		Query:       r.URL.Query(),
 | 
			
		||||
		Time:        signTime,
 | 
			
		||||
		ExpireTime:  exp,
 | 
			
		||||
		isPresign:   exp != 0,
 | 
			
		||||
		ServiceName: service,
 | 
			
		||||
		Region:      region,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ctx.isRequestSigned() {
 | 
			
		||||
		if !v4.Credentials.IsExpired() && currentTimeFn().Before(ctx.Time.Add(10*time.Minute)) {
 | 
			
		||||
			// If the request is already signed, and the credentials have not
 | 
			
		||||
			// expired, and the request is not too old ignore the signing request.
 | 
			
		||||
			return ctx.SignedHeaderVals, nil
 | 
			
		||||
		}
 | 
			
		||||
		ctx.Time = currentTimeFn()
 | 
			
		||||
		ctx.handlePresignRemoval()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	ctx.credValues, err = v4.Credentials.Get()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return http.Header{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.assignAmzQueryValues()
 | 
			
		||||
	ctx.build(v4.DisableHeaderHoisting)
 | 
			
		||||
 | 
			
		||||
	if v4.Debug.Matches(aws.LogDebugWithSigning) {
 | 
			
		||||
		v4.logSigningInfo(ctx)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ctx.SignedHeaderVals, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *signingCtx) handlePresignRemoval() {
 | 
			
		||||
	if !ctx.isPresign {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The credentials have expired for this request. The current signing
 | 
			
		||||
	// is invalid, and needs to be request because the request will fail.
 | 
			
		||||
	ctx.removePresign()
 | 
			
		||||
 | 
			
		||||
	// Update the request's query string to ensure the values stays in
 | 
			
		||||
	// sync in the case retrieving the new credentials fails.
 | 
			
		||||
	ctx.Request.URL.RawQuery = ctx.Query.Encode()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *signingCtx) assignAmzQueryValues() {
 | 
			
		||||
	if ctx.isPresign {
 | 
			
		||||
		ctx.Query.Set("X-Amz-Algorithm", authHeaderPrefix)
 | 
			
		||||
		if ctx.credValues.SessionToken != "" {
 | 
			
		||||
			ctx.Query.Set("X-Amz-Security-Token", ctx.credValues.SessionToken)
 | 
			
		||||
		} else {
 | 
			
		||||
			ctx.Query.Del("X-Amz-Security-Token")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ctx.credValues.SessionToken != "" {
 | 
			
		||||
		ctx.Request.Header.Set("X-Amz-Security-Token", ctx.credValues.SessionToken)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SignRequestHandler is a named request handler the SDK will use to sign
 | 
			
		||||
// service client request with using the V4 signature.
 | 
			
		||||
var SignRequestHandler = request.NamedHandler{
 | 
			
		||||
	Name: "v4.SignRequestHandler", Fn: SignSDKRequest,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SignSDKRequest signs an AWS request with the V4 signature. This
 | 
			
		||||
// request handler is bested used only with the SDK's built in service client's
 | 
			
		||||
// API operation requests.
 | 
			
		||||
//
 | 
			
		||||
// This function should not be used on its on its own, but in conjunction with
 | 
			
		||||
// an AWS service client's API operation call. To sign a standalone request
 | 
			
		||||
// not created by a service client's API operation method use the "Sign" or
 | 
			
		||||
// "Presign" functions of the "Signer" type.
 | 
			
		||||
//
 | 
			
		||||
// If the credentials of the request's config are set to
 | 
			
		||||
// credentials.AnonymousCredentials the request will not be signed.
 | 
			
		||||
func SignSDKRequest(req *request.Request) {
 | 
			
		||||
	signSDKRequestWithCurrTime(req, time.Now)
 | 
			
		||||
}
 | 
			
		||||
func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time) {
 | 
			
		||||
	// If the request does not need to be signed ignore the signing of the
 | 
			
		||||
	// request if the AnonymousCredentials object is used.
 | 
			
		||||
	if req.Config.Credentials == credentials.AnonymousCredentials {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	region := req.ClientInfo.SigningRegion
 | 
			
		||||
	if region == "" {
 | 
			
		||||
		region = aws.StringValue(req.Config.Region)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name := req.ClientInfo.SigningName
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		name = req.ClientInfo.ServiceName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v4 := NewSigner(req.Config.Credentials, func(v4 *Signer) {
 | 
			
		||||
		v4.Debug = req.Config.LogLevel.Value()
 | 
			
		||||
		v4.Logger = req.Config.Logger
 | 
			
		||||
		v4.DisableHeaderHoisting = req.NotHoist
 | 
			
		||||
		v4.currentTimeFn = curTimeFn
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	signingTime := req.Time
 | 
			
		||||
	if !req.LastSignedAt.IsZero() {
 | 
			
		||||
		signingTime = req.LastSignedAt
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	signedHeaders, err := v4.signWithBody(req.HTTPRequest, req.Body, name, region, req.ExpireTime, signingTime)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		req.Error = err
 | 
			
		||||
		req.SignedHeaderVals = nil
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	req.SignedHeaderVals = signedHeaders
 | 
			
		||||
	req.LastSignedAt = curTimeFn()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const logSignInfoMsg = `DEBUG: Request Signiture:
 | 
			
		||||
---[ CANONICAL STRING  ]-----------------------------
 | 
			
		||||
%s
 | 
			
		||||
---[ STRING TO SIGN ]--------------------------------
 | 
			
		||||
%s%s
 | 
			
		||||
-----------------------------------------------------`
 | 
			
		||||
const logSignedURLMsg = `
 | 
			
		||||
---[ SIGNED URL ]------------------------------------
 | 
			
		||||
%s`
 | 
			
		||||
 | 
			
		||||
func (v4 *Signer) logSigningInfo(ctx *signingCtx) {
 | 
			
		||||
	signedURLMsg := ""
 | 
			
		||||
	if ctx.isPresign {
 | 
			
		||||
		signedURLMsg = fmt.Sprintf(logSignedURLMsg, ctx.Request.URL.String())
 | 
			
		||||
	}
 | 
			
		||||
	msg := fmt.Sprintf(logSignInfoMsg, ctx.canonicalString, ctx.stringToSign, signedURLMsg)
 | 
			
		||||
	v4.Logger.Log(msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *signingCtx) build(disableHeaderHoisting bool) {
 | 
			
		||||
	ctx.buildTime()             // no depends
 | 
			
		||||
	ctx.buildCredentialString() // no depends
 | 
			
		||||
 | 
			
		||||
	unsignedHeaders := ctx.Request.Header
 | 
			
		||||
	if ctx.isPresign {
 | 
			
		||||
		if !disableHeaderHoisting {
 | 
			
		||||
			urlValues := url.Values{}
 | 
			
		||||
			urlValues, unsignedHeaders = buildQuery(allowedQueryHoisting, unsignedHeaders) // no depends
 | 
			
		||||
			for k := range urlValues {
 | 
			
		||||
				ctx.Query[k] = urlValues[k]
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.buildBodyDigest()
 | 
			
		||||
	ctx.buildCanonicalHeaders(ignoredHeaders, unsignedHeaders)
 | 
			
		||||
	ctx.buildCanonicalString() // depends on canon headers / signed headers
 | 
			
		||||
	ctx.buildStringToSign()    // depends on canon string
 | 
			
		||||
	ctx.buildSignature()       // depends on string to sign
 | 
			
		||||
 | 
			
		||||
	if ctx.isPresign {
 | 
			
		||||
		ctx.Request.URL.RawQuery += "&X-Amz-Signature=" + ctx.signature
 | 
			
		||||
	} else {
 | 
			
		||||
		parts := []string{
 | 
			
		||||
			authHeaderPrefix + " Credential=" + ctx.credValues.AccessKeyID + "/" + ctx.credentialString,
 | 
			
		||||
			"SignedHeaders=" + ctx.signedHeaders,
 | 
			
		||||
			"Signature=" + ctx.signature,
 | 
			
		||||
		}
 | 
			
		||||
		ctx.Request.Header.Set("Authorization", strings.Join(parts, ", "))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *signingCtx) buildTime() {
 | 
			
		||||
	ctx.formattedTime = ctx.Time.UTC().Format(timeFormat)
 | 
			
		||||
	ctx.formattedShortTime = ctx.Time.UTC().Format(shortTimeFormat)
 | 
			
		||||
 | 
			
		||||
	if ctx.isPresign {
 | 
			
		||||
		duration := int64(ctx.ExpireTime / time.Second)
 | 
			
		||||
		ctx.Query.Set("X-Amz-Date", ctx.formattedTime)
 | 
			
		||||
		ctx.Query.Set("X-Amz-Expires", strconv.FormatInt(duration, 10))
 | 
			
		||||
	} else {
 | 
			
		||||
		ctx.Request.Header.Set("X-Amz-Date", ctx.formattedTime)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *signingCtx) buildCredentialString() {
 | 
			
		||||
	ctx.credentialString = strings.Join([]string{
 | 
			
		||||
		ctx.formattedShortTime,
 | 
			
		||||
		ctx.Region,
 | 
			
		||||
		ctx.ServiceName,
 | 
			
		||||
		"aws4_request",
 | 
			
		||||
	}, "/")
 | 
			
		||||
 | 
			
		||||
	if ctx.isPresign {
 | 
			
		||||
		ctx.Query.Set("X-Amz-Credential", ctx.credValues.AccessKeyID+"/"+ctx.credentialString)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func buildQuery(r rule, header http.Header) (url.Values, http.Header) {
 | 
			
		||||
	query := url.Values{}
 | 
			
		||||
	unsignedHeaders := http.Header{}
 | 
			
		||||
	for k, h := range header {
 | 
			
		||||
		if r.IsValid(k) {
 | 
			
		||||
			query[k] = h
 | 
			
		||||
		} else {
 | 
			
		||||
			unsignedHeaders[k] = h
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return query, unsignedHeaders
 | 
			
		||||
}
 | 
			
		||||
func (ctx *signingCtx) buildCanonicalHeaders(r rule, header http.Header) {
 | 
			
		||||
	var headers []string
 | 
			
		||||
	headers = append(headers, "host")
 | 
			
		||||
	for k, v := range header {
 | 
			
		||||
		canonicalKey := http.CanonicalHeaderKey(k)
 | 
			
		||||
		if !r.IsValid(canonicalKey) {
 | 
			
		||||
			continue // ignored header
 | 
			
		||||
		}
 | 
			
		||||
		if ctx.SignedHeaderVals == nil {
 | 
			
		||||
			ctx.SignedHeaderVals = make(http.Header)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lowerCaseKey := strings.ToLower(k)
 | 
			
		||||
		if _, ok := ctx.SignedHeaderVals[lowerCaseKey]; ok {
 | 
			
		||||
			// include additional values
 | 
			
		||||
			ctx.SignedHeaderVals[lowerCaseKey] = append(ctx.SignedHeaderVals[lowerCaseKey], v...)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		headers = append(headers, lowerCaseKey)
 | 
			
		||||
		ctx.SignedHeaderVals[lowerCaseKey] = v
 | 
			
		||||
	}
 | 
			
		||||
	sort.Strings(headers)
 | 
			
		||||
 | 
			
		||||
	ctx.signedHeaders = strings.Join(headers, ";")
 | 
			
		||||
 | 
			
		||||
	if ctx.isPresign {
 | 
			
		||||
		ctx.Query.Set("X-Amz-SignedHeaders", ctx.signedHeaders)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	headerValues := make([]string, len(headers))
 | 
			
		||||
	for i, k := range headers {
 | 
			
		||||
		if k == "host" {
 | 
			
		||||
			headerValues[i] = "host:" + ctx.Request.URL.Host
 | 
			
		||||
		} else {
 | 
			
		||||
			headerValues[i] = k + ":" +
 | 
			
		||||
				strings.Join(ctx.SignedHeaderVals[k], ",")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.canonicalHeaders = strings.Join(stripExcessSpaces(headerValues), "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *signingCtx) buildCanonicalString() {
 | 
			
		||||
	ctx.Request.URL.RawQuery = strings.Replace(ctx.Query.Encode(), "+", "%20", -1)
 | 
			
		||||
	uri := ctx.Request.URL.Opaque
 | 
			
		||||
	if uri != "" {
 | 
			
		||||
		uri = "/" + strings.Join(strings.Split(uri, "/")[3:], "/")
 | 
			
		||||
	} else {
 | 
			
		||||
		uri = ctx.Request.URL.Path
 | 
			
		||||
	}
 | 
			
		||||
	if uri == "" {
 | 
			
		||||
		uri = "/"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ctx.ServiceName != "s3" {
 | 
			
		||||
		uri = rest.EscapePath(uri, false)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.canonicalString = strings.Join([]string{
 | 
			
		||||
		ctx.Request.Method,
 | 
			
		||||
		uri,
 | 
			
		||||
		ctx.Request.URL.RawQuery,
 | 
			
		||||
		ctx.canonicalHeaders + "\n",
 | 
			
		||||
		ctx.signedHeaders,
 | 
			
		||||
		ctx.bodyDigest,
 | 
			
		||||
	}, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *signingCtx) buildStringToSign() {
 | 
			
		||||
	ctx.stringToSign = strings.Join([]string{
 | 
			
		||||
		authHeaderPrefix,
 | 
			
		||||
		ctx.formattedTime,
 | 
			
		||||
		ctx.credentialString,
 | 
			
		||||
		hex.EncodeToString(makeSha256([]byte(ctx.canonicalString))),
 | 
			
		||||
	}, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *signingCtx) buildSignature() {
 | 
			
		||||
	secret := ctx.credValues.SecretAccessKey
 | 
			
		||||
	date := makeHmac([]byte("AWS4"+secret), []byte(ctx.formattedShortTime))
 | 
			
		||||
	region := makeHmac(date, []byte(ctx.Region))
 | 
			
		||||
	service := makeHmac(region, []byte(ctx.ServiceName))
 | 
			
		||||
	credentials := makeHmac(service, []byte("aws4_request"))
 | 
			
		||||
	signature := makeHmac(credentials, []byte(ctx.stringToSign))
 | 
			
		||||
	ctx.signature = hex.EncodeToString(signature)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *signingCtx) buildBodyDigest() {
 | 
			
		||||
	hash := ctx.Request.Header.Get("X-Amz-Content-Sha256")
 | 
			
		||||
	if hash == "" {
 | 
			
		||||
		if ctx.isPresign && ctx.ServiceName == "s3" {
 | 
			
		||||
			hash = "UNSIGNED-PAYLOAD"
 | 
			
		||||
		} else if ctx.Body == nil {
 | 
			
		||||
			hash = emptyStringSHA256
 | 
			
		||||
		} else {
 | 
			
		||||
			hash = hex.EncodeToString(makeSha256Reader(ctx.Body))
 | 
			
		||||
		}
 | 
			
		||||
		if ctx.ServiceName == "s3" {
 | 
			
		||||
			ctx.Request.Header.Set("X-Amz-Content-Sha256", hash)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ctx.bodyDigest = hash
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// isRequestSigned returns if the request is currently signed or presigned
 | 
			
		||||
func (ctx *signingCtx) isRequestSigned() bool {
 | 
			
		||||
	if ctx.isPresign && ctx.Query.Get("X-Amz-Signature") != "" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if ctx.Request.Header.Get("Authorization") != "" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unsign removes signing flags for both signed and presigned requests.
 | 
			
		||||
func (ctx *signingCtx) removePresign() {
 | 
			
		||||
	ctx.Query.Del("X-Amz-Algorithm")
 | 
			
		||||
	ctx.Query.Del("X-Amz-Signature")
 | 
			
		||||
	ctx.Query.Del("X-Amz-Security-Token")
 | 
			
		||||
	ctx.Query.Del("X-Amz-Date")
 | 
			
		||||
	ctx.Query.Del("X-Amz-Expires")
 | 
			
		||||
	ctx.Query.Del("X-Amz-Credential")
 | 
			
		||||
	ctx.Query.Del("X-Amz-SignedHeaders")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeHmac(key []byte, data []byte) []byte {
 | 
			
		||||
	hash := hmac.New(sha256.New, key)
 | 
			
		||||
	hash.Write(data)
 | 
			
		||||
	return hash.Sum(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeSha256(data []byte) []byte {
 | 
			
		||||
	hash := sha256.New()
 | 
			
		||||
	hash.Write(data)
 | 
			
		||||
	return hash.Sum(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeSha256Reader(reader io.ReadSeeker) []byte {
 | 
			
		||||
	hash := sha256.New()
 | 
			
		||||
	start, _ := reader.Seek(0, 1)
 | 
			
		||||
	defer reader.Seek(start, 0)
 | 
			
		||||
 | 
			
		||||
	io.Copy(hash, reader)
 | 
			
		||||
	return hash.Sum(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const doubleSpaces = "  "
 | 
			
		||||
 | 
			
		||||
var doubleSpaceBytes = []byte(doubleSpaces)
 | 
			
		||||
 | 
			
		||||
func stripExcessSpaces(headerVals []string) []string {
 | 
			
		||||
	vals := make([]string, len(headerVals))
 | 
			
		||||
	for i, str := range headerVals {
 | 
			
		||||
		// Trim leading and trailing spaces
 | 
			
		||||
		trimmed := strings.TrimSpace(str)
 | 
			
		||||
 | 
			
		||||
		idx := strings.Index(trimmed, doubleSpaces)
 | 
			
		||||
		var buf []byte
 | 
			
		||||
		for idx > -1 {
 | 
			
		||||
			// Multiple adjacent spaces found
 | 
			
		||||
			if buf == nil {
 | 
			
		||||
				// first time create the buffer
 | 
			
		||||
				buf = []byte(trimmed)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			stripToIdx := -1
 | 
			
		||||
			for j := idx + 1; j < len(buf); j++ {
 | 
			
		||||
				if buf[j] != ' ' {
 | 
			
		||||
					buf = append(buf[:idx+1], buf[j:]...)
 | 
			
		||||
					stripToIdx = j
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if stripToIdx >= 0 {
 | 
			
		||||
				idx = bytes.Index(buf[stripToIdx:], doubleSpaceBytes)
 | 
			
		||||
				if idx >= 0 {
 | 
			
		||||
					idx += stripToIdx
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				idx = -1
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if buf != nil {
 | 
			
		||||
			vals[i] = string(buf)
 | 
			
		||||
		} else {
 | 
			
		||||
			vals[i] = trimmed
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return vals
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -61,23 +61,41 @@ func (r ReaderSeekerCloser) Close() error {
 | 
			
		|||
type WriteAtBuffer struct {
 | 
			
		||||
	buf []byte
 | 
			
		||||
	m   sync.Mutex
 | 
			
		||||
 | 
			
		||||
	// GrowthCoeff defines the growth rate of the internal buffer. By
 | 
			
		||||
	// default, the growth rate is 1, where expanding the internal
 | 
			
		||||
	// buffer will allocate only enough capacity to fit the new expected
 | 
			
		||||
	// length.
 | 
			
		||||
	GrowthCoeff float64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWriteAtBuffer creates a WriteAtBuffer with an internal buffer
 | 
			
		||||
// provided by buf.
 | 
			
		||||
func NewWriteAtBuffer(buf []byte) *WriteAtBuffer {
 | 
			
		||||
	return &WriteAtBuffer{buf: buf}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteAt writes a slice of bytes to a buffer starting at the position provided
 | 
			
		||||
// The number of bytes written will be returned, or error. Can overwrite previous
 | 
			
		||||
// written slices if the write ats overlap.
 | 
			
		||||
func (b *WriteAtBuffer) WriteAt(p []byte, pos int64) (n int, err error) {
 | 
			
		||||
	pLen := len(p)
 | 
			
		||||
	expLen := pos + int64(pLen)
 | 
			
		||||
	b.m.Lock()
 | 
			
		||||
	defer b.m.Unlock()
 | 
			
		||||
 | 
			
		||||
	expLen := pos + int64(len(p))
 | 
			
		||||
	if int64(len(b.buf)) < expLen {
 | 
			
		||||
		newBuf := make([]byte, expLen)
 | 
			
		||||
		copy(newBuf, b.buf)
 | 
			
		||||
		b.buf = newBuf
 | 
			
		||||
		if int64(cap(b.buf)) < expLen {
 | 
			
		||||
			if b.GrowthCoeff < 1 {
 | 
			
		||||
				b.GrowthCoeff = 1
 | 
			
		||||
			}
 | 
			
		||||
			newBuf := make([]byte, expLen, int64(b.GrowthCoeff*float64(expLen)))
 | 
			
		||||
			copy(newBuf, b.buf)
 | 
			
		||||
			b.buf = newBuf
 | 
			
		||||
		}
 | 
			
		||||
		b.buf = b.buf[:expLen]
 | 
			
		||||
	}
 | 
			
		||||
	copy(b.buf[pos:], p)
 | 
			
		||||
	return len(p), nil
 | 
			
		||||
	return pLen, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Bytes returns a slice of bytes written to the buffer.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,4 +5,4 @@ package aws
 | 
			
		|||
const SDKName = "aws-sdk-go"
 | 
			
		||||
 | 
			
		||||
// SDKVersion is the version of this SDK
 | 
			
		||||
const SDKVersion = "1.1.0"
 | 
			
		||||
const SDKVersion = "1.2.4"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,9 @@
 | 
			
		|||
      "endpoint": "{service}.{region}.amazonaws.com.cn",
 | 
			
		||||
      "signatureVersion": "v4"
 | 
			
		||||
    },
 | 
			
		||||
    "cn-north-1/ec2metadata": {
 | 
			
		||||
      "endpoint": "http://169.254.169.254/latest"
 | 
			
		||||
    },
 | 
			
		||||
    "us-gov-west-1/iam": {
 | 
			
		||||
      "endpoint": "iam.us-gov.amazonaws.com"
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +20,9 @@
 | 
			
		|||
    "us-gov-west-1/s3": {
 | 
			
		||||
      "endpoint": "s3-{region}.amazonaws.com"
 | 
			
		||||
    },
 | 
			
		||||
    "us-gov-west-1/ec2metadata": {
 | 
			
		||||
      "endpoint": "http://169.254.169.254/latest"
 | 
			
		||||
    },
 | 
			
		||||
    "*/cloudfront": {
 | 
			
		||||
      "endpoint": "cloudfront.amazonaws.com",
 | 
			
		||||
      "signingRegion": "us-east-1"
 | 
			
		||||
| 
						 | 
				
			
			@ -30,8 +36,7 @@
 | 
			
		|||
      "signingRegion": "us-east-1"
 | 
			
		||||
    },
 | 
			
		||||
    "*/ec2metadata": {
 | 
			
		||||
      "endpoint": "http://169.254.169.254/latest",
 | 
			
		||||
      "signingRegion": "us-east-1"
 | 
			
		||||
      "endpoint": "http://169.254.169.254/latest"
 | 
			
		||||
    },
 | 
			
		||||
    "*/iam": {
 | 
			
		||||
      "endpoint": "iam.amazonaws.com",
 | 
			
		||||
| 
						 | 
				
			
			@ -57,36 +62,14 @@
 | 
			
		|||
      "endpoint": "sdb.amazonaws.com",
 | 
			
		||||
      "signingRegion": "us-east-1"
 | 
			
		||||
    },
 | 
			
		||||
    "*/s3": {
 | 
			
		||||
      "endpoint": "s3-{region}.amazonaws.com"
 | 
			
		||||
    },
 | 
			
		||||
    "us-east-1/s3": {
 | 
			
		||||
      "endpoint": "s3.amazonaws.com"
 | 
			
		||||
    },
 | 
			
		||||
    "us-west-1/s3": {
 | 
			
		||||
      "endpoint": "s3-{region}.amazonaws.com"
 | 
			
		||||
    },
 | 
			
		||||
    "us-west-2/s3": {
 | 
			
		||||
      "endpoint": "s3-{region}.amazonaws.com"
 | 
			
		||||
    },
 | 
			
		||||
    "eu-west-1/s3": {
 | 
			
		||||
      "endpoint": "s3-{region}.amazonaws.com"
 | 
			
		||||
    },
 | 
			
		||||
    "ap-southeast-1/s3": {
 | 
			
		||||
      "endpoint": "s3-{region}.amazonaws.com"
 | 
			
		||||
    },
 | 
			
		||||
    "ap-southeast-2/s3": {
 | 
			
		||||
      "endpoint": "s3-{region}.amazonaws.com"
 | 
			
		||||
    },
 | 
			
		||||
    "ap-northeast-1/s3": {
 | 
			
		||||
      "endpoint": "s3-{region}.amazonaws.com"
 | 
			
		||||
    },
 | 
			
		||||
    "ap-northeast-2/s3": {
 | 
			
		||||
      "endpoint": "s3-{region}.amazonaws.com"
 | 
			
		||||
    },
 | 
			
		||||
    "sa-east-1/s3": {
 | 
			
		||||
      "endpoint": "s3-{region}.amazonaws.com"
 | 
			
		||||
    },
 | 
			
		||||
    "eu-central-1/s3": {
 | 
			
		||||
      "endpoint": "{service}.{region}.amazonaws.com",
 | 
			
		||||
      "signatureVersion": "v4"
 | 
			
		||||
      "endpoint": "{service}.{region}.amazonaws.com"
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,8 +31,7 @@ var endpointsMap = endpointStruct{
 | 
			
		|||
			SigningRegion: "us-east-1",
 | 
			
		||||
		},
 | 
			
		||||
		"*/ec2metadata": {
 | 
			
		||||
			Endpoint:      "http://169.254.169.254/latest",
 | 
			
		||||
			SigningRegion: "us-east-1",
 | 
			
		||||
			Endpoint: "http://169.254.169.254/latest",
 | 
			
		||||
		},
 | 
			
		||||
		"*/iam": {
 | 
			
		||||
			Endpoint:      "iam.amazonaws.com",
 | 
			
		||||
| 
						 | 
				
			
			@ -46,6 +45,9 @@ var endpointsMap = endpointStruct{
 | 
			
		|||
			Endpoint:      "route53.amazonaws.com",
 | 
			
		||||
			SigningRegion: "us-east-1",
 | 
			
		||||
		},
 | 
			
		||||
		"*/s3": {
 | 
			
		||||
			Endpoint: "s3-{region}.amazonaws.com",
 | 
			
		||||
		},
 | 
			
		||||
		"*/sts": {
 | 
			
		||||
			Endpoint:      "sts.amazonaws.com",
 | 
			
		||||
			SigningRegion: "us-east-1",
 | 
			
		||||
| 
						 | 
				
			
			@ -54,30 +56,15 @@ var endpointsMap = endpointStruct{
 | 
			
		|||
			Endpoint:      "waf.amazonaws.com",
 | 
			
		||||
			SigningRegion: "us-east-1",
 | 
			
		||||
		},
 | 
			
		||||
		"ap-northeast-1/s3": {
 | 
			
		||||
			Endpoint: "s3-{region}.amazonaws.com",
 | 
			
		||||
		},
 | 
			
		||||
		"ap-northeast-2/s3": {
 | 
			
		||||
			Endpoint: "s3-{region}.amazonaws.com",
 | 
			
		||||
		},
 | 
			
		||||
		"ap-southeast-1/s3": {
 | 
			
		||||
			Endpoint: "s3-{region}.amazonaws.com",
 | 
			
		||||
		},
 | 
			
		||||
		"ap-southeast-2/s3": {
 | 
			
		||||
			Endpoint: "s3-{region}.amazonaws.com",
 | 
			
		||||
		},
 | 
			
		||||
		"cn-north-1/*": {
 | 
			
		||||
			Endpoint: "{service}.{region}.amazonaws.com.cn",
 | 
			
		||||
		},
 | 
			
		||||
		"cn-north-1/ec2metadata": {
 | 
			
		||||
			Endpoint: "http://169.254.169.254/latest",
 | 
			
		||||
		},
 | 
			
		||||
		"eu-central-1/s3": {
 | 
			
		||||
			Endpoint: "{service}.{region}.amazonaws.com",
 | 
			
		||||
		},
 | 
			
		||||
		"eu-west-1/s3": {
 | 
			
		||||
			Endpoint: "s3-{region}.amazonaws.com",
 | 
			
		||||
		},
 | 
			
		||||
		"sa-east-1/s3": {
 | 
			
		||||
			Endpoint: "s3-{region}.amazonaws.com",
 | 
			
		||||
		},
 | 
			
		||||
		"us-east-1/s3": {
 | 
			
		||||
			Endpoint: "s3.amazonaws.com",
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +72,9 @@ var endpointsMap = endpointStruct{
 | 
			
		|||
			Endpoint:      "sdb.amazonaws.com",
 | 
			
		||||
			SigningRegion: "us-east-1",
 | 
			
		||||
		},
 | 
			
		||||
		"us-gov-west-1/ec2metadata": {
 | 
			
		||||
			Endpoint: "http://169.254.169.254/latest",
 | 
			
		||||
		},
 | 
			
		||||
		"us-gov-west-1/iam": {
 | 
			
		||||
			Endpoint: "iam.us-gov.amazonaws.com",
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -94,11 +84,5 @@ var endpointsMap = endpointStruct{
 | 
			
		|||
		"us-gov-west-1/sts": {
 | 
			
		||||
			Endpoint: "sts.us-gov-west-1.amazonaws.com",
 | 
			
		||||
		},
 | 
			
		||||
		"us-west-1/s3": {
 | 
			
		||||
			Endpoint: "s3-{region}.amazonaws.com",
 | 
			
		||||
		},
 | 
			
		||||
		"us-west-2/s3": {
 | 
			
		||||
			Endpoint: "s3-{region}.amazonaws.com",
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ package query
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/awserr"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/request"
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +15,10 @@ type xmlErrorResponse struct {
 | 
			
		|||
	RequestID string   `xml:"RequestId"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type xmlServiceUnavailableResponse struct {
 | 
			
		||||
	XMLName xml.Name `xml:"ServiceUnavailableException"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalErrorHandler is a name request handler to unmarshal request errors
 | 
			
		||||
var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.query.UnmarshalError", Fn: UnmarshalError}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -22,11 +26,16 @@ var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.query.UnmarshalEr
 | 
			
		|||
func UnmarshalError(r *request.Request) {
 | 
			
		||||
	defer r.HTTPResponse.Body.Close()
 | 
			
		||||
 | 
			
		||||
	resp := &xmlErrorResponse{}
 | 
			
		||||
	err := xml.NewDecoder(r.HTTPResponse.Body).Decode(resp)
 | 
			
		||||
	if err != nil && err != io.EOF {
 | 
			
		||||
		r.Error = awserr.New("SerializationError", "failed to decode query XML error response", err)
 | 
			
		||||
	} else {
 | 
			
		||||
	bodyBytes, err := ioutil.ReadAll(r.HTTPResponse.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		r.Error = awserr.New("SerializationError", "failed to read from query HTTP response body", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// First check for specific error
 | 
			
		||||
	resp := xmlErrorResponse{}
 | 
			
		||||
	decodeErr := xml.Unmarshal(bodyBytes, &resp)
 | 
			
		||||
	if decodeErr == nil {
 | 
			
		||||
		reqID := resp.RequestID
 | 
			
		||||
		if reqID == "" {
 | 
			
		||||
			reqID = r.RequestID
 | 
			
		||||
| 
						 | 
				
			
			@ -36,5 +45,22 @@ func UnmarshalError(r *request.Request) {
 | 
			
		|||
			r.HTTPResponse.StatusCode,
 | 
			
		||||
			reqID,
 | 
			
		||||
		)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check for unhandled error
 | 
			
		||||
	servUnavailResp := xmlServiceUnavailableResponse{}
 | 
			
		||||
	unavailErr := xml.Unmarshal(bodyBytes, &servUnavailResp)
 | 
			
		||||
	if unavailErr == nil {
 | 
			
		||||
		r.Error = awserr.NewRequestFailure(
 | 
			
		||||
			awserr.New("ServiceUnavailableException", "service is unavailable", nil),
 | 
			
		||||
			r.HTTPResponse.StatusCode,
 | 
			
		||||
			r.RequestID,
 | 
			
		||||
		)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Failed to retrieve any error message from the response body
 | 
			
		||||
	r.Error = awserr.New("SerializationError",
 | 
			
		||||
		"failed to decode query XML error response", decodeErr)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -222,8 +222,7 @@ func EscapePath(path string, encodeSep bool) string {
 | 
			
		|||
		if noEscape[c] || (c == '/' && !encodeSep) {
 | 
			
		||||
			buf.WriteByte(c)
 | 
			
		||||
		} else {
 | 
			
		||||
			buf.WriteByte('%')
 | 
			
		||||
			buf.WriteString(strings.ToUpper(strconv.FormatUint(uint64(c), 16)))
 | 
			
		||||
			fmt.Fprintf(&buf, "%%%02X", c)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return buf.String()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package rest
 | 
			
		|||
import (
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"reflect"
 | 
			
		||||
| 
						 | 
				
			
			@ -51,6 +52,7 @@ func unmarshalBody(r *request.Request, v reflect.Value) {
 | 
			
		|||
				if payload.IsValid() {
 | 
			
		||||
					switch payload.Interface().(type) {
 | 
			
		||||
					case []byte:
 | 
			
		||||
						defer r.HTTPResponse.Body.Close()
 | 
			
		||||
						b, err := ioutil.ReadAll(r.HTTPResponse.Body)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							r.Error = awserr.New("SerializationError", "failed to decode REST response", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -58,6 +60,7 @@ func unmarshalBody(r *request.Request, v reflect.Value) {
 | 
			
		|||
							payload.Set(reflect.ValueOf(b))
 | 
			
		||||
						}
 | 
			
		||||
					case *string:
 | 
			
		||||
						defer r.HTTPResponse.Body.Close()
 | 
			
		||||
						b, err := ioutil.ReadAll(r.HTTPResponse.Body)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							r.Error = awserr.New("SerializationError", "failed to decode REST response", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -72,6 +75,8 @@ func unmarshalBody(r *request.Request, v reflect.Value) {
 | 
			
		|||
						case "aws.ReadSeekCloser", "io.ReadCloser":
 | 
			
		||||
							payload.Set(reflect.ValueOf(r.HTTPResponse.Body))
 | 
			
		||||
						default:
 | 
			
		||||
							io.Copy(ioutil.Discard, r.HTTPResponse.Body)
 | 
			
		||||
							defer r.HTTPResponse.Body.Close()
 | 
			
		||||
							r.Error = awserr.New("SerializationError",
 | 
			
		||||
								"failed to decode REST response",
 | 
			
		||||
								fmt.Errorf("unknown payload type %s", payload.Type()))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,438 +0,0 @@
 | 
			
		|||
// Package v4 implements signing for AWS V4 signer
 | 
			
		||||
package v4
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/hmac"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/credentials"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/request"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/private/protocol/rest"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	authHeaderPrefix = "AWS4-HMAC-SHA256"
 | 
			
		||||
	timeFormat       = "20060102T150405Z"
 | 
			
		||||
	shortTimeFormat  = "20060102"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ignoredHeaders = rules{
 | 
			
		||||
	blacklist{
 | 
			
		||||
		mapRule{
 | 
			
		||||
			"Content-Length": struct{}{},
 | 
			
		||||
			"User-Agent":     struct{}{},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// requiredSignedHeaders is a whitelist for build canonical headers.
 | 
			
		||||
var requiredSignedHeaders = rules{
 | 
			
		||||
	whitelist{
 | 
			
		||||
		mapRule{
 | 
			
		||||
			"Cache-Control":                                               struct{}{},
 | 
			
		||||
			"Content-Disposition":                                         struct{}{},
 | 
			
		||||
			"Content-Encoding":                                            struct{}{},
 | 
			
		||||
			"Content-Language":                                            struct{}{},
 | 
			
		||||
			"Content-Md5":                                                 struct{}{},
 | 
			
		||||
			"Content-Type":                                                struct{}{},
 | 
			
		||||
			"Expires":                                                     struct{}{},
 | 
			
		||||
			"If-Match":                                                    struct{}{},
 | 
			
		||||
			"If-Modified-Since":                                           struct{}{},
 | 
			
		||||
			"If-None-Match":                                               struct{}{},
 | 
			
		||||
			"If-Unmodified-Since":                                         struct{}{},
 | 
			
		||||
			"Range":                                                       struct{}{},
 | 
			
		||||
			"X-Amz-Acl":                                                   struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source":                                           struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-If-Match":                                  struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-If-Modified-Since":                         struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-If-None-Match":                             struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-If-Unmodified-Since":                       struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-Range":                                     struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key":       struct{}{},
 | 
			
		||||
			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5":   struct{}{},
 | 
			
		||||
			"X-Amz-Grant-Full-control":                                    struct{}{},
 | 
			
		||||
			"X-Amz-Grant-Read":                                            struct{}{},
 | 
			
		||||
			"X-Amz-Grant-Read-Acp":                                        struct{}{},
 | 
			
		||||
			"X-Amz-Grant-Write":                                           struct{}{},
 | 
			
		||||
			"X-Amz-Grant-Write-Acp":                                       struct{}{},
 | 
			
		||||
			"X-Amz-Metadata-Directive":                                    struct{}{},
 | 
			
		||||
			"X-Amz-Mfa":                                                   struct{}{},
 | 
			
		||||
			"X-Amz-Request-Payer":                                         struct{}{},
 | 
			
		||||
			"X-Amz-Server-Side-Encryption":                                struct{}{},
 | 
			
		||||
			"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id":                 struct{}{},
 | 
			
		||||
			"X-Amz-Server-Side-Encryption-Customer-Algorithm":             struct{}{},
 | 
			
		||||
			"X-Amz-Server-Side-Encryption-Customer-Key":                   struct{}{},
 | 
			
		||||
			"X-Amz-Server-Side-Encryption-Customer-Key-Md5":               struct{}{},
 | 
			
		||||
			"X-Amz-Storage-Class":                                         struct{}{},
 | 
			
		||||
			"X-Amz-Website-Redirect-Location":                             struct{}{},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	patterns{"X-Amz-Meta-"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// allowedHoisting is a whitelist for build query headers. The boolean value
 | 
			
		||||
// represents whether or not it is a pattern.
 | 
			
		||||
var allowedQueryHoisting = inclusiveRules{
 | 
			
		||||
	blacklist{requiredSignedHeaders},
 | 
			
		||||
	patterns{"X-Amz-"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type signer struct {
 | 
			
		||||
	Request     *http.Request
 | 
			
		||||
	Time        time.Time
 | 
			
		||||
	ExpireTime  time.Duration
 | 
			
		||||
	ServiceName string
 | 
			
		||||
	Region      string
 | 
			
		||||
	CredValues  credentials.Value
 | 
			
		||||
	Credentials *credentials.Credentials
 | 
			
		||||
	Query       url.Values
 | 
			
		||||
	Body        io.ReadSeeker
 | 
			
		||||
	Debug       aws.LogLevelType
 | 
			
		||||
	Logger      aws.Logger
 | 
			
		||||
 | 
			
		||||
	isPresign          bool
 | 
			
		||||
	formattedTime      string
 | 
			
		||||
	formattedShortTime string
 | 
			
		||||
 | 
			
		||||
	signedHeaders    string
 | 
			
		||||
	canonicalHeaders string
 | 
			
		||||
	canonicalString  string
 | 
			
		||||
	credentialString string
 | 
			
		||||
	stringToSign     string
 | 
			
		||||
	signature        string
 | 
			
		||||
	authorization    string
 | 
			
		||||
	notHoist         bool
 | 
			
		||||
	signedHeaderVals http.Header
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sign requests with signature version 4.
 | 
			
		||||
//
 | 
			
		||||
// Will sign the requests with the service config's Credentials object
 | 
			
		||||
// Signing is skipped if the credentials is the credentials.AnonymousCredentials
 | 
			
		||||
// object.
 | 
			
		||||
func Sign(req *request.Request) {
 | 
			
		||||
	// If the request does not need to be signed ignore the signing of the
 | 
			
		||||
	// request if the AnonymousCredentials object is used.
 | 
			
		||||
	if req.Config.Credentials == credentials.AnonymousCredentials {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	region := req.ClientInfo.SigningRegion
 | 
			
		||||
	if region == "" {
 | 
			
		||||
		region = aws.StringValue(req.Config.Region)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name := req.ClientInfo.SigningName
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		name = req.ClientInfo.ServiceName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s := signer{
 | 
			
		||||
		Request:     req.HTTPRequest,
 | 
			
		||||
		Time:        req.Time,
 | 
			
		||||
		ExpireTime:  req.ExpireTime,
 | 
			
		||||
		Query:       req.HTTPRequest.URL.Query(),
 | 
			
		||||
		Body:        req.Body,
 | 
			
		||||
		ServiceName: name,
 | 
			
		||||
		Region:      region,
 | 
			
		||||
		Credentials: req.Config.Credentials,
 | 
			
		||||
		Debug:       req.Config.LogLevel.Value(),
 | 
			
		||||
		Logger:      req.Config.Logger,
 | 
			
		||||
		notHoist:    req.NotHoist,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	req.Error = s.sign()
 | 
			
		||||
	req.SignedHeaderVals = s.signedHeaderVals
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v4 *signer) sign() error {
 | 
			
		||||
	if v4.ExpireTime != 0 {
 | 
			
		||||
		v4.isPresign = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v4.isRequestSigned() {
 | 
			
		||||
		if !v4.Credentials.IsExpired() {
 | 
			
		||||
			// If the request is already signed, and the credentials have not
 | 
			
		||||
			// expired yet ignore the signing request.
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// The credentials have expired for this request. The current signing
 | 
			
		||||
		// is invalid, and needs to be request because the request will fail.
 | 
			
		||||
		if v4.isPresign {
 | 
			
		||||
			v4.removePresign()
 | 
			
		||||
			// Update the request's query string to ensure the values stays in
 | 
			
		||||
			// sync in the case retrieving the new credentials fails.
 | 
			
		||||
			v4.Request.URL.RawQuery = v4.Query.Encode()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	v4.CredValues, err = v4.Credentials.Get()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v4.isPresign {
 | 
			
		||||
		v4.Query.Set("X-Amz-Algorithm", authHeaderPrefix)
 | 
			
		||||
		if v4.CredValues.SessionToken != "" {
 | 
			
		||||
			v4.Query.Set("X-Amz-Security-Token", v4.CredValues.SessionToken)
 | 
			
		||||
		} else {
 | 
			
		||||
			v4.Query.Del("X-Amz-Security-Token")
 | 
			
		||||
		}
 | 
			
		||||
	} else if v4.CredValues.SessionToken != "" {
 | 
			
		||||
		v4.Request.Header.Set("X-Amz-Security-Token", v4.CredValues.SessionToken)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v4.build()
 | 
			
		||||
 | 
			
		||||
	if v4.Debug.Matches(aws.LogDebugWithSigning) {
 | 
			
		||||
		v4.logSigningInfo()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const logSignInfoMsg = `DEBUG: Request Signiture:
 | 
			
		||||
---[ CANONICAL STRING  ]-----------------------------
 | 
			
		||||
%s
 | 
			
		||||
---[ STRING TO SIGN ]--------------------------------
 | 
			
		||||
%s%s
 | 
			
		||||
-----------------------------------------------------`
 | 
			
		||||
const logSignedURLMsg = `
 | 
			
		||||
---[ SIGNED URL ]------------------------------------
 | 
			
		||||
%s`
 | 
			
		||||
 | 
			
		||||
func (v4 *signer) logSigningInfo() {
 | 
			
		||||
	signedURLMsg := ""
 | 
			
		||||
	if v4.isPresign {
 | 
			
		||||
		signedURLMsg = fmt.Sprintf(logSignedURLMsg, v4.Request.URL.String())
 | 
			
		||||
	}
 | 
			
		||||
	msg := fmt.Sprintf(logSignInfoMsg, v4.canonicalString, v4.stringToSign, signedURLMsg)
 | 
			
		||||
	v4.Logger.Log(msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v4 *signer) build() {
 | 
			
		||||
 | 
			
		||||
	v4.buildTime()             // no depends
 | 
			
		||||
	v4.buildCredentialString() // no depends
 | 
			
		||||
 | 
			
		||||
	unsignedHeaders := v4.Request.Header
 | 
			
		||||
	if v4.isPresign {
 | 
			
		||||
		if !v4.notHoist {
 | 
			
		||||
			urlValues := url.Values{}
 | 
			
		||||
			urlValues, unsignedHeaders = buildQuery(allowedQueryHoisting, unsignedHeaders) // no depends
 | 
			
		||||
			for k := range urlValues {
 | 
			
		||||
				v4.Query[k] = urlValues[k]
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v4.buildCanonicalHeaders(ignoredHeaders, unsignedHeaders)
 | 
			
		||||
	v4.buildCanonicalString() // depends on canon headers / signed headers
 | 
			
		||||
	v4.buildStringToSign()    // depends on canon string
 | 
			
		||||
	v4.buildSignature()       // depends on string to sign
 | 
			
		||||
 | 
			
		||||
	if v4.isPresign {
 | 
			
		||||
		v4.Request.URL.RawQuery += "&X-Amz-Signature=" + v4.signature
 | 
			
		||||
	} else {
 | 
			
		||||
		parts := []string{
 | 
			
		||||
			authHeaderPrefix + " Credential=" + v4.CredValues.AccessKeyID + "/" + v4.credentialString,
 | 
			
		||||
			"SignedHeaders=" + v4.signedHeaders,
 | 
			
		||||
			"Signature=" + v4.signature,
 | 
			
		||||
		}
 | 
			
		||||
		v4.Request.Header.Set("Authorization", strings.Join(parts, ", "))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v4 *signer) buildTime() {
 | 
			
		||||
	v4.formattedTime = v4.Time.UTC().Format(timeFormat)
 | 
			
		||||
	v4.formattedShortTime = v4.Time.UTC().Format(shortTimeFormat)
 | 
			
		||||
 | 
			
		||||
	if v4.isPresign {
 | 
			
		||||
		duration := int64(v4.ExpireTime / time.Second)
 | 
			
		||||
		v4.Query.Set("X-Amz-Date", v4.formattedTime)
 | 
			
		||||
		v4.Query.Set("X-Amz-Expires", strconv.FormatInt(duration, 10))
 | 
			
		||||
	} else {
 | 
			
		||||
		v4.Request.Header.Set("X-Amz-Date", v4.formattedTime)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v4 *signer) buildCredentialString() {
 | 
			
		||||
	v4.credentialString = strings.Join([]string{
 | 
			
		||||
		v4.formattedShortTime,
 | 
			
		||||
		v4.Region,
 | 
			
		||||
		v4.ServiceName,
 | 
			
		||||
		"aws4_request",
 | 
			
		||||
	}, "/")
 | 
			
		||||
 | 
			
		||||
	if v4.isPresign {
 | 
			
		||||
		v4.Query.Set("X-Amz-Credential", v4.CredValues.AccessKeyID+"/"+v4.credentialString)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func buildQuery(r rule, header http.Header) (url.Values, http.Header) {
 | 
			
		||||
	query := url.Values{}
 | 
			
		||||
	unsignedHeaders := http.Header{}
 | 
			
		||||
	for k, h := range header {
 | 
			
		||||
		if r.IsValid(k) {
 | 
			
		||||
			query[k] = h
 | 
			
		||||
		} else {
 | 
			
		||||
			unsignedHeaders[k] = h
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return query, unsignedHeaders
 | 
			
		||||
}
 | 
			
		||||
func (v4 *signer) buildCanonicalHeaders(r rule, header http.Header) {
 | 
			
		||||
	var headers []string
 | 
			
		||||
	headers = append(headers, "host")
 | 
			
		||||
	for k, v := range header {
 | 
			
		||||
		canonicalKey := http.CanonicalHeaderKey(k)
 | 
			
		||||
		if !r.IsValid(canonicalKey) {
 | 
			
		||||
			continue // ignored header
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lowerCaseKey := strings.ToLower(k)
 | 
			
		||||
		headers = append(headers, lowerCaseKey)
 | 
			
		||||
 | 
			
		||||
		if v4.signedHeaderVals == nil {
 | 
			
		||||
			v4.signedHeaderVals = make(http.Header)
 | 
			
		||||
		}
 | 
			
		||||
		v4.signedHeaderVals[lowerCaseKey] = v
 | 
			
		||||
	}
 | 
			
		||||
	sort.Strings(headers)
 | 
			
		||||
 | 
			
		||||
	v4.signedHeaders = strings.Join(headers, ";")
 | 
			
		||||
 | 
			
		||||
	if v4.isPresign {
 | 
			
		||||
		v4.Query.Set("X-Amz-SignedHeaders", v4.signedHeaders)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	headerValues := make([]string, len(headers))
 | 
			
		||||
	for i, k := range headers {
 | 
			
		||||
		if k == "host" {
 | 
			
		||||
			headerValues[i] = "host:" + v4.Request.URL.Host
 | 
			
		||||
		} else {
 | 
			
		||||
			headerValues[i] = k + ":" +
 | 
			
		||||
				strings.Join(v4.Request.Header[http.CanonicalHeaderKey(k)], ",")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v4.canonicalHeaders = strings.Join(headerValues, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v4 *signer) buildCanonicalString() {
 | 
			
		||||
	v4.Request.URL.RawQuery = strings.Replace(v4.Query.Encode(), "+", "%20", -1)
 | 
			
		||||
	uri := v4.Request.URL.Opaque
 | 
			
		||||
	if uri != "" {
 | 
			
		||||
		uri = "/" + strings.Join(strings.Split(uri, "/")[3:], "/")
 | 
			
		||||
	} else {
 | 
			
		||||
		uri = v4.Request.URL.Path
 | 
			
		||||
	}
 | 
			
		||||
	if uri == "" {
 | 
			
		||||
		uri = "/"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v4.ServiceName != "s3" {
 | 
			
		||||
		uri = rest.EscapePath(uri, false)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v4.canonicalString = strings.Join([]string{
 | 
			
		||||
		v4.Request.Method,
 | 
			
		||||
		uri,
 | 
			
		||||
		v4.Request.URL.RawQuery,
 | 
			
		||||
		v4.canonicalHeaders + "\n",
 | 
			
		||||
		v4.signedHeaders,
 | 
			
		||||
		v4.bodyDigest(),
 | 
			
		||||
	}, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v4 *signer) buildStringToSign() {
 | 
			
		||||
	v4.stringToSign = strings.Join([]string{
 | 
			
		||||
		authHeaderPrefix,
 | 
			
		||||
		v4.formattedTime,
 | 
			
		||||
		v4.credentialString,
 | 
			
		||||
		hex.EncodeToString(makeSha256([]byte(v4.canonicalString))),
 | 
			
		||||
	}, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v4 *signer) buildSignature() {
 | 
			
		||||
	secret := v4.CredValues.SecretAccessKey
 | 
			
		||||
	date := makeHmac([]byte("AWS4"+secret), []byte(v4.formattedShortTime))
 | 
			
		||||
	region := makeHmac(date, []byte(v4.Region))
 | 
			
		||||
	service := makeHmac(region, []byte(v4.ServiceName))
 | 
			
		||||
	credentials := makeHmac(service, []byte("aws4_request"))
 | 
			
		||||
	signature := makeHmac(credentials, []byte(v4.stringToSign))
 | 
			
		||||
	v4.signature = hex.EncodeToString(signature)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v4 *signer) bodyDigest() string {
 | 
			
		||||
	hash := v4.Request.Header.Get("X-Amz-Content-Sha256")
 | 
			
		||||
	if hash == "" {
 | 
			
		||||
		if v4.isPresign && v4.ServiceName == "s3" {
 | 
			
		||||
			hash = "UNSIGNED-PAYLOAD"
 | 
			
		||||
		} else if v4.Body == nil {
 | 
			
		||||
			hash = hex.EncodeToString(makeSha256([]byte{}))
 | 
			
		||||
		} else {
 | 
			
		||||
			hash = hex.EncodeToString(makeSha256Reader(v4.Body))
 | 
			
		||||
		}
 | 
			
		||||
		v4.Request.Header.Add("X-Amz-Content-Sha256", hash)
 | 
			
		||||
	}
 | 
			
		||||
	return hash
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// isRequestSigned returns if the request is currently signed or presigned
 | 
			
		||||
func (v4 *signer) isRequestSigned() bool {
 | 
			
		||||
	if v4.isPresign && v4.Query.Get("X-Amz-Signature") != "" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if v4.Request.Header.Get("Authorization") != "" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unsign removes signing flags for both signed and presigned requests.
 | 
			
		||||
func (v4 *signer) removePresign() {
 | 
			
		||||
	v4.Query.Del("X-Amz-Algorithm")
 | 
			
		||||
	v4.Query.Del("X-Amz-Signature")
 | 
			
		||||
	v4.Query.Del("X-Amz-Security-Token")
 | 
			
		||||
	v4.Query.Del("X-Amz-Date")
 | 
			
		||||
	v4.Query.Del("X-Amz-Expires")
 | 
			
		||||
	v4.Query.Del("X-Amz-Credential")
 | 
			
		||||
	v4.Query.Del("X-Amz-SignedHeaders")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeHmac(key []byte, data []byte) []byte {
 | 
			
		||||
	hash := hmac.New(sha256.New, key)
 | 
			
		||||
	hash.Write(data)
 | 
			
		||||
	return hash.Sum(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeSha256(data []byte) []byte {
 | 
			
		||||
	hash := sha256.New()
 | 
			
		||||
	hash.Write(data)
 | 
			
		||||
	return hash.Sum(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeSha256Reader(reader io.ReadSeeker) []byte {
 | 
			
		||||
	hash := sha256.New()
 | 
			
		||||
	start, _ := reader.Seek(0, 1)
 | 
			
		||||
	defer reader.Seek(start, 0)
 | 
			
		||||
 | 
			
		||||
	io.Copy(hash, reader)
 | 
			
		||||
	return hash.Sum(nil)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -13,6 +13,7 @@ import (
 | 
			
		|||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
	"unicode"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// An AWSEpochTime wraps a time value providing JSON serialization needed for
 | 
			
		||||
| 
						 | 
				
			
			@ -110,6 +111,12 @@ func (p *Policy) Validate() error {
 | 
			
		|||
		if s.Resource == "" {
 | 
			
		||||
			return fmt.Errorf("statement at index %d does not have a resource", i)
 | 
			
		||||
		}
 | 
			
		||||
		if !isASCII(s.Resource) {
 | 
			
		||||
			return fmt.Errorf("unable to sign resource, [%s]. "+
 | 
			
		||||
				"Resources must only contain ascii characters. "+
 | 
			
		||||
				"Hostnames with unicode should be encoded as Punycode, (e.g. golang.org/x/net/idna), "+
 | 
			
		||||
				"and URL unicode path/query characters should be escaped.", s.Resource)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +127,7 @@ func (p *Policy) Validate() error {
 | 
			
		|||
func CreateResource(scheme, u string) (string, error) {
 | 
			
		||||
	scheme = strings.ToLower(scheme)
 | 
			
		||||
 | 
			
		||||
	if scheme == "http" || scheme == "https" {
 | 
			
		||||
	if scheme == "http" || scheme == "https" || scheme == "http*" || scheme == "*" {
 | 
			
		||||
		return u, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -208,3 +215,12 @@ func awsEscapeEncoded(b []byte) {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isASCII(u string) bool {
 | 
			
		||||
	for _, c := range u {
 | 
			
		||||
		if c > unicode.MaxASCII {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										241
									
								
								vendor/github.com/aws/aws-sdk-go/service/cloudfront/sign/sign_cookie.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										241
									
								
								vendor/github.com/aws/aws-sdk-go/service/cloudfront/sign/sign_cookie.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,241 @@
 | 
			
		|||
package sign
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// CookiePolicyName name of the policy cookie
 | 
			
		||||
	CookiePolicyName = "CloudFront-Policy"
 | 
			
		||||
	// CookieSignatureName name of the signature cookie
 | 
			
		||||
	CookieSignatureName = "CloudFront-Signature"
 | 
			
		||||
	// CookieKeyIDName name of the signing Key ID cookie
 | 
			
		||||
	CookieKeyIDName = "CloudFront-Key-Pair-Id"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A CookieOptions optional additonal options that can be applied to the signed
 | 
			
		||||
// cookies.
 | 
			
		||||
type CookieOptions struct {
 | 
			
		||||
	Path   string
 | 
			
		||||
	Domain string
 | 
			
		||||
	Secure bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// apply will integration the options provided into the base cookie options
 | 
			
		||||
// a new copy will be returned. The base CookieOption will not be modified.
 | 
			
		||||
func (o CookieOptions) apply(opts ...func(*CookieOptions)) CookieOptions {
 | 
			
		||||
	if len(opts) == 0 {
 | 
			
		||||
		return o
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, opt := range opts {
 | 
			
		||||
		opt(&o)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return o
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A CookieSigner provides signing utilities to sign Cookies for Amazon CloudFront
 | 
			
		||||
// resources. Using a private key and Credential Key Pair key ID the CookieSigner
 | 
			
		||||
// only needs to be created once per Credential Key Pair key ID and private key.
 | 
			
		||||
//
 | 
			
		||||
// More information about signed Cookies and their structure can be found at:
 | 
			
		||||
// http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html
 | 
			
		||||
//
 | 
			
		||||
// To sign a Cookie, create a CookieSigner with your private key and credential
 | 
			
		||||
// pair key ID. Once you have a CookieSigner instance you can call Sign or
 | 
			
		||||
// SignWithPolicy to sign the URLs.
 | 
			
		||||
//
 | 
			
		||||
// The signer is safe to use concurrently, but the optional cookies options
 | 
			
		||||
// are not safe to modify concurrently.
 | 
			
		||||
type CookieSigner struct {
 | 
			
		||||
	keyID   string
 | 
			
		||||
	privKey *rsa.PrivateKey
 | 
			
		||||
 | 
			
		||||
	Opts CookieOptions
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCookieSigner constructs and returns a new CookieSigner to be used to for
 | 
			
		||||
// signing Amazon CloudFront URL resources with.
 | 
			
		||||
func NewCookieSigner(keyID string, privKey *rsa.PrivateKey, opts ...func(*CookieOptions)) *CookieSigner {
 | 
			
		||||
	signer := &CookieSigner{
 | 
			
		||||
		keyID:   keyID,
 | 
			
		||||
		privKey: privKey,
 | 
			
		||||
		Opts:    CookieOptions{}.apply(opts...),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return signer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sign returns the cookies needed to allow user agents to make arbetrary
 | 
			
		||||
// requests to cloudfront for the resource(s) defined by the policy.
 | 
			
		||||
//
 | 
			
		||||
// Sign will create a CloudFront policy with only a resource and condition of
 | 
			
		||||
// DateLessThan equal to the expires time provided.
 | 
			
		||||
//
 | 
			
		||||
// The returned slice cookies should all be added to the Client's cookies or
 | 
			
		||||
// server's response.
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//    s := NewCookieSigner(keyID, privKey)
 | 
			
		||||
//
 | 
			
		||||
//    // Get Signed cookies for a resource that will expire in 1 hour
 | 
			
		||||
//    cookies, err := s.Sign("*", time.Now().Add(1 * time.Hour))
 | 
			
		||||
//    if err != nil {
 | 
			
		||||
//        fmt.Println("failed to create signed cookies", err)
 | 
			
		||||
//        return
 | 
			
		||||
//    }
 | 
			
		||||
//
 | 
			
		||||
//    // Or get Signed cookies for a resource that will expire in 1 hour
 | 
			
		||||
//    // and set path and domain of cookies
 | 
			
		||||
//    cookies, err := s.Sign("*", time.Now().Add(1 * time.Hour), func(o *sign.CookieOptions) {
 | 
			
		||||
//        o.Path = "/"
 | 
			
		||||
//        o.Domain = ".example.com"
 | 
			
		||||
//    })
 | 
			
		||||
//    if err != nil {
 | 
			
		||||
//        fmt.Println("failed to create signed cookies", err)
 | 
			
		||||
//        return
 | 
			
		||||
//    }
 | 
			
		||||
//
 | 
			
		||||
//    // Server Response via http.ResponseWriter
 | 
			
		||||
//    for _, c := range cookies {
 | 
			
		||||
//        http.SetCookie(w, c)
 | 
			
		||||
//    }
 | 
			
		||||
//
 | 
			
		||||
//    // Client request via the cookie jar
 | 
			
		||||
//    if client.CookieJar != nil {
 | 
			
		||||
//        for _, c := range cookies {
 | 
			
		||||
//           client.Cookie(w, c)
 | 
			
		||||
//        }
 | 
			
		||||
//    }
 | 
			
		||||
func (s CookieSigner) Sign(u string, expires time.Time, opts ...func(*CookieOptions)) ([]*http.Cookie, error) {
 | 
			
		||||
	scheme, err := cookieURLScheme(u)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resource, err := CreateResource(scheme, u)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := NewCannedPolicy(resource, expires)
 | 
			
		||||
	return createCookies(p, s.keyID, s.privKey, s.Opts.apply(opts...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns and validates the URL's scheme.
 | 
			
		||||
// http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html#private-content-custom-policy-statement-cookies
 | 
			
		||||
func cookieURLScheme(u string) (string, error) {
 | 
			
		||||
	parts := strings.SplitN(u, "://", 2)
 | 
			
		||||
	if len(parts) != 2 {
 | 
			
		||||
		return "", fmt.Errorf("invalid cookie URL, missing scheme")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scheme := strings.ToLower(parts[0])
 | 
			
		||||
	if scheme != "http" && scheme != "https" && scheme != "http*" {
 | 
			
		||||
		return "", fmt.Errorf("invalid cookie URL scheme. Expect http, https, or http*. Go, %s", scheme)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return scheme, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SignWithPolicy returns the cookies needed to allow user agents to make
 | 
			
		||||
// arbetrairy requets to cloudfront for the resource(s) defined by the policy.
 | 
			
		||||
//
 | 
			
		||||
// The returned slice cookies should all be added to the Client's cookies or
 | 
			
		||||
// server's response.
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//    s := NewCookieSigner(keyID, privKey)
 | 
			
		||||
//
 | 
			
		||||
//    policy := &sign.Policy{
 | 
			
		||||
//        Statements: []sign.Statement{
 | 
			
		||||
//            {
 | 
			
		||||
//                // Read the provided documentation on how to set this
 | 
			
		||||
//                // correctly, you'll probably want to use wildcards.
 | 
			
		||||
//                Resource: RawCloudFrontURL,
 | 
			
		||||
//                Condition: sign.Condition{
 | 
			
		||||
//                    // Optional IP source address range
 | 
			
		||||
//                    IPAddress: &sign.IPAddress{SourceIP: "192.0.2.0/24"},
 | 
			
		||||
//                    // Optional date URL is not valid until
 | 
			
		||||
//                    DateGreaterThan: &sign.AWSEpochTime{time.Now().Add(30 * time.Minute)},
 | 
			
		||||
//                    // Required date the URL will expire after
 | 
			
		||||
//                    DateLessThan: &sign.AWSEpochTime{time.Now().Add(1 * time.Hour)},
 | 
			
		||||
//                },
 | 
			
		||||
//            },
 | 
			
		||||
//        },
 | 
			
		||||
//    }
 | 
			
		||||
//
 | 
			
		||||
//    // Get Signed cookies for a resource that will expire in 1 hour
 | 
			
		||||
//    cookies, err := s.SignWithPolicy(policy)
 | 
			
		||||
//    if err != nil {
 | 
			
		||||
//        fmt.Println("failed to create signed cookies", err)
 | 
			
		||||
//        return
 | 
			
		||||
//    }
 | 
			
		||||
//
 | 
			
		||||
//    // Or get Signed cookies for a resource that will expire in 1 hour
 | 
			
		||||
//    // and set path and domain of cookies
 | 
			
		||||
//    cookies, err := s.Sign(policy, func(o *sign.CookieOptions) {
 | 
			
		||||
//        o.Path = "/"
 | 
			
		||||
//        o.Domain = ".example.com"
 | 
			
		||||
//    })
 | 
			
		||||
//    if err != nil {
 | 
			
		||||
//        fmt.Println("failed to create signed cookies", err)
 | 
			
		||||
//        return
 | 
			
		||||
//    }
 | 
			
		||||
//
 | 
			
		||||
//    // Server Response via http.ResponseWriter
 | 
			
		||||
//    for _, c := range cookies {
 | 
			
		||||
//        http.SetCookie(w, c)
 | 
			
		||||
//    }
 | 
			
		||||
//
 | 
			
		||||
//    // Client request via the cookie jar
 | 
			
		||||
//    if client.CookieJar != nil {
 | 
			
		||||
//        for _, c := range cookies {
 | 
			
		||||
//           client.Cookie(w, c)
 | 
			
		||||
//        }
 | 
			
		||||
//    }
 | 
			
		||||
func (s CookieSigner) SignWithPolicy(p *Policy, opts ...func(*CookieOptions)) ([]*http.Cookie, error) {
 | 
			
		||||
	return createCookies(p, s.keyID, s.privKey, s.Opts.apply(opts...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Prepares the cookies to be attached to the header. An (optional) options
 | 
			
		||||
// struct is provided in case people don't want to manually edit their cookies.
 | 
			
		||||
func createCookies(p *Policy, keyID string, privKey *rsa.PrivateKey, opt CookieOptions) ([]*http.Cookie, error) {
 | 
			
		||||
	b64Sig, b64Policy, err := p.Sign(privKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Creates proper cookies
 | 
			
		||||
	cPolicy := &http.Cookie{
 | 
			
		||||
		Name:     CookiePolicyName,
 | 
			
		||||
		Value:    string(b64Policy),
 | 
			
		||||
		HttpOnly: true,
 | 
			
		||||
	}
 | 
			
		||||
	cSignature := &http.Cookie{
 | 
			
		||||
		Name:     CookieSignatureName,
 | 
			
		||||
		Value:    string(b64Sig),
 | 
			
		||||
		HttpOnly: true,
 | 
			
		||||
	}
 | 
			
		||||
	cKey := &http.Cookie{
 | 
			
		||||
		Name:     CookieKeyIDName,
 | 
			
		||||
		Value:    keyID,
 | 
			
		||||
		HttpOnly: true,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cookies := []*http.Cookie{cPolicy, cSignature, cKey}
 | 
			
		||||
 | 
			
		||||
	// Applie the cookie options
 | 
			
		||||
	for _, c := range cookies {
 | 
			
		||||
		c.Path = opt.Path
 | 
			
		||||
		c.Domain = opt.Domain
 | 
			
		||||
		c.Secure = opt.Secure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cookies, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -90,19 +90,19 @@ func (s URLSigner) Sign(url string, expires time.Time) (string, error) {
 | 
			
		|||
//     // Sign URL to be valid for 30 minutes from now, expires one hour from now, and
 | 
			
		||||
//     // restricted to the 192.0.2.0/24 IP address range.
 | 
			
		||||
//     policy := &sign.Policy{
 | 
			
		||||
//         Statements: []Statement{
 | 
			
		||||
//         Statements: []sign.Statement{
 | 
			
		||||
//             {
 | 
			
		||||
//                 Resource: rawURL,
 | 
			
		||||
//                 Condition: Condition{
 | 
			
		||||
//                 Condition: sign.Condition{
 | 
			
		||||
//                     // Optional IP source address range
 | 
			
		||||
//                     IPAddress: &IPAddress{SourceIP: "192.0.2.0/24"},
 | 
			
		||||
//                     IPAddress: &sign.IPAddress{SourceIP: "192.0.2.0/24"},
 | 
			
		||||
//                     // Optional date URL is not valid until
 | 
			
		||||
//                     DateGreaterThan: &AWSEpochTime{time.Now().Add(30 * time.Minute)},
 | 
			
		||||
//                     DateGreaterThan: &sign.AWSEpochTime{time.Now().Add(30 * time.Minute)},
 | 
			
		||||
//                     // Required date the URL will expire after
 | 
			
		||||
//                     DateLessThan: &AWSEpochTime{time.Now().Add(1 * time.Hour)},
 | 
			
		||||
//                 }
 | 
			
		||||
//             }
 | 
			
		||||
//         }
 | 
			
		||||
//                     DateLessThan: &sign.AWSEpochTime{time.Now().Add(1 * time.Hour)},
 | 
			
		||||
//                 },
 | 
			
		||||
//             },
 | 
			
		||||
//         },
 | 
			
		||||
//     }
 | 
			
		||||
//
 | 
			
		||||
//     signer := sign.NewURLSigner(keyID, privKey)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -6,32 +6,41 @@ import (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	initClient = func(c *client.Client) {
 | 
			
		||||
		// Support building custom host-style bucket endpoints
 | 
			
		||||
		c.Handlers.Build.PushFront(updateHostWithBucket)
 | 
			
		||||
	initClient = defaultInitClientFn
 | 
			
		||||
	initRequest = defaultInitRequestFn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		// Require SSL when using SSE keys
 | 
			
		||||
		c.Handlers.Validate.PushBack(validateSSERequiresSSL)
 | 
			
		||||
		c.Handlers.Build.PushBack(computeSSEKeys)
 | 
			
		||||
func defaultInitClientFn(c *client.Client) {
 | 
			
		||||
	// Support building custom endpoints based on config
 | 
			
		||||
	c.Handlers.Build.PushFront(updateEndpointForS3Config)
 | 
			
		||||
 | 
			
		||||
		// S3 uses custom error unmarshaling logic
 | 
			
		||||
		c.Handlers.UnmarshalError.Clear()
 | 
			
		||||
		c.Handlers.UnmarshalError.PushBack(unmarshalError)
 | 
			
		||||
	}
 | 
			
		||||
	// Require SSL when using SSE keys
 | 
			
		||||
	c.Handlers.Validate.PushBack(validateSSERequiresSSL)
 | 
			
		||||
	c.Handlers.Build.PushBack(computeSSEKeys)
 | 
			
		||||
 | 
			
		||||
	initRequest = func(r *request.Request) {
 | 
			
		||||
		switch r.Operation.Name {
 | 
			
		||||
		case opPutBucketCors, opPutBucketLifecycle, opPutBucketPolicy, opPutBucketTagging, opDeleteObjects, opPutBucketLifecycleConfiguration:
 | 
			
		||||
			// These S3 operations require Content-MD5 to be set
 | 
			
		||||
			r.Handlers.Build.PushBack(contentMD5)
 | 
			
		||||
		case opGetBucketLocation:
 | 
			
		||||
			// GetBucketLocation has custom parsing logic
 | 
			
		||||
			r.Handlers.Unmarshal.PushFront(buildGetBucketLocation)
 | 
			
		||||
		case opCreateBucket:
 | 
			
		||||
			// Auto-populate LocationConstraint with current region
 | 
			
		||||
			r.Handlers.Validate.PushFront(populateLocationConstraint)
 | 
			
		||||
		case opCopyObject, opUploadPartCopy, opCompleteMultipartUpload:
 | 
			
		||||
			r.Handlers.Unmarshal.PushFront(copyMultipartStatusOKUnmarhsalError)
 | 
			
		||||
		}
 | 
			
		||||
	// S3 uses custom error unmarshaling logic
 | 
			
		||||
	c.Handlers.UnmarshalError.Clear()
 | 
			
		||||
	c.Handlers.UnmarshalError.PushBack(unmarshalError)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defaultInitRequestFn(r *request.Request) {
 | 
			
		||||
	// Add reuest handlers for specific platforms.
 | 
			
		||||
	// e.g. 100-continue support for PUT requests using Go 1.6
 | 
			
		||||
	platformRequestHandlers(r)
 | 
			
		||||
 | 
			
		||||
	switch r.Operation.Name {
 | 
			
		||||
	case opPutBucketCors, opPutBucketLifecycle, opPutBucketPolicy,
 | 
			
		||||
		opPutBucketTagging, opDeleteObjects, opPutBucketLifecycleConfiguration,
 | 
			
		||||
		opPutBucketReplication:
 | 
			
		||||
		// These S3 operations require Content-MD5 to be set
 | 
			
		||||
		r.Handlers.Build.PushBack(contentMD5)
 | 
			
		||||
	case opGetBucketLocation:
 | 
			
		||||
		// GetBucketLocation has custom parsing logic
 | 
			
		||||
		r.Handlers.Unmarshal.PushFront(buildGetBucketLocation)
 | 
			
		||||
	case opCreateBucket:
 | 
			
		||||
		// Auto-populate LocationConstraint with current region
 | 
			
		||||
		r.Handlers.Validate.PushFront(populateLocationConstraint)
 | 
			
		||||
	case opCopyObject, opUploadPartCopy, opCompleteMultipartUpload:
 | 
			
		||||
		r.Handlers.Unmarshal.PushFront(copyMultipartStatusOKUnmarhsalError)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +1,124 @@
 | 
			
		|||
package s3
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/awserr"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/awsutil"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/request"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// an operationBlacklist is a list of operation names that should a
 | 
			
		||||
// request handler should not be executed with.
 | 
			
		||||
type operationBlacklist []string
 | 
			
		||||
 | 
			
		||||
// Continue will return true of the Request's operation name is not
 | 
			
		||||
// in the blacklist. False otherwise.
 | 
			
		||||
func (b operationBlacklist) Continue(r *request.Request) bool {
 | 
			
		||||
	for i := 0; i < len(b); i++ {
 | 
			
		||||
		if b[i] == r.Operation.Name {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var accelerateOpBlacklist = operationBlacklist{
 | 
			
		||||
	opListBuckets, opCreateBucket, opDeleteBucket,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Request handler to automatically add the bucket name to the endpoint domain
 | 
			
		||||
// if possible. This style of bucket is valid for all bucket names which are
 | 
			
		||||
// DNS compatible and do not contain "."
 | 
			
		||||
func updateEndpointForS3Config(r *request.Request) {
 | 
			
		||||
	forceHostStyle := aws.BoolValue(r.Config.S3ForcePathStyle)
 | 
			
		||||
	accelerate := aws.BoolValue(r.Config.S3UseAccelerate)
 | 
			
		||||
 | 
			
		||||
	if accelerate && accelerateOpBlacklist.Continue(r) {
 | 
			
		||||
		if forceHostStyle {
 | 
			
		||||
			if r.Config.Logger != nil {
 | 
			
		||||
				r.Config.Logger.Log("ERROR: aws.Config.S3UseAccelerate is not compatible with aws.Config.S3ForcePathStyle, ignoring S3ForcePathStyle.")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		updateEndpointForAccelerate(r)
 | 
			
		||||
	} else if !forceHostStyle && r.Operation.Name != opGetBucketLocation {
 | 
			
		||||
		updateEndpointForHostStyle(r)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateEndpointForHostStyle(r *request.Request) {
 | 
			
		||||
	bucket, ok := bucketNameFromReqParams(r.Params)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		// Ignore operation requests if the bucketname was not provided
 | 
			
		||||
		// if this is an input validation error the validation handler
 | 
			
		||||
		// will report it.
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !hostCompatibleBucketName(r.HTTPRequest.URL, bucket) {
 | 
			
		||||
		// bucket name must be valid to put into the host
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	moveBucketToHost(r.HTTPRequest.URL, bucket)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateEndpointForAccelerate(r *request.Request) {
 | 
			
		||||
	bucket, ok := bucketNameFromReqParams(r.Params)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		// Ignore operation requests if the bucketname was not provided
 | 
			
		||||
		// if this is an input validation error the validation handler
 | 
			
		||||
		// will report it.
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !hostCompatibleBucketName(r.HTTPRequest.URL, bucket) {
 | 
			
		||||
		r.Error = awserr.New("InvalidParameterException",
 | 
			
		||||
			fmt.Sprintf("bucket name %s is not compatibile with S3 Accelerate", bucket),
 | 
			
		||||
			nil)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Change endpoint from s3(-[a-z0-1-])?.amazonaws.com to s3-accelerate.amazonaws.com
 | 
			
		||||
	r.HTTPRequest.URL.Host = replaceHostRegion(r.HTTPRequest.URL.Host, "accelerate")
 | 
			
		||||
	moveBucketToHost(r.HTTPRequest.URL, bucket)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attempts to retrieve the bucket name from the request input parameters.
 | 
			
		||||
// If no bucket is found, or the field is empty "", false will be returned.
 | 
			
		||||
func bucketNameFromReqParams(params interface{}) (string, bool) {
 | 
			
		||||
	b, _ := awsutil.ValuesAtPath(params, "Bucket")
 | 
			
		||||
	if len(b) == 0 {
 | 
			
		||||
		return "", false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if bucket, ok := b[0].(*string); ok {
 | 
			
		||||
		if bucketStr := aws.StringValue(bucket); bucketStr != "" {
 | 
			
		||||
			return bucketStr, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hostCompatibleBucketName returns true if the request should
 | 
			
		||||
// put the bucket in the host. This is false if S3ForcePathStyle is
 | 
			
		||||
// explicitly set or if the bucket is not DNS compatible.
 | 
			
		||||
func hostCompatibleBucketName(u *url.URL, bucket string) bool {
 | 
			
		||||
	// Bucket might be DNS compatible but dots in the hostname will fail
 | 
			
		||||
	// certificate validation, so do not use host-style.
 | 
			
		||||
	if u.Scheme == "https" && strings.Contains(bucket, ".") {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if the bucket is DNS compatible
 | 
			
		||||
	return dnsCompatibleBucketName(bucket)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var reDomain = regexp.MustCompile(`^[a-z0-9][a-z0-9\.\-]{1,61}[a-z0-9]$`)
 | 
			
		||||
var reIPAddress = regexp.MustCompile(`^(\d+\.){3}\d+$`)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -20,41 +130,36 @@ func dnsCompatibleBucketName(bucket string) bool {
 | 
			
		|||
		!strings.Contains(bucket, "..")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hostStyleBucketName returns true if the request should put the bucket in
 | 
			
		||||
// the host. This is false if S3ForcePathStyle is explicitly set or if the
 | 
			
		||||
// bucket is not DNS compatible.
 | 
			
		||||
func hostStyleBucketName(r *request.Request, bucket string) bool {
 | 
			
		||||
	if aws.BoolValue(r.Config.S3ForcePathStyle) {
 | 
			
		||||
		return false
 | 
			
		||||
// moveBucketToHost moves the bucket name from the URI path to URL host.
 | 
			
		||||
func moveBucketToHost(u *url.URL, bucket string) {
 | 
			
		||||
	u.Host = bucket + "." + u.Host
 | 
			
		||||
	u.Path = strings.Replace(u.Path, "/{Bucket}", "", -1)
 | 
			
		||||
	if u.Path == "" {
 | 
			
		||||
		u.Path = "/"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Bucket might be DNS compatible but dots in the hostname will fail
 | 
			
		||||
	// certificate validation, so do not use host-style.
 | 
			
		||||
	if r.HTTPRequest.URL.Scheme == "https" && strings.Contains(bucket, ".") {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// GetBucketLocation should be able to be called from any region within
 | 
			
		||||
	// a partition, and return the associated region of the bucket.
 | 
			
		||||
	if r.Operation.Name == opGetBucketLocation {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Use host-style if the bucket is DNS compatible
 | 
			
		||||
	return dnsCompatibleBucketName(bucket)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateHostWithBucket(r *request.Request) {
 | 
			
		||||
	b, _ := awsutil.ValuesAtPath(r.Params, "Bucket")
 | 
			
		||||
	if len(b) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
const s3HostPrefix = "s3"
 | 
			
		||||
 | 
			
		||||
// replaceHostRegion replaces the S3 region string in the host with the
 | 
			
		||||
// value provided. If v is empty the host prefix returned will be s3.
 | 
			
		||||
func replaceHostRegion(host, v string) string {
 | 
			
		||||
	if !strings.HasPrefix(host, s3HostPrefix) {
 | 
			
		||||
		return host
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if bucket := b[0].(*string); aws.StringValue(bucket) != "" && hostStyleBucketName(r, *bucket) {
 | 
			
		||||
		r.HTTPRequest.URL.Host = *bucket + "." + r.HTTPRequest.URL.Host
 | 
			
		||||
		r.HTTPRequest.URL.Path = strings.Replace(r.HTTPRequest.URL.Path, "/{Bucket}", "", -1)
 | 
			
		||||
		if r.HTTPRequest.URL.Path == "" {
 | 
			
		||||
			r.HTTPRequest.URL.Path = "/"
 | 
			
		||||
	suffix := host[len(s3HostPrefix):]
 | 
			
		||||
	for i := len(s3HostPrefix); i < len(host); i++ {
 | 
			
		||||
		if host[i] == '.' {
 | 
			
		||||
			// Trim until '.' leave the it in place.
 | 
			
		||||
			suffix = host[i:]
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(v) == 0 {
 | 
			
		||||
		return fmt.Sprintf("s3%s", suffix)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("s3-%s%s", v, suffix)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
// +build !go1.6
 | 
			
		||||
 | 
			
		||||
package s3
 | 
			
		||||
 | 
			
		||||
import "github.com/aws/aws-sdk-go/aws/request"
 | 
			
		||||
 | 
			
		||||
func platformRequestHandlers(r *request.Request) {
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								vendor/github.com/aws/aws-sdk-go/service/s3/platform_handlers_go1.6.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										28
									
								
								vendor/github.com/aws/aws-sdk-go/service/s3/platform_handlers_go1.6.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
// +build go1.6
 | 
			
		||||
 | 
			
		||||
package s3
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/request"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func platformRequestHandlers(r *request.Request) {
 | 
			
		||||
	if r.Operation.HTTPMethod == "PUT" {
 | 
			
		||||
		// 100-Continue should only be used on put requests.
 | 
			
		||||
		r.Handlers.Sign.PushBack(add100Continue)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func add100Continue(r *request.Request) {
 | 
			
		||||
	if aws.BoolValue(r.Config.S3Disable100Continue) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if r.HTTPRequest.ContentLength < 1024*1024*2 {
 | 
			
		||||
		// Ignore requests smaller than 2MB. This helps prevent delaying
 | 
			
		||||
		// requests unnecessarily.
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.HTTPRequest.Header.Set("Expect", "100-Continue")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -7,8 +7,8 @@ import (
 | 
			
		|||
	"github.com/aws/aws-sdk-go/aws/client"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/client/metadata"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/request"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/aws/signer/v4"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/private/protocol/restxml"
 | 
			
		||||
	"github.com/aws/aws-sdk-go/private/signer/v4"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// S3 is a client for Amazon S3.
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +58,7 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// Handlers
 | 
			
		||||
	svc.Handlers.Sign.PushBack(v4.Sign)
 | 
			
		||||
	svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler)
 | 
			
		||||
	svc.Handlers.Build.PushBackNamed(restxml.BuildHandler)
 | 
			
		||||
	svc.Handlers.Unmarshal.PushBackNamed(restxml.UnmarshalHandler)
 | 
			
		||||
	svc.Handlers.UnmarshalMeta.PushBackNamed(restxml.UnmarshalMetaHandler)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ import (
 | 
			
		|||
	"encoding/xml"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +21,7 @@ type xmlErrorResponse struct {
 | 
			
		|||
 | 
			
		||||
func unmarshalError(r *request.Request) {
 | 
			
		||||
	defer r.HTTPResponse.Body.Close()
 | 
			
		||||
	defer io.Copy(ioutil.Discard, r.HTTPResponse.Body)
 | 
			
		||||
 | 
			
		||||
	if r.HTTPResponse.StatusCode == http.StatusMovedPermanently {
 | 
			
		||||
		r.Error = awserr.NewRequestFailure(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,12 @@ func (c *S3) WaitUntilBucketExists(input *HeadBucketInput) error {
 | 
			
		|||
				Argument: "",
 | 
			
		||||
				Expected: 200,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				State:    "success",
 | 
			
		||||
				Matcher:  "status",
 | 
			
		||||
				Argument: "",
 | 
			
		||||
				Expected: 301,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				State:    "success",
 | 
			
		||||
				Matcher:  "status",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,20 +22,8 @@ Package ini provides INI file read and write functionality in Go.
 | 
			
		|||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
To use a tagged revision:
 | 
			
		||||
 | 
			
		||||
	go get gopkg.in/ini.v1
 | 
			
		||||
 | 
			
		||||
To use with latest changes:
 | 
			
		||||
 | 
			
		||||
	go get github.com/go-ini/ini
 | 
			
		||||
 | 
			
		||||
### Testing
 | 
			
		||||
 | 
			
		||||
If you want to test on your machine, please apply `-t` flag:
 | 
			
		||||
 | 
			
		||||
	go get -t gopkg.in/ini.v1
 | 
			
		||||
 | 
			
		||||
## Getting Started
 | 
			
		||||
 | 
			
		||||
### Loading from data sources
 | 
			
		||||
| 
						 | 
				
			
			@ -107,12 +95,6 @@ Same rule applies to key operations:
 | 
			
		|||
key := cfg.Section("").Key("key name")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To check if a key exists:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
yes := cfg.Section("").HasKey("key name")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To create a new key:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
| 
						 | 
				
			
			@ -151,24 +133,12 @@ val := cfg.Section("").Key("key name").Validate(func(in string) string {
 | 
			
		|||
})
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If you do not want any auto-transformation (such as recursive read) for the values, you can get raw value directly (this way you get much better performance):
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
val := cfg.Section("").Key("key name").Value()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To check if raw value exists:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
yes := cfg.Section("").HasValue("test value")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To get value with types:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
// For boolean values:
 | 
			
		||||
// true when value is: 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On
 | 
			
		||||
// false when value is: 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off
 | 
			
		||||
// true when value is: 1, t, T, TRUE, true, True, YES, yes, Yes, ON, on, On
 | 
			
		||||
// false when value is: 0, f, F, FALSE, false, False, NO, no, No, OFF, off, Off
 | 
			
		||||
v, err = cfg.Section("").Key("BOOL").Bool()
 | 
			
		||||
v, err = cfg.Section("").Key("FLOAT64").Float64()
 | 
			
		||||
v, err = cfg.Section("").Key("INT").Int()
 | 
			
		||||
| 
						 | 
				
			
			@ -494,7 +464,7 @@ type Info struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("package_name=ini"))
 | 
			
		||||
	err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("packag_name=ini"))
 | 
			
		||||
	// ...
 | 
			
		||||
 | 
			
		||||
	cfg, err := ini.Load([]byte("PACKAGE_NAME=ini"))
 | 
			
		||||
| 
						 | 
				
			
			@ -15,20 +15,8 @@
 | 
			
		|||
 | 
			
		||||
## 下载安装
 | 
			
		||||
 | 
			
		||||
使用一个特定版本:
 | 
			
		||||
 | 
			
		||||
    go get gopkg.in/ini.v1
 | 
			
		||||
 | 
			
		||||
使用最新版:
 | 
			
		||||
 | 
			
		||||
	go get github.com/go-ini/ini
 | 
			
		||||
 | 
			
		||||
### 测试安装
 | 
			
		||||
 | 
			
		||||
如果您想要在自己的机器上运行测试,请使用 `-t` 标记:
 | 
			
		||||
 | 
			
		||||
	go get -t gopkg.in/ini.v1
 | 
			
		||||
 | 
			
		||||
## 开始使用
 | 
			
		||||
 | 
			
		||||
### 从数据源加载
 | 
			
		||||
| 
						 | 
				
			
			@ -100,12 +88,6 @@ key, err := cfg.Section("").GetKey("key name")
 | 
			
		|||
key := cfg.Section("").Key("key name")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
判断某个键是否存在:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
yes := cfg.Section("").HasKey("key name")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
创建一个新的键:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
| 
						 | 
				
			
			@ -144,24 +126,12 @@ val := cfg.Section("").Key("key name").Validate(func(in string) string {
 | 
			
		|||
})
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
如果您不需要任何对值的自动转变功能(例如递归读取),可以直接获取原值(这种方式性能最佳):
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
val := cfg.Section("").Key("key name").Value()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
判断某个原值是否存在:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
yes := cfg.Section("").HasValue("test value")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
获取其它类型的值:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
// 布尔值的规则:
 | 
			
		||||
// true 当值为:1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On
 | 
			
		||||
// false 当值为:0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off
 | 
			
		||||
// true 当值为:1, t, T, TRUE, true, True, YES, yes, Yes, ON, on, On
 | 
			
		||||
// false 当值为:0, f, F, FALSE, false, False, NO, no, No, OFF, off, Off
 | 
			
		||||
v, err = cfg.Section("").Key("BOOL").Bool()
 | 
			
		||||
v, err = cfg.Section("").Key("FLOAT64").Float64()
 | 
			
		||||
v, err = cfg.Section("").Key("INT").Int()
 | 
			
		||||
| 
						 | 
				
			
			@ -485,7 +455,7 @@ type Info struct{
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("package_name=ini"))
 | 
			
		||||
	err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("packag_name=ini"))
 | 
			
		||||
	// ...
 | 
			
		||||
 | 
			
		||||
	cfg, err := ini.Load([]byte("PACKAGE_NAME=ini"))
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +16,7 @@
 | 
			
		|||
package ini
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +35,7 @@ const (
 | 
			
		|||
	// Maximum allowed depth when recursively substituing variable names.
 | 
			
		||||
	_DEPTH_VALUES = 99
 | 
			
		||||
 | 
			
		||||
	_VERSION = "1.8.6"
 | 
			
		||||
	_VERSION = "1.6.0"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Version() string {
 | 
			
		||||
| 
						 | 
				
			
			@ -163,14 +164,14 @@ func (k *Key) Validate(fn func(string) string) string {
 | 
			
		|||
 | 
			
		||||
// parseBool returns the boolean value represented by the string.
 | 
			
		||||
//
 | 
			
		||||
// It accepts 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On,
 | 
			
		||||
// 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off.
 | 
			
		||||
// It accepts 1, t, T, TRUE, true, True, YES, yes, Yes, ON, on, On,
 | 
			
		||||
// 0, f, F, FALSE, false, False, NO, no, No, OFF, off, Off.
 | 
			
		||||
// Any other value returns an error.
 | 
			
		||||
func parseBool(str string) (value bool, err error) {
 | 
			
		||||
	switch str {
 | 
			
		||||
	case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "y", "ON", "on", "On":
 | 
			
		||||
	case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "ON", "on", "On":
 | 
			
		||||
		return true, nil
 | 
			
		||||
	case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "n", "OFF", "off", "Off":
 | 
			
		||||
	case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "OFF", "off", "Off":
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	return false, fmt.Errorf("parsing \"%s\": invalid syntax", str)
 | 
			
		||||
| 
						 | 
				
			
			@ -453,7 +454,7 @@ func (k *Key) RangeTime(defaultVal, min, max time.Time) time.Time {
 | 
			
		|||
	return k.RangeTimeFormat(time.RFC3339, defaultVal, min, max)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Strings returns list of string divided by given delimiter.
 | 
			
		||||
// Strings returns list of string devide by given delimiter.
 | 
			
		||||
func (k *Key) Strings(delim string) []string {
 | 
			
		||||
	str := k.String()
 | 
			
		||||
	if len(str) == 0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -467,7 +468,7 @@ func (k *Key) Strings(delim string) []string {
 | 
			
		|||
	return vals
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Float64s returns list of float64 divided by given delimiter.
 | 
			
		||||
// Float64s returns list of float64 devide by given delimiter.
 | 
			
		||||
func (k *Key) Float64s(delim string) []float64 {
 | 
			
		||||
	strs := k.Strings(delim)
 | 
			
		||||
	vals := make([]float64, len(strs))
 | 
			
		||||
| 
						 | 
				
			
			@ -477,7 +478,7 @@ func (k *Key) Float64s(delim string) []float64 {
 | 
			
		|||
	return vals
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ints returns list of int divided by given delimiter.
 | 
			
		||||
// Ints returns list of int devide by given delimiter.
 | 
			
		||||
func (k *Key) Ints(delim string) []int {
 | 
			
		||||
	strs := k.Strings(delim)
 | 
			
		||||
	vals := make([]int, len(strs))
 | 
			
		||||
| 
						 | 
				
			
			@ -487,7 +488,7 @@ func (k *Key) Ints(delim string) []int {
 | 
			
		|||
	return vals
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Int64s returns list of int64 divided by given delimiter.
 | 
			
		||||
// Int64s returns list of int64 devide by given delimiter.
 | 
			
		||||
func (k *Key) Int64s(delim string) []int64 {
 | 
			
		||||
	strs := k.Strings(delim)
 | 
			
		||||
	vals := make([]int64, len(strs))
 | 
			
		||||
| 
						 | 
				
			
			@ -497,18 +498,18 @@ func (k *Key) Int64s(delim string) []int64 {
 | 
			
		|||
	return vals
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Uints returns list of uint divided by given delimiter.
 | 
			
		||||
// Uints returns list of uint devide by given delimiter.
 | 
			
		||||
func (k *Key) Uints(delim string) []uint {
 | 
			
		||||
	strs := k.Strings(delim)
 | 
			
		||||
	vals := make([]uint, len(strs))
 | 
			
		||||
	for i := range strs {
 | 
			
		||||
		u, _ := strconv.ParseUint(strs[i], 10, 0)
 | 
			
		||||
		u, _ := strconv.ParseUint(strs[i], 10, 64)
 | 
			
		||||
		vals[i] = uint(u)
 | 
			
		||||
	}
 | 
			
		||||
	return vals
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Uint64s returns list of uint64 divided by given delimiter.
 | 
			
		||||
// Uint64s returns list of uint64 devide by given delimiter.
 | 
			
		||||
func (k *Key) Uint64s(delim string) []uint64 {
 | 
			
		||||
	strs := k.Strings(delim)
 | 
			
		||||
	vals := make([]uint64, len(strs))
 | 
			
		||||
| 
						 | 
				
			
			@ -518,7 +519,7 @@ func (k *Key) Uint64s(delim string) []uint64 {
 | 
			
		|||
	return vals
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TimesFormat parses with given format and returns list of time.Time divided by given delimiter.
 | 
			
		||||
// TimesFormat parses with given format and returns list of time.Time devide by given delimiter.
 | 
			
		||||
func (k *Key) TimesFormat(format, delim string) []time.Time {
 | 
			
		||||
	strs := k.Strings(delim)
 | 
			
		||||
	vals := make([]time.Time, len(strs))
 | 
			
		||||
| 
						 | 
				
			
			@ -528,20 +529,14 @@ func (k *Key) TimesFormat(format, delim string) []time.Time {
 | 
			
		|||
	return vals
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Times parses with RFC3339 format and returns list of time.Time divided by given delimiter.
 | 
			
		||||
// Times parses with RFC3339 format and returns list of time.Time devide by given delimiter.
 | 
			
		||||
func (k *Key) Times(delim string) []time.Time {
 | 
			
		||||
	return k.TimesFormat(time.RFC3339, delim)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetValue changes key value.
 | 
			
		||||
func (k *Key) SetValue(v string) {
 | 
			
		||||
	if k.s.f.BlockMode {
 | 
			
		||||
		k.s.f.lock.Lock()
 | 
			
		||||
		defer k.s.f.lock.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.value = v
 | 
			
		||||
	k.s.keysHash[k.name] = v
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//   _________              __  .__
 | 
			
		||||
| 
						 | 
				
			
			@ -623,32 +618,6 @@ func (s *Section) GetKey(name string) (*Key, error) {
 | 
			
		|||
	return key, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasKey returns true if section contains a key with given name.
 | 
			
		||||
func (s *Section) HasKey(name string) bool {
 | 
			
		||||
	key, _ := s.GetKey(name)
 | 
			
		||||
	return key != nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Haskey is a backwards-compatible name for HasKey.
 | 
			
		||||
func (s *Section) Haskey(name string) bool {
 | 
			
		||||
	return s.HasKey(name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasValue returns true if section contains given raw value.
 | 
			
		||||
func (s *Section) HasValue(value string) bool {
 | 
			
		||||
	if s.f.BlockMode {
 | 
			
		||||
		s.f.lock.RLock()
 | 
			
		||||
		defer s.f.lock.RUnlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, k := range s.keys {
 | 
			
		||||
		if value == k.value {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Key assumes named Key exists in section and returns a zero-value when not.
 | 
			
		||||
func (s *Section) Key(name string) *Key {
 | 
			
		||||
	key, err := s.GetKey(name)
 | 
			
		||||
| 
						 | 
				
			
			@ -768,10 +737,7 @@ func Load(source interface{}, others ...interface{}) (_ *File, err error) {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
	f := newFile(sources)
 | 
			
		||||
	if err = f.Reload(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return f, nil
 | 
			
		||||
	return f, f.Reload()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Empty returns an empty file object.
 | 
			
		||||
| 
						 | 
				
			
			@ -877,6 +843,240 @@ func (f *File) DeleteSection(name string) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cutComment(str string) string {
 | 
			
		||||
	i := strings.Index(str, "#")
 | 
			
		||||
	if i == -1 {
 | 
			
		||||
		return str
 | 
			
		||||
	}
 | 
			
		||||
	return str[:i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkMultipleLines(buf *bufio.Reader, line, val, valQuote string) (string, error) {
 | 
			
		||||
	isEnd := false
 | 
			
		||||
	for {
 | 
			
		||||
		next, err := buf.ReadString('\n')
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err != io.EOF {
 | 
			
		||||
				return "", err
 | 
			
		||||
			}
 | 
			
		||||
			isEnd = true
 | 
			
		||||
		}
 | 
			
		||||
		pos := strings.LastIndex(next, valQuote)
 | 
			
		||||
		if pos > -1 {
 | 
			
		||||
			val += next[:pos]
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		val += next
 | 
			
		||||
		if isEnd {
 | 
			
		||||
			return "", fmt.Errorf("error parsing line: missing closing key quote from '%s' to '%s'", line, next)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return val, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkContinuationLines(buf *bufio.Reader, val string) (string, bool, error) {
 | 
			
		||||
	isEnd := false
 | 
			
		||||
	for {
 | 
			
		||||
		valLen := len(val)
 | 
			
		||||
		if valLen == 0 || val[valLen-1] != '\\' {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		val = val[:valLen-1]
 | 
			
		||||
 | 
			
		||||
		next, err := buf.ReadString('\n')
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err != io.EOF {
 | 
			
		||||
				return "", isEnd, err
 | 
			
		||||
			}
 | 
			
		||||
			isEnd = true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		next = strings.TrimSpace(next)
 | 
			
		||||
		if len(next) == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		val += next
 | 
			
		||||
	}
 | 
			
		||||
	return val, isEnd, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parse parses data through an io.Reader.
 | 
			
		||||
func (f *File) parse(reader io.Reader) error {
 | 
			
		||||
	buf := bufio.NewReader(reader)
 | 
			
		||||
 | 
			
		||||
	// Handle BOM-UTF8.
 | 
			
		||||
	// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding
 | 
			
		||||
	mask, err := buf.Peek(3)
 | 
			
		||||
	if err == nil && len(mask) >= 3 && mask[0] == 239 && mask[1] == 187 && mask[2] == 191 {
 | 
			
		||||
		buf.Read(mask)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	count := 1
 | 
			
		||||
	comments := ""
 | 
			
		||||
	isEnd := false
 | 
			
		||||
 | 
			
		||||
	section, err := f.NewSection(DEFAULT_SECTION)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		line, err := buf.ReadString('\n')
 | 
			
		||||
		line = strings.TrimSpace(line)
 | 
			
		||||
		length := len(line)
 | 
			
		||||
 | 
			
		||||
		// Check error and ignore io.EOF just for a moment.
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err != io.EOF {
 | 
			
		||||
				return fmt.Errorf("error reading next line: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			// The last line of file could be an empty line.
 | 
			
		||||
			if length == 0 {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			isEnd = true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Skip empty lines.
 | 
			
		||||
		if length == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch {
 | 
			
		||||
		case line[0] == '#' || line[0] == ';': // Comments.
 | 
			
		||||
			if len(comments) == 0 {
 | 
			
		||||
				comments = line
 | 
			
		||||
			} else {
 | 
			
		||||
				comments += LineBreak + line
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		case line[0] == '[' && line[length-1] == ']': // New sction.
 | 
			
		||||
			section, err = f.NewSection(strings.TrimSpace(line[1 : length-1]))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if len(comments) > 0 {
 | 
			
		||||
				section.Comment = comments
 | 
			
		||||
				comments = ""
 | 
			
		||||
			}
 | 
			
		||||
			// Reset counter.
 | 
			
		||||
			count = 1
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Other possibilities.
 | 
			
		||||
		var (
 | 
			
		||||
			i        int
 | 
			
		||||
			keyQuote string
 | 
			
		||||
			kname    string
 | 
			
		||||
			valQuote string
 | 
			
		||||
			val      string
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		// Key name surrounded by quotes.
 | 
			
		||||
		if line[0] == '"' {
 | 
			
		||||
			if length > 6 && line[0:3] == `"""` {
 | 
			
		||||
				keyQuote = `"""`
 | 
			
		||||
			} else {
 | 
			
		||||
				keyQuote = `"`
 | 
			
		||||
			}
 | 
			
		||||
		} else if line[0] == '`' {
 | 
			
		||||
			keyQuote = "`"
 | 
			
		||||
		}
 | 
			
		||||
		if len(keyQuote) > 0 {
 | 
			
		||||
			qLen := len(keyQuote)
 | 
			
		||||
			pos := strings.Index(line[qLen:], keyQuote)
 | 
			
		||||
			if pos == -1 {
 | 
			
		||||
				return fmt.Errorf("error parsing line: missing closing key quote: %s", line)
 | 
			
		||||
			}
 | 
			
		||||
			pos = pos + qLen
 | 
			
		||||
			i = strings.IndexAny(line[pos:], "=:")
 | 
			
		||||
			if i < 0 {
 | 
			
		||||
				return fmt.Errorf("error parsing line: key-value delimiter not found: %s", line)
 | 
			
		||||
			} else if i == pos {
 | 
			
		||||
				return fmt.Errorf("error parsing line: key is empty: %s", line)
 | 
			
		||||
			}
 | 
			
		||||
			i = i + pos
 | 
			
		||||
			kname = line[qLen:pos] // Just keep spaces inside quotes.
 | 
			
		||||
		} else {
 | 
			
		||||
			i = strings.IndexAny(line, "=:")
 | 
			
		||||
			if i < 0 {
 | 
			
		||||
				return fmt.Errorf("error parsing line: key-value delimiter not found: %s", line)
 | 
			
		||||
			} else if i == 0 {
 | 
			
		||||
				return fmt.Errorf("error parsing line: key is empty: %s", line)
 | 
			
		||||
			}
 | 
			
		||||
			kname = strings.TrimSpace(line[0:i])
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		isAutoIncr := false
 | 
			
		||||
		// Auto increment.
 | 
			
		||||
		if kname == "-" {
 | 
			
		||||
			isAutoIncr = true
 | 
			
		||||
			kname = "#" + fmt.Sprint(count)
 | 
			
		||||
			count++
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lineRight := strings.TrimSpace(line[i+1:])
 | 
			
		||||
		lineRightLength := len(lineRight)
 | 
			
		||||
		firstChar := ""
 | 
			
		||||
		if lineRightLength >= 2 {
 | 
			
		||||
			firstChar = lineRight[0:1]
 | 
			
		||||
		}
 | 
			
		||||
		if firstChar == "`" {
 | 
			
		||||
			valQuote = "`"
 | 
			
		||||
		} else if firstChar == `"` {
 | 
			
		||||
			if lineRightLength >= 3 && lineRight[0:3] == `"""` {
 | 
			
		||||
				valQuote = `"""`
 | 
			
		||||
			} else {
 | 
			
		||||
				valQuote = `"`
 | 
			
		||||
			}
 | 
			
		||||
		} else if firstChar == `'` {
 | 
			
		||||
			valQuote = `'`
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(valQuote) > 0 {
 | 
			
		||||
			qLen := len(valQuote)
 | 
			
		||||
			pos := strings.LastIndex(lineRight[qLen:], valQuote)
 | 
			
		||||
			// For multiple-line value check.
 | 
			
		||||
			if pos == -1 {
 | 
			
		||||
				if valQuote == `"` || valQuote == `'` {
 | 
			
		||||
					return fmt.Errorf("error parsing line: single quote does not allow multiple-line value: %s", line)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				val = lineRight[qLen:] + "\n"
 | 
			
		||||
				val, err = checkMultipleLines(buf, line, val, valQuote)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				val = lineRight[qLen : pos+qLen]
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			val = strings.TrimSpace(cutComment(lineRight))
 | 
			
		||||
			val, isEnd, err = checkContinuationLines(buf, val)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		k, err := section.NewKey(kname, val)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		k.isAutoIncr = isAutoIncr
 | 
			
		||||
		if len(comments) > 0 {
 | 
			
		||||
			k.Comment = comments
 | 
			
		||||
			comments = ""
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if isEnd {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *File) reload(s dataSource) error {
 | 
			
		||||
	r, err := s.ReadCloser()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -966,18 +1166,17 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
 | 
			
		|||
			switch {
 | 
			
		||||
			case key.isAutoIncr:
 | 
			
		||||
				kname = "-"
 | 
			
		||||
			case strings.ContainsAny(kname, "\"=:"):
 | 
			
		||||
				kname = "`" + kname + "`"
 | 
			
		||||
			case strings.Contains(kname, "`"):
 | 
			
		||||
			case strings.Contains(kname, "`") || strings.Contains(kname, `"`):
 | 
			
		||||
				kname = `"""` + kname + `"""`
 | 
			
		||||
			case strings.Contains(kname, `=`) || strings.Contains(kname, `:`):
 | 
			
		||||
				kname = "`" + kname + "`"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			val := key.value
 | 
			
		||||
			// In case key value contains "\n", "`", "\"", "#" or ";".
 | 
			
		||||
			if strings.ContainsAny(val, "\n`") {
 | 
			
		||||
			// In case key value contains "\n", "`" or "\"".
 | 
			
		||||
			if strings.Contains(val, "\n") || strings.Contains(val, "`") || strings.Contains(val, `"`) ||
 | 
			
		||||
				strings.Contains(val, "#") {
 | 
			
		||||
				val = `"""` + val + `"""`
 | 
			
		||||
			} else if strings.ContainsAny(val, "#;") {
 | 
			
		||||
				val = "`" + val + "`"
 | 
			
		||||
			}
 | 
			
		||||
			if _, err = buf.WriteString(kname + equalSign + val + LineBreak); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
| 
						 | 
				
			
			@ -94,14 +94,13 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri
 | 
			
		|||
		field.SetBool(boolVal)
 | 
			
		||||
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
			
		||||
		durationVal, err := key.Duration()
 | 
			
		||||
		// Skip zero value
 | 
			
		||||
		if err == nil && int(durationVal) > 0 {
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			field.Set(reflect.ValueOf(durationVal))
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		intVal, err := key.Int64()
 | 
			
		||||
		if err != nil || intVal == 0 {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		field.SetInt(intVal)
 | 
			
		||||
| 
						 | 
				
			
			@ -1,312 +0,0 @@
 | 
			
		|||
// Copyright 2015 Unknwon
 | 
			
		||||
//
 | 
			
		||||
// 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 implied. See the
 | 
			
		||||
// License for the specific language governing permissions and limitations
 | 
			
		||||
// under the License.
 | 
			
		||||
 | 
			
		||||
package ini
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unicode"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type tokenType int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_TOKEN_INVALID tokenType = iota
 | 
			
		||||
	_TOKEN_COMMENT
 | 
			
		||||
	_TOKEN_SECTION
 | 
			
		||||
	_TOKEN_KEY
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type parser struct {
 | 
			
		||||
	buf     *bufio.Reader
 | 
			
		||||
	isEOF   bool
 | 
			
		||||
	count   int
 | 
			
		||||
	comment *bytes.Buffer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newParser(r io.Reader) *parser {
 | 
			
		||||
	return &parser{
 | 
			
		||||
		buf:     bufio.NewReader(r),
 | 
			
		||||
		count:   1,
 | 
			
		||||
		comment: &bytes.Buffer{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BOM handles header of BOM-UTF8 format.
 | 
			
		||||
// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding
 | 
			
		||||
func (p *parser) BOM() error {
 | 
			
		||||
	mask, err := p.buf.Peek(3)
 | 
			
		||||
	if err != nil && err != io.EOF {
 | 
			
		||||
		return err
 | 
			
		||||
	} else if len(mask) < 3 {
 | 
			
		||||
		return nil
 | 
			
		||||
	} else if mask[0] == 239 && mask[1] == 187 && mask[2] == 191 {
 | 
			
		||||
		p.buf.Read(mask)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *parser) readUntil(delim byte) ([]byte, error) {
 | 
			
		||||
	data, err := p.buf.ReadBytes(delim)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if err == io.EOF {
 | 
			
		||||
			p.isEOF = true
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return data, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cleanComment(in []byte) ([]byte, bool) {
 | 
			
		||||
	i := bytes.IndexAny(in, "#;")
 | 
			
		||||
	if i == -1 {
 | 
			
		||||
		return nil, false
 | 
			
		||||
	}
 | 
			
		||||
	return in[i:], true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readKeyName(in []byte) (string, int, error) {
 | 
			
		||||
	line := string(in)
 | 
			
		||||
 | 
			
		||||
	// Check if key name surrounded by quotes.
 | 
			
		||||
	var keyQuote string
 | 
			
		||||
	if line[0] == '"' {
 | 
			
		||||
		if len(line) > 6 && string(line[0:3]) == `"""` {
 | 
			
		||||
			keyQuote = `"""`
 | 
			
		||||
		} else {
 | 
			
		||||
			keyQuote = `"`
 | 
			
		||||
		}
 | 
			
		||||
	} else if line[0] == '`' {
 | 
			
		||||
		keyQuote = "`"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get out key name
 | 
			
		||||
	endIdx := -1
 | 
			
		||||
	if len(keyQuote) > 0 {
 | 
			
		||||
		startIdx := len(keyQuote)
 | 
			
		||||
		// FIXME: fail case -> """"""name"""=value
 | 
			
		||||
		pos := strings.Index(line[startIdx:], keyQuote)
 | 
			
		||||
		if pos == -1 {
 | 
			
		||||
			return "", -1, fmt.Errorf("missing closing key quote: %s", line)
 | 
			
		||||
		}
 | 
			
		||||
		pos += startIdx
 | 
			
		||||
 | 
			
		||||
		// Find key-value delimiter
 | 
			
		||||
		i := strings.IndexAny(line[pos+startIdx:], "=:")
 | 
			
		||||
		if i < 0 {
 | 
			
		||||
			return "", -1, fmt.Errorf("key-value delimiter not found: %s", line)
 | 
			
		||||
		}
 | 
			
		||||
		endIdx = pos + i
 | 
			
		||||
		return strings.TrimSpace(line[startIdx:pos]), endIdx + startIdx + 1, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	endIdx = strings.IndexAny(line, "=:")
 | 
			
		||||
	if endIdx < 0 {
 | 
			
		||||
		return "", -1, fmt.Errorf("key-value delimiter not found: %s", line)
 | 
			
		||||
	}
 | 
			
		||||
	return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *parser) readMultilines(line, val, valQuote string) (string, error) {
 | 
			
		||||
	for {
 | 
			
		||||
		data, err := p.readUntil('\n')
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		next := string(data)
 | 
			
		||||
 | 
			
		||||
		pos := strings.LastIndex(next, valQuote)
 | 
			
		||||
		if pos > -1 {
 | 
			
		||||
			val += next[:pos]
 | 
			
		||||
 | 
			
		||||
			comment, has := cleanComment([]byte(next[pos:]))
 | 
			
		||||
			if has {
 | 
			
		||||
				p.comment.Write(bytes.TrimSpace(comment))
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		val += next
 | 
			
		||||
		if p.isEOF {
 | 
			
		||||
			return "", fmt.Errorf("missing closing key quote from '%s' to '%s'", line, next)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return val, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *parser) readContinuationLines(val string) (string, error) {
 | 
			
		||||
	for {
 | 
			
		||||
		data, err := p.readUntil('\n')
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		next := strings.TrimSpace(string(data))
 | 
			
		||||
 | 
			
		||||
		if len(next) == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		val += next
 | 
			
		||||
		if val[len(val)-1] != '\\' {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		val = val[:len(val)-1]
 | 
			
		||||
	}
 | 
			
		||||
	return val, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hasSurroundedQuote check if and only if the first and last characters
 | 
			
		||||
// are quotes \" or \'.
 | 
			
		||||
// It returns false if any other parts also contain same kind of quotes.
 | 
			
		||||
func hasSurroundedQuote(in string, quote byte) bool {
 | 
			
		||||
	return len(in) > 2 && in[0] == quote && in[len(in)-1] == quote &&
 | 
			
		||||
		strings.IndexByte(in[1:], quote) == len(in)-2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *parser) readValue(in []byte) (string, error) {
 | 
			
		||||
	line := strings.TrimLeftFunc(string(in), unicode.IsSpace)
 | 
			
		||||
	if len(line) == 0 {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var valQuote string
 | 
			
		||||
	if len(line) > 3 && string(line[0:3]) == `"""` {
 | 
			
		||||
		valQuote = `"""`
 | 
			
		||||
	} else if line[0] == '`' {
 | 
			
		||||
		valQuote = "`"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(valQuote) > 0 {
 | 
			
		||||
		startIdx := len(valQuote)
 | 
			
		||||
		pos := strings.LastIndex(line[startIdx:], valQuote)
 | 
			
		||||
		// Check for multi-line value
 | 
			
		||||
		if pos == -1 {
 | 
			
		||||
			return p.readMultilines(line, line[startIdx:], valQuote)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return line[startIdx : pos+startIdx], nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Won't be able to reach here if value only contains whitespace.
 | 
			
		||||
	line = strings.TrimSpace(line)
 | 
			
		||||
 | 
			
		||||
	// Check continuation lines
 | 
			
		||||
	if line[len(line)-1] == '\\' {
 | 
			
		||||
		return p.readContinuationLines(line[:len(line)-1])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	i := strings.IndexAny(line, "#;")
 | 
			
		||||
	if i > -1 {
 | 
			
		||||
		p.comment.WriteString(line[i:])
 | 
			
		||||
		line = strings.TrimSpace(line[:i])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Trim single quotes
 | 
			
		||||
	if hasSurroundedQuote(line, '\'') ||
 | 
			
		||||
		hasSurroundedQuote(line, '"') {
 | 
			
		||||
		line = line[1 : len(line)-1]
 | 
			
		||||
	}
 | 
			
		||||
	return line, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parse parses data through an io.Reader.
 | 
			
		||||
func (f *File) parse(reader io.Reader) (err error) {
 | 
			
		||||
	p := newParser(reader)
 | 
			
		||||
	if err = p.BOM(); err != nil {
 | 
			
		||||
		return fmt.Errorf("BOM: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Ignore error because default section name is never empty string.
 | 
			
		||||
	section, _ := f.NewSection(DEFAULT_SECTION)
 | 
			
		||||
 | 
			
		||||
	var line []byte
 | 
			
		||||
	for !p.isEOF {
 | 
			
		||||
		line, err = p.readUntil('\n')
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		line = bytes.TrimLeftFunc(line, unicode.IsSpace)
 | 
			
		||||
		if len(line) == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Comments
 | 
			
		||||
		if line[0] == '#' || line[0] == ';' {
 | 
			
		||||
			// Note: we do not care ending line break,
 | 
			
		||||
			// it is needed for adding second line,
 | 
			
		||||
			// so just clean it once at the end when set to value.
 | 
			
		||||
			p.comment.Write(line)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Section
 | 
			
		||||
		if line[0] == '[' {
 | 
			
		||||
			// Read to the next ']' (TODO: support quoted strings)
 | 
			
		||||
			closeIdx := bytes.IndexByte(line, ']')
 | 
			
		||||
			if closeIdx == -1 {
 | 
			
		||||
				return fmt.Errorf("unclosed section: %s", line)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			section, err = f.NewSection(string(line[1:closeIdx]))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			comment, has := cleanComment(line[closeIdx+1:])
 | 
			
		||||
			if has {
 | 
			
		||||
				p.comment.Write(comment)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			section.Comment = strings.TrimSpace(p.comment.String())
 | 
			
		||||
 | 
			
		||||
			// Reset aotu-counter and comments
 | 
			
		||||
			p.comment.Reset()
 | 
			
		||||
			p.count = 1
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		kname, offset, err := readKeyName(line)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Auto increment.
 | 
			
		||||
		isAutoIncr := false
 | 
			
		||||
		if kname == "-" {
 | 
			
		||||
			isAutoIncr = true
 | 
			
		||||
			kname = "#" + strconv.Itoa(p.count)
 | 
			
		||||
			p.count++
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		key, err := section.NewKey(kname, "")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		key.isAutoIncr = isAutoIncr
 | 
			
		||||
 | 
			
		||||
		value, err := p.readValue(line[offset:])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		key.SetValue(value)
 | 
			
		||||
		key.Comment = strings.TrimSpace(p.comment.String())
 | 
			
		||||
		p.comment.Reset()
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue