12
12
*/
13
13
package org .asynchttpclient .listener ;
14
14
15
+ import java .util .concurrent .ConcurrentLinkedQueue ;
16
+ import java .util .concurrent .atomic .AtomicLong ;
17
+
15
18
import org .asynchttpclient .AsyncCompletionHandlerBase ;
16
19
import org .asynchttpclient .FluentCaseInsensitiveStringsMap ;
17
20
import org .asynchttpclient .HttpResponseBodyPart ;
20
23
import org .slf4j .Logger ;
21
24
import org .slf4j .LoggerFactory ;
22
25
23
- import java .io .IOException ;
24
- import java .util .concurrent .ConcurrentLinkedQueue ;
25
-
26
26
/**
27
27
* A {@link org.asynchttpclient.AsyncHandler} that can be used to notify a set of {@link TransferListener}
28
28
* <p/>
29
- * <blockquote><pre>
29
+ * <blockquote>
30
+ *
31
+ * <pre>
30
32
* AsyncHttpClient client = new AsyncHttpClient();
31
33
* TransferCompletionHandler tl = new TransferCompletionHandler();
32
34
* tl.addTransferListener(new TransferListener() {
51
53
* });
52
54
* <p/>
53
55
* Response response = httpClient.prepareGet("http://...").execute(tl).get();
54
- * </pre></blockquote>
56
+ * </pre>
57
+ *
58
+ * </blockquote>
55
59
*/
56
60
public class TransferCompletionHandler extends AsyncCompletionHandlerBase {
57
61
private final static Logger logger = LoggerFactory .getLogger (TransferCompletionHandler .class );
58
62
private final ConcurrentLinkedQueue <TransferListener > listeners = new ConcurrentLinkedQueue <TransferListener >();
59
63
private final boolean accumulateResponseBytes ;
60
64
private TransferAdapter transferAdapter ;
65
+ private AtomicLong bytesTransferred = new AtomicLong (0 );
66
+ private AtomicLong totalBytesToTransfer = new AtomicLong (-1 );
61
67
62
68
/**
63
69
* Create a TransferCompletionHandler that will not accumulate bytes. The resulting {@link org.asynchttpclient.Response#getResponseBody()},
64
- * {@link org.asynchttpclient.Response#getResponseBodyAsStream()} and {@link Response#getResponseBodyExcerpt(int)} will
65
- * throw an IllegalStateException if called.
70
+ * {@link org.asynchttpclient.Response#getResponseBodyAsStream()} and {@link Response#getResponseBodyExcerpt(int)} will throw an IllegalStateException if called.
66
71
*/
67
72
public TransferCompletionHandler () {
68
73
this (false );
69
74
}
70
75
71
76
/**
72
- * Create a TransferCompletionHandler that can or cannot accumulate bytes and make it available when
73
- * {@link org.asynchttpclient.Response#getResponseBody()} get called. The default is false.
74
- *
75
- * @param accumulateResponseBytes true to accumulates bytes in memory.
77
+ * Create a TransferCompletionHandler that can or cannot accumulate bytes and make it available when {@link org.asynchttpclient.Response#getResponseBody()} get called. The
78
+ * default is false.
79
+ *
80
+ * @param accumulateResponseBytes
81
+ * true to accumulates bytes in memory.
76
82
*/
77
83
public TransferCompletionHandler (boolean accumulateResponseBytes ) {
78
84
this .accumulateResponseBytes = accumulateResponseBytes ;
79
85
}
80
86
81
87
/**
82
88
* Add a {@link TransferListener}
83
- *
84
- * @param t a {@link TransferListener}
89
+ *
90
+ * @param t
91
+ * a {@link TransferListener}
85
92
* @return this
86
93
*/
87
94
public TransferCompletionHandler addTransferListener (TransferListener t ) {
@@ -91,8 +98,9 @@ public TransferCompletionHandler addTransferListener(TransferListener t) {
91
98
92
99
/**
93
100
* Remove a {@link TransferListener}
94
- *
95
- * @param t a {@link TransferListener}
101
+ *
102
+ * @param t
103
+ * a {@link TransferListener}
96
104
* @return this
97
105
*/
98
106
public TransferCompletionHandler removeTransferListener (TransferListener t ) {
@@ -102,8 +110,9 @@ public TransferCompletionHandler removeTransferListener(TransferListener t) {
102
110
103
111
/**
104
112
* Associate a {@link TransferCompletionHandler.TransferAdapter} with this listener.
105
- *
106
- * @param transferAdapter {@link TransferAdapter}
113
+ *
114
+ * @param transferAdapter
115
+ * {@link TransferAdapter}
107
116
*/
108
117
public void transferAdapter (TransferAdapter transferAdapter ) {
109
118
this .transferAdapter = transferAdapter ;
@@ -127,6 +136,10 @@ public STATE onBodyPartReceived(final HttpResponseBodyPart content) throws Excep
127
136
128
137
@ Override
129
138
public Response onCompleted (Response response ) throws Exception {
139
+ if (bytesTransferred .get () > 0L ) {
140
+ // onContentWriteCompleted hasn't been notified, it would have been set to -1L (async race)
141
+ onContentWriteCompleted ();
142
+ }
130
143
fireOnEnd ();
131
144
return response ;
132
145
}
@@ -141,16 +154,35 @@ public STATE onHeaderWriteCompleted() {
141
154
142
155
@ Override
143
156
public STATE onContentWriteCompleted () {
157
+ // onContentWriteProgress might not have been called on last write
158
+ long transferred = bytesTransferred .getAndSet (-1L );
159
+ long expected = totalBytesToTransfer .get ();
160
+
161
+ if (expected <= 0L && transferAdapter != null ) {
162
+ FluentCaseInsensitiveStringsMap headers = transferAdapter .getHeaders ();
163
+ String contentLengthString = headers .getFirstValue ("Content-Length" );
164
+ if (contentLengthString != null )
165
+ expected = Long .valueOf (contentLengthString );
166
+ }
167
+
168
+ if (expected > 0L && transferred != expected ) {
169
+ fireOnBytesSent (expected - transferred , expected , expected );
170
+ }
171
+
144
172
return STATE .CONTINUE ;
145
173
}
146
174
147
175
@ Override
148
176
public STATE onContentWriteProgress (long amount , long current , long total ) {
177
+ bytesTransferred .addAndGet (amount );
178
+
179
+ if (total > 0L )
180
+ totalBytesToTransfer .set (total );
181
+
149
182
fireOnBytesSent (amount , current , total );
150
183
return STATE .CONTINUE ;
151
184
}
152
185
153
-
154
186
@ Override
155
187
public void onThrowable (Throwable t ) {
156
188
fireOnThrowable (t );
0 commit comments