37
37
import java .io .RandomAccessFile ;
38
38
import java .net .InetSocketAddress ;
39
39
import java .net .URI ;
40
+ import java .util .List ;
40
41
import java .util .Map ;
41
42
import java .util .concurrent .Future ;
42
43
import java .util .concurrent .RejectedExecutionException ;
57
58
import org .asynchttpclient .filter .FilterContext ;
58
59
import org .asynchttpclient .filter .FilterException ;
59
60
import org .asynchttpclient .filter .IOExceptionFilter ;
61
+ import org .asynchttpclient .generators .FileBodyGenerator ;
60
62
import org .asynchttpclient .generators .InputStreamBodyGenerator ;
61
63
import org .asynchttpclient .listener .TransferCompletionHandler ;
62
64
import org .asynchttpclient .multipart .MultipartBody ;
65
+ import org .asynchttpclient .multipart .Part ;
63
66
import org .asynchttpclient .providers .netty .Constants ;
64
67
import org .asynchttpclient .providers .netty .channel .Channels ;
65
68
import org .asynchttpclient .providers .netty .future .FutureReaper ;
@@ -255,8 +258,7 @@ private <T> ListenableFuture<T> sendRequestWithNewChannel(Request request, URI u
255
258
return cl .future ();
256
259
}
257
260
258
- public <T > ListenableFuture <T > sendRequest (final Request request , final AsyncHandler <T > asyncHandler , NettyResponseFuture <T > future , boolean reclaimCache )
259
- throws IOException {
261
+ public <T > ListenableFuture <T > sendRequest (final Request request , final AsyncHandler <T > asyncHandler , NettyResponseFuture <T > future , boolean reclaimCache ) throws IOException {
260
262
261
263
if (closed .get ()) {
262
264
throw new IOException ("Closed" );
@@ -278,17 +280,15 @@ public <T> ListenableFuture<T> sendRequest(final Request request, final AsyncHan
278
280
}
279
281
}
280
282
281
- private void sendFileBody (Channel channel , File file , NettyResponseFuture <?> future ) throws IOException {
283
+ private void sendFileBody (Channel channel , File file , long offset , long fileLength , NettyResponseFuture <?> future ) throws IOException {
282
284
final RandomAccessFile raf = new RandomAccessFile (file , "r" );
283
285
284
286
try {
285
- long fileLength = raf .length ();
286
-
287
287
ChannelFuture writeFuture ;
288
288
if (Channels .getSslHandler (channel ) != null ) {
289
- writeFuture = channel .write (new ChunkedFile (raf , 0 , fileLength , Constants .MAX_BUFFERED_BYTES ), channel .newProgressivePromise ());
289
+ writeFuture = channel .write (new ChunkedFile (raf , offset , fileLength , Constants .MAX_BUFFERED_BYTES ), channel .newProgressivePromise ());
290
290
} else {
291
- FileRegion region = new DefaultFileRegion (raf .getChannel (), 0 , fileLength );
291
+ FileRegion region = new DefaultFileRegion (raf .getChannel (), offset , fileLength );
292
292
writeFuture = channel .write (region , channel .newProgressivePromise ());
293
293
}
294
294
// FIXME probably useless in Netty 4
@@ -345,10 +345,13 @@ public void operationComplete(ChannelProgressiveFuture cf) {
345
345
public void sendBody (final Channel channel , final Body body , NettyResponseFuture <?> future ) {
346
346
Object msg ;
347
347
if (Channels .getSslHandler (channel ) == null && body instanceof RandomAccessBody ) {
348
+ // FIXME also do something for multipart and use a ChunkedInput
348
349
msg = new BodyFileRegion ((RandomAccessBody ) body );
350
+
349
351
} else {
350
- BodyGenerator bg = future .getRequest ().getBodyGenerator ();
351
352
msg = new BodyChunkedInput (body );
353
+
354
+ BodyGenerator bg = future .getRequest ().getBodyGenerator ();
352
355
if (bg instanceof FeedableBodyGenerator ) {
353
356
FeedableBodyGenerator .class .cast (bg ).setListener (new FeedListener () {
354
357
@ Override
@@ -374,38 +377,48 @@ public void operationComplete(ChannelProgressiveFuture cf) {
374
377
channel .writeAndFlush (LastHttpContent .EMPTY_LAST_CONTENT );
375
378
}
376
379
377
- private Body computeBody (HttpRequest nettyRequest , NettyResponseFuture <?> future ) {
378
-
379
- if (nettyRequest .getMethod ().equals (HttpMethod .CONNECT )) {
380
- return null ;
381
- }
380
+ private Body computeBodyAndSetContentLengthOrTransferEncodingHeader (HttpRequest nettyRequest , NettyResponseFuture <?> future ) {
382
381
383
- HttpHeaders headers = nettyRequest .headers ();
384
382
BodyGenerator bg = future .getRequest ().getBodyGenerator ();
383
+ List <Part > parts = future .getRequest ().getParts ();
385
384
Body body = null ;
386
- if (bg != null ) {
387
- try {
388
- body = bg .createBody ();
389
- } catch (IOException ex ) {
390
- throw new IllegalStateException (ex );
385
+
386
+ if (bg != null || parts != null ) {
387
+ HttpHeaders headers = nettyRequest .headers ();
388
+ long length = -1L ;
389
+
390
+ if (bg instanceof FileBodyGenerator ) {
391
+ // don't even compute body, file be be directly passed
392
+ length = FileBodyGenerator .class .cast (bg ).getRegionLength ();
393
+
394
+ } else if (bg instanceof InputStreamBodyGenerator ) {
395
+ // don't even compute body, stream be be directly passed
396
+ length = -1L ;
397
+
398
+ } else if (bg != null ) {
399
+ try {
400
+ body = bg .createBody ();
401
+ length = body .getContentLength ();
402
+
403
+ } catch (IOException ex ) {
404
+ throw new IllegalStateException (ex );
405
+ }
406
+
407
+ } else {
408
+ String contentType = headers .get (HttpHeaders .Names .CONTENT_TYPE );
409
+ String contentLength = nettyRequest .headers ().get (HttpHeaders .Names .CONTENT_LENGTH );
410
+
411
+ if (contentLength != null ) {
412
+ length = Long .parseLong (contentLength );
413
+ }
414
+ body = new MultipartBody (parts , contentType , length );
391
415
}
392
- long length = body . getContentLength ();
416
+
393
417
if (length >= 0 ) {
394
418
headers .set (HttpHeaders .Names .CONTENT_LENGTH , length );
395
419
} else {
396
420
headers .set (HttpHeaders .Names .TRANSFER_ENCODING , HttpHeaders .Values .CHUNKED );
397
421
}
398
- } else if (future .getRequest ().getParts () != null ) {
399
- String contentType = headers .get (HttpHeaders .Names .CONTENT_TYPE );
400
- String contentLength = nettyRequest .headers ().get (HttpHeaders .Names .CONTENT_LENGTH );
401
-
402
- long length = -1 ;
403
- if (contentLength != null ) {
404
- length = Long .parseLong (contentLength );
405
- } else {
406
- nettyRequest .headers ().add (HttpHeaders .Names .TRANSFER_ENCODING , HttpHeaders .Values .CHUNKED );
407
- }
408
- body = new MultipartBody (future .getRequest ().getParts (), contentType , length );
409
422
}
410
423
411
424
return body ;
@@ -449,7 +462,9 @@ public final <T> void writeRequest(final Channel channel, final AsyncHttpClientC
449
462
450
463
HttpRequest nettyRequest = future .getNettyRequest ();
451
464
AsyncHandler <T > handler = future .getAsyncHandler ();
452
- Body body = computeBody (nettyRequest , future );
465
+
466
+ // beware: compute early because actually also write headers
467
+ Body body = computeBodyAndSetContentLengthOrTransferEncodingHeader (nettyRequest , future );
453
468
454
469
if (handler instanceof TransferCompletionHandler ) {
455
470
configureTransferAdapter (handler , nettyRequest );
@@ -477,14 +492,22 @@ public final <T> void writeRequest(final Channel channel, final AsyncHttpClientC
477
492
478
493
// FIXME OK, why? and what's the point of not having a is/get?
479
494
if (future .getAndSetWriteBody (true )) {
480
- if (!future .getNettyRequest ().getMethod ().equals (HttpMethod .CONNECT )) {
495
+ if (!nettyRequest .getMethod ().equals (HttpMethod .CONNECT )) {
496
+
481
497
if (future .getRequest ().getFile () != null ) {
482
- sendFileBody (channel , future .getRequest ().getFile (), future );
498
+ File file = future .getRequest ().getFile ();
499
+ sendFileBody (channel , file , 0 , file .length (), future );
500
+
501
+ } else if (future .getRequest ().getBodyGenerator () instanceof FileBodyGenerator ) {
502
+ // 2 different ways of passing a file, wouhou!
503
+ FileBodyGenerator fileBodyGenerator = (FileBodyGenerator ) future .getRequest ().getBodyGenerator ();
504
+ sendFileBody (channel , fileBodyGenerator .getFile (), fileBodyGenerator .getRegionSeek (), fileBodyGenerator .getRegionLength (), future );
483
505
484
506
} else if (future .getRequest ().getStreamData () != null ) {
485
507
if (sendStreamAndExit (channel , future .getRequest ().getStreamData (), future ))
486
508
return ;
487
509
} else if (future .getRequest ().getBodyGenerator () instanceof InputStreamBodyGenerator ) {
510
+ // 2 different ways of passing a stream, wouhou!
488
511
if (sendStreamAndExit (channel , InputStreamBodyGenerator .class .cast (future .getRequest ().getBodyGenerator ()).getInputStream (), future ))
489
512
return ;
490
513
0 commit comments