From 136e04f8a34a2c2a91829491d0b5a0dbfcb7835d Mon Sep 17 00:00:00 2001 From: Jason Del Ponte <961963+jasdel@users.noreply.github.com> Date: Mon, 21 Feb 2022 14:38:02 -0800 Subject: [PATCH] codegen: Add fix to prevent HTTP/2 event stream request hang on error Adds fix addressing an issue where SDK's bi-directional eventstream API operation request could hang when the HTTP/2 stream was closed by the service with a non-200 status code. Go 1.15 through 1.17 have an issue where the HTTP/2 request can hang while waiting for the HTTP response, and input payload reader. The Expect: 100-Continue workaround forces the HTTP client to wait for a `100 continue` or other HTTP status code before attempting to wait for the request's body to be read. This workaround is not needed for Go 1.18, as a fix to the HTTP client resolves this issue. Related to https://github.com/aws/aws-sdk-go-v2/pull/1515 --- CHANGELOG_PENDING.md | 3 ++ private/model/api/operation.go | 1 + .../eventstream/eventstreamapi/transport.go | 10 +++++ .../eventstreamapi/transport_go1.17.go | 19 +++++++++ .../cust_integ_eventstream_test.go | 42 +++++++++++++++++++ 5 files changed, 75 insertions(+) create mode 100644 private/protocol/eventstream/eventstreamapi/transport.go create mode 100644 private/protocol/eventstream/eventstreamapi/transport_go1.17.go create mode 100644 service/lexruntimev2/cust_integ_eventstream_test.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 8a1927a39c..988e821078 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -3,3 +3,6 @@ ### SDK Enhancements ### SDK Bugs +* `service/lexruntimev2`: Add fix to prevent HTTP/2 event stream request hang on error with Go 1.15 through 1.17. +* `service/transcribestreamingservice`: Add fix to prevent HTTP/2 event stream request hang on error with Go 1.15 through 1.17. + * Adds fix addressing an issue where SDK's bi-directional eventstream API operation request could hang when the HTTP/2 stream was closed by the service with a non-200 status code. diff --git a/private/model/api/operation.go b/private/model/api/operation.go index fea81108e9..e06398e4c6 100644 --- a/private/model/api/operation.go +++ b/private/model/api/operation.go @@ -263,6 +263,7 @@ func (c *{{ .API.StructName }}) {{ .ExportedName }}Request(` + "X-Amz-Content-Sha256": "STREAMING-AWS4-HMAC-SHA256-EVENTS", })) req.Handlers.Build.Swap({{ .API.ProtocolPackage }}.BuildHandler.Name, rest.BuildHandler) + eventstreamapi.ApplyHTTPTransportFixes(req) req.Handlers.Send.Swap(client.LogHTTPRequestHandler.Name, client.LogHTTPRequestHeaderHandler) req.Handlers.Unmarshal.PushBack(es.runInputStream) diff --git a/private/protocol/eventstream/eventstreamapi/transport.go b/private/protocol/eventstream/eventstreamapi/transport.go new file mode 100644 index 0000000000..e3f7d22bd6 --- /dev/null +++ b/private/protocol/eventstream/eventstreamapi/transport.go @@ -0,0 +1,10 @@ +//go:build go1.18 +// +build go1.18 + +package eventstreamapi + +import "github.com/aws/aws-sdk-go/aws/request" + +// This is a no-op for Go 1.18 and above. +func ApplyHTTPTransportFixes(r *request.Request) { +} diff --git a/private/protocol/eventstream/eventstreamapi/transport_go1.17.go b/private/protocol/eventstream/eventstreamapi/transport_go1.17.go new file mode 100644 index 0000000000..2ee2c36fd3 --- /dev/null +++ b/private/protocol/eventstream/eventstreamapi/transport_go1.17.go @@ -0,0 +1,19 @@ +//go:build !go1.18 +// +build !go1.18 + +package eventstreamapi + +import "github.com/aws/aws-sdk-go/aws/request" + +// ApplyHTTPTransportFixes applies fixes to the HTTP request for proper event +// stream functionality. Go 1.15 through 1.17 HTTP client could hang forever +// when an HTTP/2 connection failed with an non-200 status code and err. Using +// Expect 100-Continue, allows the HTTP client to gracefully handle the non-200 +// status code, and close the connection. +// +// This is a no-op for Go 1.18 and above. +func ApplyHTTPTransportFixes(r *request.Request) { + r.Handlers.Sign.PushBack(func(r *request.Request) { + r.HTTPRequest.Header.Set("Expect", "100-Continue") + }) +} diff --git a/service/lexruntimev2/cust_integ_eventstream_test.go b/service/lexruntimev2/cust_integ_eventstream_test.go new file mode 100644 index 0000000000..1df25dc481 --- /dev/null +++ b/service/lexruntimev2/cust_integ_eventstream_test.go @@ -0,0 +1,42 @@ +//go:build integration +// +build integration + +package lexruntimev2 + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/awstesting/integration" +) + +func TestInteg_StartConversation_errorCase(t *testing.T) { + sess := integration.SessionWithDefaultRegion("us-west-2") + + client := New(sess, &aws.Config{ + Logger: t, + LogLevel: aws.LogLevel(aws.LogDebugWithEventStreamBody | aws.LogDebugWithHTTPBody), + }) + + _, err := client.StartConversation(&StartConversationInput{ + BotAliasId: aws.String("mockAlias"), + BotId: aws.String("mockId01234567890"), + LocaleId: aws.String("mockLocale"), + SessionId: aws.String("mockSession"), + }) + if err == nil { + t.Fatalf("expect error, got none") + } + + aErr, ok := err.(awserr.RequestFailure) + if !ok { + t.Fatalf("expect %T error, got %T, %v", aErr, err, err) + } + if aErr.Code() == "" { + t.Errorf("expect error code, got none") + } + if aErr.Message() == "" { + t.Errorf("expect error message, got none") + } +}