|
Packit |
63bb0d |
package request
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
import (
|
|
Packit |
63bb0d |
"io"
|
|
Packit |
63bb0d |
"time"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
var timeoutErr = awserr.New(
|
|
Packit |
63bb0d |
ErrCodeResponseTimeout,
|
|
Packit |
63bb0d |
"read on body has reached the timeout limit",
|
|
Packit |
63bb0d |
nil,
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
type readResult struct {
|
|
Packit |
63bb0d |
n int
|
|
Packit |
63bb0d |
err error
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// timeoutReadCloser will handle body reads that take too long.
|
|
Packit |
63bb0d |
// We will return a ErrReadTimeout error if a timeout occurs.
|
|
Packit |
63bb0d |
type timeoutReadCloser struct {
|
|
Packit |
63bb0d |
reader io.ReadCloser
|
|
Packit |
63bb0d |
duration time.Duration
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Read will spin off a goroutine to call the reader's Read method. We will
|
|
Packit |
63bb0d |
// select on the timer's channel or the read's channel. Whoever completes first
|
|
Packit |
63bb0d |
// will be returned.
|
|
Packit |
63bb0d |
func (r *timeoutReadCloser) Read(b []byte) (int, error) {
|
|
Packit |
63bb0d |
timer := time.NewTimer(r.duration)
|
|
Packit |
63bb0d |
c := make(chan readResult, 1)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
go func() {
|
|
Packit |
63bb0d |
n, err := r.reader.Read(b)
|
|
Packit |
63bb0d |
timer.Stop()
|
|
Packit |
63bb0d |
c <- readResult{n: n, err: err}
|
|
Packit |
63bb0d |
}()
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
select {
|
|
Packit |
63bb0d |
case data := <-c:
|
|
Packit |
63bb0d |
return data.n, data.err
|
|
Packit |
63bb0d |
case <-timer.C:
|
|
Packit |
63bb0d |
return 0, timeoutErr
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func (r *timeoutReadCloser) Close() error {
|
|
Packit |
63bb0d |
return r.reader.Close()
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
const (
|
|
Packit |
63bb0d |
// HandlerResponseTimeout is what we use to signify the name of the
|
|
Packit |
63bb0d |
// response timeout handler.
|
|
Packit |
63bb0d |
HandlerResponseTimeout = "ResponseTimeoutHandler"
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// adaptToResponseTimeoutError is a handler that will replace any top level error
|
|
Packit |
63bb0d |
// to a ErrCodeResponseTimeout, if its child is that.
|
|
Packit |
63bb0d |
func adaptToResponseTimeoutError(req *Request) {
|
|
Packit |
63bb0d |
if err, ok := req.Error.(awserr.Error); ok {
|
|
Packit |
63bb0d |
aerr, ok := err.OrigErr().(awserr.Error)
|
|
Packit |
63bb0d |
if ok && aerr.Code() == ErrCodeResponseTimeout {
|
|
Packit |
63bb0d |
req.Error = aerr
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// WithResponseReadTimeout is a request option that will wrap the body in a timeout read closer.
|
|
Packit |
63bb0d |
// This will allow for per read timeouts. If a timeout occurred, we will return the
|
|
Packit |
63bb0d |
// ErrCodeResponseTimeout.
|
|
Packit |
63bb0d |
//
|
|
Packit |
63bb0d |
// svc.PutObjectWithContext(ctx, params, request.WithTimeoutReadCloser(30 * time.Second)
|
|
Packit |
63bb0d |
func WithResponseReadTimeout(duration time.Duration) Option {
|
|
Packit |
63bb0d |
return func(r *Request) {
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
var timeoutHandler = NamedHandler{
|
|
Packit |
63bb0d |
HandlerResponseTimeout,
|
|
Packit |
63bb0d |
func(req *Request) {
|
|
Packit |
63bb0d |
req.HTTPResponse.Body = &timeoutReadCloser{
|
|
Packit |
63bb0d |
reader: req.HTTPResponse.Body,
|
|
Packit |
63bb0d |
duration: duration,
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// remove the handler so we are not stomping over any new durations.
|
|
Packit |
63bb0d |
r.Handlers.Send.RemoveByName(HandlerResponseTimeout)
|
|
Packit |
63bb0d |
r.Handlers.Send.PushBackNamed(timeoutHandler)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
r.Handlers.Unmarshal.PushBack(adaptToResponseTimeoutError)
|
|
Packit |
63bb0d |
r.Handlers.UnmarshalError.PushBack(adaptToResponseTimeoutError)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|