15
15
16
16
package software .amazon .awssdk .http .crt .internal .response ;
17
17
18
+ import java .nio .ByteBuffer ;
18
19
import java .util .concurrent .CompletableFuture ;
19
20
import software .amazon .awssdk .annotations .SdkInternalApi ;
20
21
import software .amazon .awssdk .crt .CRT ;
26
27
import software .amazon .awssdk .crt .http .HttpStreamResponseHandler ;
27
28
import software .amazon .awssdk .http .HttpStatusFamily ;
28
29
import software .amazon .awssdk .http .SdkHttpResponse ;
29
- import software .amazon .awssdk .http .async .AsyncExecuteRequest ;
30
- import software .amazon .awssdk .http .crt .internal .CrtRequestContext ;
30
+ import software .amazon .awssdk .http .async .SdkAsyncHttpResponseHandler ;
31
31
import software .amazon .awssdk .utils .Logger ;
32
32
import software .amazon .awssdk .utils .Validate ;
33
+ import software .amazon .awssdk .utils .async .SimplePublisher ;
33
34
34
35
/**
35
36
* Implements the CrtHttpStreamHandler API and converts CRT callbacks into calls to SDK AsyncExecuteRequest methods
@@ -39,97 +40,110 @@ public final class CrtResponseAdapter implements HttpStreamResponseHandler {
39
40
private static final Logger log = Logger .loggerFor (CrtResponseAdapter .class );
40
41
41
42
private final HttpClientConnection connection ;
42
- private final CompletableFuture <Void > responseComplete ;
43
- private final AsyncExecuteRequest sdkRequest ;
44
- private final SdkHttpResponse .Builder respBuilder = SdkHttpResponse .builder ();
45
- private final int windowSize ;
46
- private CrtResponseBodyPublisher respBodyPublisher ;
43
+ private final CompletableFuture <Void > completionFuture ;
44
+ private final SdkAsyncHttpResponseHandler responseHandler ;
45
+ private final SimplePublisher <ByteBuffer > responsePublisher = new SimplePublisher <>();
47
46
48
- private CrtResponseAdapter (HttpClientConnection connection ,
49
- CompletableFuture <Void > responseComplete ,
50
- AsyncExecuteRequest sdkRequest ,
51
- int windowSize ) {
52
- this .connection = Validate .notNull (connection , "HttpConnection is null" );
53
- this .responseComplete = Validate .notNull (responseComplete , "reqComplete Future is null" );
54
- this .sdkRequest = Validate .notNull (sdkRequest , "AsyncExecuteRequest Future is null" );
55
- this .windowSize = Validate .isPositive (windowSize , "windowSize is <= 0" );
56
- }
47
+ private final SdkHttpResponse .Builder responseBuilder = SdkHttpResponse .builder ();
57
48
58
- public static HttpStreamResponseHandler toCrtResponseHandler (HttpClientConnection connection ,
59
- CompletableFuture <Void > responseComplete ,
60
- CrtRequestContext request ) {
61
- return new CrtResponseAdapter (connection , responseComplete , request .sdkRequest (), request .readBufferSize ());
49
+ private CrtResponseAdapter (HttpClientConnection connection ,
50
+ CompletableFuture <Void > completionFuture ,
51
+ SdkAsyncHttpResponseHandler responseHandler ) {
52
+ this .connection = Validate .paramNotNull (connection , "connection" );
53
+ this .completionFuture = Validate .paramNotNull (completionFuture , "completionFuture" );
54
+ this .responseHandler = Validate .paramNotNull (responseHandler , "responseHandler" );
62
55
}
63
56
64
- private void initRespBodyPublisherIfNeeded ( HttpStream stream ) {
65
- if ( respBodyPublisher == null ) {
66
- respBodyPublisher = new CrtResponseBodyPublisher ( connection , stream , responseComplete , windowSize );
67
- }
57
+ public static HttpStreamResponseHandler toCrtResponseHandler ( HttpClientConnection crtConn ,
58
+ CompletableFuture < Void > requestFuture ,
59
+ SdkAsyncHttpResponseHandler responseHandler ) {
60
+ return new CrtResponseAdapter ( crtConn , requestFuture , responseHandler );
68
61
}
69
62
70
63
@ Override
71
- public void onResponseHeaders (HttpStream stream , int responseStatusCode , int blockType , HttpHeader [] nextHeaders ) {
72
- initRespBodyPublisherIfNeeded ( stream );
73
-
74
- for ( HttpHeader h : nextHeaders ) {
75
- respBuilder . appendHeader ( h . getName (), h . getValue ());
64
+ public void onResponseHeaders (HttpStream stream , int responseStatusCode , int headerType , HttpHeader [] nextHeaders ) {
65
+ if ( headerType == HttpHeaderBlock . MAIN . getValue ()) {
66
+ for ( HttpHeader h : nextHeaders ) {
67
+ responseBuilder . appendHeader ( h . getName (), h . getValue ());
68
+ }
76
69
}
77
70
}
78
71
79
72
@ Override
80
73
public void onResponseHeadersDone (HttpStream stream , int headerType ) {
81
74
if (headerType == HttpHeaderBlock .MAIN .getValue ()) {
82
- initRespBodyPublisherIfNeeded (stream );
83
-
84
- respBuilder .statusCode (stream .getResponseStatusCode ());
85
- sdkRequest .responseHandler ().onHeaders (respBuilder .build ());
86
- sdkRequest .responseHandler ().onStream (respBodyPublisher );
75
+ responseBuilder .statusCode (stream .getResponseStatusCode ());
76
+ responseHandler .onHeaders (responseBuilder .build ());
77
+ responseHandler .onStream (responsePublisher );
87
78
}
88
79
}
89
80
90
81
@ Override
91
82
public int onResponseBody (HttpStream stream , byte [] bodyBytesIn ) {
92
- initRespBodyPublisherIfNeeded (stream );
83
+ CompletableFuture <Void > writeFuture = responsePublisher .send (ByteBuffer .wrap (bodyBytesIn ));
84
+
85
+ if (writeFuture .isDone () && !writeFuture .isCompletedExceptionally ()) {
86
+ // Optimization: If write succeeded immediately, return non-zero to avoid the extra call back into the CRT.
87
+ return bodyBytesIn .length ;
88
+ }
93
89
94
- respBodyPublisher .queueBuffer (bodyBytesIn );
95
- respBodyPublisher .publishToSubscribers ();
90
+ writeFuture .whenComplete ((result , failure ) -> {
91
+ if (failure != null ) {
92
+ failResponseHandlerAndFuture (stream , failure );
93
+ return ;
94
+ }
95
+
96
+ stream .incrementWindow (bodyBytesIn .length );
97
+ });
96
98
97
- /*
98
- * Intentionally zero. We manually manage the crt stream's window within the body publisher by updating with
99
- * the exact amount we were able to push to the subcriber.
100
- *
101
- * See the call to stream.incrementWindow() in AwsCrtResponseBodyPublisher.
102
- */
103
99
return 0 ;
104
100
}
105
101
106
102
@ Override
107
103
public void onResponseComplete (HttpStream stream , int errorCode ) {
108
- initRespBodyPublisherIfNeeded (stream );
109
-
110
- if (HttpStatusFamily .of (respBuilder .statusCode ()) == HttpStatusFamily .SERVER_ERROR ) {
111
- connection .shutdown ();
112
- }
113
-
114
104
if (errorCode == CRT .AWS_CRT_SUCCESS ) {
115
- log .debug (() -> "Response Completed Successfully" );
116
- respBodyPublisher .setQueueComplete ();
117
- respBodyPublisher .publishToSubscribers ();
105
+ onSuccessfulResponseComplete (stream );
118
106
} else {
119
- HttpException error = new HttpException (errorCode );
120
- log .error (() -> "Response Encountered an Error." , error );
121
-
122
- // Invoke Error Callback on SdkAsyncHttpResponseHandler
123
- try {
124
- sdkRequest .responseHandler ().onError (error );
125
- } catch (Exception e ) {
126
- log .error (() -> String .format ("SdkAsyncHttpResponseHandler %s threw an exception in onError: %s" ,
127
- sdkRequest .responseHandler (), e ));
107
+ onFailedResponseComplete (stream , new HttpException (errorCode ));
108
+ }
109
+ }
110
+
111
+ private void onSuccessfulResponseComplete (HttpStream stream ) {
112
+ responsePublisher .complete ().whenComplete ((result , failure ) -> {
113
+ if (failure != null ) {
114
+ failResponseHandlerAndFuture (stream , failure );
115
+ return ;
116
+ }
117
+
118
+ if (HttpStatusFamily .of (responseBuilder .statusCode ()) == HttpStatusFamily .SERVER_ERROR ) {
119
+ connection .shutdown ();
128
120
}
129
121
130
- // Invoke Error Callback on any Subscriber's of the Response Body
131
- respBodyPublisher .setError (error );
132
- respBodyPublisher .publishToSubscribers ();
122
+ connection .close ();
123
+ stream .close ();
124
+ completionFuture .complete (null );
125
+ });
126
+ }
127
+
128
+ private void onFailedResponseComplete (HttpStream stream , HttpException error ) {
129
+ log .error (() -> "HTTP response encountered an error." , error );
130
+ responsePublisher .error (error );
131
+ failResponseHandlerAndFuture (stream , error );
132
+ }
133
+
134
+ private void failResponseHandlerAndFuture (HttpStream stream , Throwable error ) {
135
+ callResponseHandlerOnError (error );
136
+ completionFuture .completeExceptionally (error );
137
+ connection .shutdown ();
138
+ connection .close ();
139
+ stream .close ();
140
+ }
141
+
142
+ private void callResponseHandlerOnError (Throwable error ) {
143
+ try {
144
+ responseHandler .onError (error );
145
+ } catch (RuntimeException e ) {
146
+ log .warn (() -> "Exception raised from SdkAsyncHttpResponseHandler#onError." , e );
133
147
}
134
148
}
135
149
}
0 commit comments