middleware_close_response_body.go (3844B)
1 package http 2 3 import ( 4 "context" 5 "io" 6 7 "github.com/aws/smithy-go/logging" 8 "github.com/aws/smithy-go/middleware" 9 ) 10 11 // CloseResponseBody closes the HTTP response body. It leaves the body open only 12 // for a successful response whose payload is a caller-owned stream (isStreaming 13 // with a nil opErr); on error, or for a non-streaming response, it closes the 14 // body — an error response body is diagnostic, not a caller-owned stream. 15 func CloseResponseBody(ctx context.Context, resp *Response, isStreaming bool, opErr error) { 16 if resp == nil || resp.Body == nil { 17 return 18 } 19 if isStreaming && opErr == nil { 20 return 21 } 22 23 // Drain to EOF before closing; a body closed while unread prevents 24 // connection reuse. 25 if _, copyErr := io.Copy(io.Discard, resp.Body); copyErr != nil { 26 middleware.GetLogger(ctx).Logf(logging.Warn, "failed to discard remaining HTTP response body, this may affect connection reuse") 27 } 28 29 if closeErr := resp.Body.Close(); closeErr != nil { 30 middleware.GetLogger(ctx).Logf(logging.Warn, "failed to close HTTP response body, this may affect connection reuse") 31 } 32 } 33 34 // AddErrorCloseResponseBodyMiddleware adds the middleware to automatically 35 // close the response body of an operation request if the request response 36 // failed. 37 // 38 // Deprecated: generated operation deserializers now close the response body 39 // via CloseResponseBody, so this middleware is no longer used. 40 func AddErrorCloseResponseBodyMiddleware(stack *middleware.Stack) error { 41 return stack.Deserialize.Insert(&errorCloseResponseBodyMiddleware{}, "OperationDeserializer", middleware.Before) 42 } 43 44 type errorCloseResponseBodyMiddleware struct{} 45 46 func (*errorCloseResponseBodyMiddleware) ID() string { 47 return "ErrorCloseResponseBody" 48 } 49 50 func (m *errorCloseResponseBodyMiddleware) HandleDeserialize( 51 ctx context.Context, input middleware.DeserializeInput, next middleware.DeserializeHandler, 52 ) ( 53 output middleware.DeserializeOutput, metadata middleware.Metadata, err error, 54 ) { 55 out, metadata, err := next.HandleDeserialize(ctx, input) 56 if err != nil { 57 if resp, ok := out.RawResponse.(*Response); ok && resp != nil && resp.Body != nil { 58 // Consume the full body to prevent TCP connection resets on some platforms 59 _, _ = io.Copy(io.Discard, resp.Body) 60 // Do not validate that the response closes successfully. 61 resp.Body.Close() 62 } 63 } 64 65 return out, metadata, err 66 } 67 68 // AddCloseResponseBodyMiddleware adds the middleware to automatically close 69 // the response body of an operation request, after the response had been 70 // deserialized. 71 // 72 // Deprecated: generated operation deserializers now close the response body 73 // via CloseResponseBody, so this middleware is no longer used. 74 func AddCloseResponseBodyMiddleware(stack *middleware.Stack) error { 75 return stack.Deserialize.Insert(&closeResponseBody{}, "OperationDeserializer", middleware.Before) 76 } 77 78 type closeResponseBody struct{} 79 80 func (*closeResponseBody) ID() string { 81 return "CloseResponseBody" 82 } 83 84 func (m *closeResponseBody) HandleDeserialize( 85 ctx context.Context, input middleware.DeserializeInput, next middleware.DeserializeHandler, 86 ) ( 87 output middleware.DeserializeOutput, metadata middleware.Metadata, err error, 88 ) { 89 out, metadata, err := next.HandleDeserialize(ctx, input) 90 if err != nil { 91 return out, metadata, err 92 } 93 94 if resp, ok := out.RawResponse.(*Response); ok { 95 // Consume the full body to prevent TCP connection resets on some platforms 96 _, copyErr := io.Copy(io.Discard, resp.Body) 97 if copyErr != nil { 98 middleware.GetLogger(ctx).Logf(logging.Warn, "failed to discard remaining HTTP response body, this may affect connection reuse") 99 } 100 101 closeErr := resp.Body.Close() 102 if closeErr != nil { 103 middleware.GetLogger(ctx).Logf(logging.Warn, "failed to close HTTP response body, this may affect connection reuse") 104 } 105 } 106 107 return out, metadata, err 108 }