From e4c5c7793595ca58810100f90fe0596b227ff774 Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Wed, 6 Nov 2013 21:36:58 +0100 Subject: [PATCH 0001/2211] Port 6f60a45d9ebe4ad0ebd6881746e1671f3a3a8317 on master --- .../multipart/MultipartBody.java | 16 ++- .../multipart/MultipartBodyTest.java | 109 ++++++++++++++++++ 2 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 api/src/test/java/org/asynchttpclient/multipart/MultipartBodyTest.java diff --git a/api/src/main/java/org/asynchttpclient/multipart/MultipartBody.java b/api/src/main/java/org/asynchttpclient/multipart/MultipartBody.java index 70d15e21e2..af9df966e2 100644 --- a/api/src/main/java/org/asynchttpclient/multipart/MultipartBody.java +++ b/api/src/main/java/org/asynchttpclient/multipart/MultipartBody.java @@ -78,7 +78,7 @@ public long read(ByteBuffer buffer) throws IOException { try { int overallLength = 0; - int maxLength = buffer.capacity(); + int maxLength = buffer.remaining(); if (startPart == parts.size() && endWritten) { return overallLength; @@ -132,6 +132,7 @@ public long read(ByteBuffer buffer) throws IOException { initializeFileEnd(currentFilePart); } else if (fileLocation == FileLocation.END) { startPart++; + fileLocation = FileLocation.NONE; if (startPart == parts.size() && currentStream.available() == 0) { doneWritingParts = true; } @@ -146,6 +147,7 @@ public long read(ByteBuffer buffer) throws IOException { initializeFileEnd(currentFilePart); } else if (fileLocation == FileLocation.END) { startPart++; + fileLocation = FileLocation.NONE; if (startPart == parts.size() && currentStream.available() == 0) { doneWritingParts = true; } @@ -165,6 +167,7 @@ public long read(ByteBuffer buffer) throws IOException { initializeFileEnd(currentFilePart); } else if (fileLocation == FileLocation.END) { startPart++; + fileLocation = FileLocation.NONE; if (startPart == parts.size() && currentStream.available() == 0) { doneWritingParts = true; } @@ -202,7 +205,8 @@ public long read(ByteBuffer buffer) throws IOException { private void initializeByteArrayBody(FilePart filePart) throws IOException { - ByteArrayOutputStream output = generateByteArrayBody(filePart); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + filePart.sendData(output); initializeBuffer(output); @@ -388,15 +392,9 @@ private StringPart generateClientStringpart(org.asynchttpclient.Part part) { private long handleByteArrayPart(WritableByteChannel target, FilePart filePart, byte[] data) throws IOException { - ByteArrayOutputStream output = generateByteArrayBody(filePart); - return writeToTarget(target, output); - } - - private ByteArrayOutputStream generateByteArrayBody(FilePart filePart) - throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); Part.sendPart(output, filePart, boundary); - return output; + return writeToTarget(target, output); } private long handleFileEnd(WritableByteChannel target, FilePart filePart) diff --git a/api/src/test/java/org/asynchttpclient/multipart/MultipartBodyTest.java b/api/src/test/java/org/asynchttpclient/multipart/MultipartBodyTest.java new file mode 100644 index 0000000000..9cbccb9ac0 --- /dev/null +++ b/api/src/test/java/org/asynchttpclient/multipart/MultipartBodyTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ +package org.asynchttpclient.multipart; + +import org.asynchttpclient.*; +import org.asynchttpclient.Part; +import org.asynchttpclient.util.AsyncHttpProviderUtils; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +public class MultipartBodyTest { + + @Test(groups = "fast") + public void testBasics() { + final List parts = new ArrayList(); + + // add a file + final File testFile = getTestfile(); + try { + parts.add(new FilePart("filePart", testFile)); + } catch (FileNotFoundException fne) { + Assert.fail("file not found: " + testFile); + } + + // add a byte array + try { + parts.add(new ByteArrayPart("baPart", "fileName", "testMultiPart".getBytes("utf-8"), "application/test", "utf-8")); + } catch (UnsupportedEncodingException ignore) { + } + + // add a string + parts.add(new StringPart("stringPart", "testString", "utf-8")); + + compareContentLength(parts); + } + + private static File getTestfile() { + final ClassLoader cl = MultipartBodyTest.class.getClassLoader(); + final URL url = cl.getResource("textfile.txt"); + Assert.assertNotNull(url); + File file = null; + try { + file = new File(url.toURI()); + } catch (URISyntaxException use) { + Assert.fail("uri syntax error"); + } + return file; + } + + private static void compareContentLength(final List parts) { + Assert.assertNotNull(parts); + // get expected values + MultipartRequestEntity mre = null; + try { + mre = AsyncHttpProviderUtils.createMultipartRequestEntity(parts, new FluentCaseInsensitiveStringsMap()); + } catch (FileNotFoundException fne) { + Assert.fail("file not found: " + parts); + } + final long expectedContentLength = mre.getContentLength(); + + // get real bytes + final Body multipartBody = new MultipartBody(parts, mre.getContentType(), String.valueOf(expectedContentLength)); + try { + final ByteBuffer buffer = ByteBuffer.allocate(8192); + boolean last = false; + long totalBytes = 0; + while (!last) { + long readBytes = 0; + try { + readBytes = multipartBody.read(buffer); + } catch (IOException ie) { + Assert.fail("read failure"); + } + if (readBytes >= 0) { + totalBytes += readBytes; + } else { + last = true; + } + buffer.clear(); + } + Assert.assertEquals(totalBytes, expectedContentLength); + } finally { + try { + multipartBody.close(); + } catch (IOException ignore) { + } + } + } +} From a4f00a057f9f71c496c26a87ca45cb2166f2bbdc Mon Sep 17 00:00:00 2001 From: Bongjae Chang Date: Thu, 7 Nov 2013 11:43:09 +0900 Subject: [PATCH 0002/2211] Fixed infinite loop of MultipartBodyTest for master branch. --- .../java/org/asynchttpclient/multipart/MultipartBodyTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/test/java/org/asynchttpclient/multipart/MultipartBodyTest.java b/api/src/test/java/org/asynchttpclient/multipart/MultipartBodyTest.java index 9cbccb9ac0..1e8ba2860c 100644 --- a/api/src/test/java/org/asynchttpclient/multipart/MultipartBodyTest.java +++ b/api/src/test/java/org/asynchttpclient/multipart/MultipartBodyTest.java @@ -91,7 +91,7 @@ private static void compareContentLength(final List parts) { } catch (IOException ie) { Assert.fail("read failure"); } - if (readBytes >= 0) { + if (readBytes > 0) { totalBytes += readBytes; } else { last = true; From 0f7615655c85f925cb81c377bd4205e9d5ed8bfb Mon Sep 17 00:00:00 2001 From: Bongjae Chang Date: Thu, 7 Nov 2013 12:24:52 +0900 Subject: [PATCH 0003/2211] MultipartBody#read() returns -1 when it meets EOF. Current MultipartBody#read() returns only 0 when it meets both Exception and EOF(endWritten). Then caller can't know whether the value is EOF or Error. --- .../main/java/org/asynchttpclient/multipart/MultipartBody.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/org/asynchttpclient/multipart/MultipartBody.java b/api/src/main/java/org/asynchttpclient/multipart/MultipartBody.java index af9df966e2..f72aaf62c1 100644 --- a/api/src/main/java/org/asynchttpclient/multipart/MultipartBody.java +++ b/api/src/main/java/org/asynchttpclient/multipart/MultipartBody.java @@ -81,7 +81,7 @@ public long read(ByteBuffer buffer) throws IOException { int maxLength = buffer.remaining(); if (startPart == parts.size() && endWritten) { - return overallLength; + return -1; } boolean full = false; From 3df777f7e3230711570b30348c1cd09dcd15ad01 Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Fri, 8 Nov 2013 09:33:18 +0100 Subject: [PATCH 0004/2211] Upgrade Netty 4.0.12 and javassist 3.18.1 --- providers/netty/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/providers/netty/pom.xml b/providers/netty/pom.xml index 471355e0a0..9b884d134d 100644 --- a/providers/netty/pom.xml +++ b/providers/netty/pom.xml @@ -39,12 +39,12 @@ io.netty netty-all - 4.0.11.Final + 4.0.12.Final org.javassist javassist - 3.18.0-GA + 3.18.1-GA From 3286d44d4a3682ad9230c8e9b21fb821eb51488c Mon Sep 17 00:00:00 2001 From: Ryan Lubke Date: Mon, 11 Nov 2013 08:58:24 -0800 Subject: [PATCH 0005/2211] Port changes for #411 from 1.7 to master. --- .../grizzly/bodyhandler/PartsBodyHandler.java | 86 +++++++++++++++---- 1 file changed, 68 insertions(+), 18 deletions(-) diff --git a/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/bodyhandler/PartsBodyHandler.java b/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/bodyhandler/PartsBodyHandler.java index ba656d0d68..f4491f391f 100644 --- a/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/bodyhandler/PartsBodyHandler.java +++ b/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/bodyhandler/PartsBodyHandler.java @@ -13,17 +13,22 @@ package org.asynchttpclient.providers.grizzly.bodyhandler; +import org.asynchttpclient.Body; +import org.asynchttpclient.Part; import org.asynchttpclient.Request; +import org.asynchttpclient.multipart.MultipartBody; import org.asynchttpclient.multipart.MultipartRequestEntity; +import org.asynchttpclient.providers.grizzly.FeedableBodyGenerator; +import org.asynchttpclient.providers.grizzly.GrizzlyAsyncHttpProvider; import org.asynchttpclient.util.AsyncHttpProviderUtils; import org.glassfish.grizzly.Buffer; import org.glassfish.grizzly.filterchain.FilterChainContext; -import org.glassfish.grizzly.http.HttpContent; import org.glassfish.grizzly.http.HttpRequestPacket; +import org.glassfish.grizzly.memory.Buffers; import org.glassfish.grizzly.memory.MemoryManager; -import org.glassfish.grizzly.utils.BufferOutputStream; import java.io.IOException; +import java.util.List; import static org.asynchttpclient.util.MiscUtil.isNonEmpty; @@ -42,25 +47,70 @@ public boolean doHandle(final FilterChainContext ctx, final HttpRequestPacket requestPacket) throws IOException { - MultipartRequestEntity mre = + final List parts = request.getParts(); + final MultipartRequestEntity mre = AsyncHttpProviderUtils.createMultipartRequestEntity( - request.getParts(), - request.getHeaders()); - requestPacket.setContentLengthLong(mre.getContentLength()); - requestPacket.setContentType(mre.getContentType()); - final MemoryManager mm = ctx.getMemoryManager(); - Buffer b = mm.allocate(512); - BufferOutputStream o = new BufferOutputStream(mm, b, true); - mre.writeRequest(o); - b = o.getBuffer(); - b.trim(); - if (b.hasRemaining()) { - final HttpContent content = requestPacket.httpContentBuilder().content(b).build(); - content.setLast(true); - ctx.write(content, ((!requestPacket.isCommitted()) ? ctx.getTransportContext().getCompletionHandler() : null)); + parts, request.getHeaders()); + final long contentLength = mre.getContentLength(); + final String contentType = mre.getContentType(); + requestPacket.setContentLengthLong(contentLength); + requestPacket.setContentType(contentType); + if (GrizzlyAsyncHttpProvider.LOGGER.isDebugEnabled()) { + GrizzlyAsyncHttpProvider.LOGGER.debug( + "REQUEST(modified): contentLength={}, contentType={}", + new Object[]{ + requestPacket.getContentLength(), + requestPacket.getContentType() + }); } - return true; + final FeedableBodyGenerator generator = new FeedableBodyGenerator() { + @Override + public Body createBody() throws IOException { + return new MultipartBody(parts, contentType, + String.valueOf(contentLength)); + } + }; + generator.setFeeder(new FeedableBodyGenerator.BaseFeeder(generator) { + @Override + public void flush() throws IOException { + final Body bodyLocal = feedableBodyGenerator.createBody(); + try { + final MemoryManager mm = ctx.getMemoryManager(); + boolean last = false; + while (!last) { + Buffer buffer = mm.allocate(BodyHandler.MAX_CHUNK_SIZE); + buffer.allowBufferDispose(true); + final long readBytes = + bodyLocal.read(buffer.toByteBuffer()); + if (readBytes > 0) { + buffer.position((int) readBytes); + buffer.trim(); + } else { + buffer.dispose(); + if (readBytes < 0) { + last = true; + buffer = Buffers.EMPTY_BUFFER; + } else { + throw new IllegalStateException( + "MultipartBody unexpectedly returned 0 bytes available"); + } + } + feed(buffer, last); + } + } finally { + if (bodyLocal != null) { + try { + bodyLocal.close(); + } catch (IOException ignore) { + } + } + } + } + }); + generator.initializeAsynchronousTransfer(ctx, requestPacket); + return false; + } } // END PartsBodyHandler From 6530497ddb512477d72b91b2ae87b8bd369d0b56 Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Tue, 12 Nov 2013 11:34:08 +0100 Subject: [PATCH 0006/2211] Introduce a constant exception for Remotely Closed --- .../main/java/org/asynchttpclient/AsyncHttpClient.java | 2 ++ .../org/asynchttpclient/util/AsyncHttpProviderUtils.java | 9 ++++++++- .../java/org/asynchttpclient/async/AuthTimeoutTest.java | 3 ++- .../asynchttpclient/providers/grizzly/HttpTxContext.java | 3 ++- .../providers/netty/handler/NettyChannelHandler.java | 3 ++- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/org/asynchttpclient/AsyncHttpClient.java b/api/src/main/java/org/asynchttpclient/AsyncHttpClient.java index 18ddaa77fe..85e19adc55 100755 --- a/api/src/main/java/org/asynchttpclient/AsyncHttpClient.java +++ b/api/src/main/java/org/asynchttpclient/AsyncHttpClient.java @@ -20,7 +20,9 @@ import org.asynchttpclient.filter.FilterException; import org.asynchttpclient.filter.RequestFilter; import org.asynchttpclient.resumable.ResumableAsyncHandler; + import java.io.Closeable; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/api/src/main/java/org/asynchttpclient/util/AsyncHttpProviderUtils.java b/api/src/main/java/org/asynchttpclient/util/AsyncHttpProviderUtils.java index a0599eecf6..bca98acabb 100644 --- a/api/src/main/java/org/asynchttpclient/util/AsyncHttpProviderUtils.java +++ b/api/src/main/java/org/asynchttpclient/util/AsyncHttpProviderUtils.java @@ -14,6 +14,7 @@ import java.io.ByteArrayInputStream; import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; import java.io.SequenceInputStream; import java.io.UnsupportedEncodingException; @@ -52,7 +53,13 @@ * The cookies's handling code is from the Netty framework. */ public class AsyncHttpProviderUtils { - + + public static final IOException REMOTELY_CLOSED_EXCEPTION = new IOException("Remotely Closed"); + + static { + REMOTELY_CLOSED_EXCEPTION.setStackTrace(new StackTraceElement[] {}); + } + private final static byte[] NO_BYTES = new byte[0]; public final static String DEFAULT_CHARSET = "ISO-8859-1"; diff --git a/api/src/test/java/org/asynchttpclient/async/AuthTimeoutTest.java b/api/src/test/java/org/asynchttpclient/async/AuthTimeoutTest.java index 962e996cd2..9cb3d6026b 100644 --- a/api/src/test/java/org/asynchttpclient/async/AuthTimeoutTest.java +++ b/api/src/test/java/org/asynchttpclient/async/AuthTimeoutTest.java @@ -28,6 +28,7 @@ import org.asynchttpclient.AsyncHttpClientConfig; import org.asynchttpclient.Realm; import org.asynchttpclient.Response; +import org.asynchttpclient.util.AsyncHttpProviderUtils; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; @@ -195,7 +196,7 @@ public void digestFuturePreemptiveAuthTimeoutTest() throws Exception { protected void inspectException(Throwable t) { assertNotNull(t.getCause()); assertEquals(t.getCause().getClass(), IOException.class); - if (!t.getCause().getMessage().startsWith("Remotely Closed")) { + if (t.getCause() != AsyncHttpProviderUtils.REMOTELY_CLOSED_EXCEPTION) { fail(); } } diff --git a/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/HttpTxContext.java b/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/HttpTxContext.java index 3ea419bf67..ce4368dd97 100644 --- a/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/HttpTxContext.java +++ b/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/HttpTxContext.java @@ -17,6 +17,7 @@ import org.asynchttpclient.Request; import org.asynchttpclient.providers.grizzly.bodyhandler.BodyHandler; import org.asynchttpclient.providers.grizzly.statushandler.StatusHandler; +import org.asynchttpclient.util.AsyncHttpProviderUtils; import org.asynchttpclient.websocket.WebSocket; import org.glassfish.grizzly.CloseListener; import org.glassfish.grizzly.CloseType; @@ -67,7 +68,7 @@ public final class HttpTxContext { public void onClosed(Closeable closeable, CloseType type) throws IOException { if (CloseType.REMOTELY.equals(type)) { - abort(new IOException("Remotely Closed")); + abort(AsyncHttpProviderUtils.REMOTELY_CLOSED_EXCEPTION); } } }; diff --git a/providers/netty/src/main/java/org/asynchttpclient/providers/netty/handler/NettyChannelHandler.java b/providers/netty/src/main/java/org/asynchttpclient/providers/netty/handler/NettyChannelHandler.java index 03f68ae246..e2cacfeae7 100644 --- a/providers/netty/src/main/java/org/asynchttpclient/providers/netty/handler/NettyChannelHandler.java +++ b/providers/netty/src/main/java/org/asynchttpclient/providers/netty/handler/NettyChannelHandler.java @@ -35,6 +35,7 @@ import org.asynchttpclient.providers.netty.future.NettyResponseFuture; import org.asynchttpclient.providers.netty.future.NettyResponseFutures; import org.asynchttpclient.providers.netty.request.NettyRequestSender; +import org.asynchttpclient.util.AsyncHttpProviderUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -119,7 +120,7 @@ public void channelInactive(ChannelHandlerContext ctx) throws Exception { if (future != null && !future.isDone() && !future.isCancelled()) { if (!requestSender.retry(ctx.channel(), future)) { - channels.abort(future, new IOException("Remotely Closed")); + channels.abort(future, AsyncHttpProviderUtils.REMOTELY_CLOSED_EXCEPTION); } } else { channels.closeChannel(ctx); From 55989012bc109d03d95d4e74091616bf88fd35b5 Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Tue, 12 Nov 2013 11:37:23 +0100 Subject: [PATCH 0007/2211] append to previous commit --- .../test/java/org/asynchttpclient/async/RetryRequestTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/src/test/java/org/asynchttpclient/async/RetryRequestTest.java b/api/src/test/java/org/asynchttpclient/async/RetryRequestTest.java index 6c27502c6e..fdbc1af6dc 100644 --- a/api/src/test/java/org/asynchttpclient/async/RetryRequestTest.java +++ b/api/src/test/java/org/asynchttpclient/async/RetryRequestTest.java @@ -14,6 +14,7 @@ import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.AsyncHttpClientConfig; +import org.asynchttpclient.util.AsyncHttpProviderUtils; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.testng.annotations.Test; @@ -21,6 +22,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + import java.io.IOException; import java.io.OutputStream; @@ -76,7 +78,7 @@ public void testMaxRetry() throws Exception { } catch (Exception t) { assertNotNull(t.getCause()); assertEquals(t.getCause().getClass(), IOException.class); - if (!t.getCause().getMessage().startsWith("Remotely Closed")) { + if (t.getCause() != AsyncHttpProviderUtils.REMOTELY_CLOSED_EXCEPTION) { fail(); } } finally { From d673a45fe5bd4ce890e061417daf6d2a720153b3 Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Thu, 14 Nov 2013 14:55:42 +0100 Subject: [PATCH 0008/2211] All HTTP methods allow passing a body, close #421 --- .../main/java/org/asynchttpclient/RequestBuilderBase.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/api/src/main/java/org/asynchttpclient/RequestBuilderBase.java b/api/src/main/java/org/asynchttpclient/RequestBuilderBase.java index d737158ade..0a5f6789b1 100644 --- a/api/src/main/java/org/asynchttpclient/RequestBuilderBase.java +++ b/api/src/main/java/org/asynchttpclient/RequestBuilderBase.java @@ -614,7 +614,7 @@ public T setConnectionPoolKeyStrategy(ConnectionPoolKeyStrategy connectionPoolKe } public Request build() { - if ((request.length < 0) && (request.streamData == null) && allowBody(request.getMethod())) { + if (request.length < 0 && request.streamData == null) { // can't concatenate content-length String contentLength = null; if (request.headers != null && request.headers.isEmpty()) { @@ -635,10 +635,6 @@ public Request build() { return request; } - private boolean allowBody(String method) { - return !(method.equalsIgnoreCase("GET") || method.equalsIgnoreCase("OPTIONS") || method.equalsIgnoreCase("TRACE") || method.equalsIgnoreCase("HEAD")); - } - public T addOrReplaceCookie(Cookie cookie) { String cookieKey = cookie.getName(); boolean replace = false; From 40e2536d5336cd781c6dc59c36ccdeef87d70ad1 Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Tue, 26 Nov 2013 09:43:15 +0100 Subject: [PATCH 0009/2211] Port #424 on master --- .../providers/netty/NettyAsyncHttpProvider.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/providers/netty/src/main/java/org/asynchttpclient/providers/netty/NettyAsyncHttpProvider.java b/providers/netty/src/main/java/org/asynchttpclient/providers/netty/NettyAsyncHttpProvider.java index d67821c497..4b820aab87 100644 --- a/providers/netty/src/main/java/org/asynchttpclient/providers/netty/NettyAsyncHttpProvider.java +++ b/providers/netty/src/main/java/org/asynchttpclient/providers/netty/NettyAsyncHttpProvider.java @@ -55,8 +55,9 @@ public NettyAsyncHttpProvider(AsyncHttpClientConfig config) { @Override public String toString() { + int availablePermits = channels.freeConnections != null ? channels.freeConnections.availablePermits() : 0; return String.format("NettyAsyncHttpProvider4:\n\t- maxConnections: %d\n\t- openChannels: %s\n\t- connectionPools: %s", config.getMaxTotalConnections() - - channels.freeConnections.availablePermits(), channels.openChannels.toString(), channels.connectionsPool.toString()); + - availablePermits, channels.openChannels.toString(), channels.connectionsPool.toString()); } @Override From aa42d85261c632049ed99747cd3705e5d6c842a4 Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Thu, 28 Nov 2013 22:28:16 +0100 Subject: [PATCH 0010/2211] Upgrade Netty 4.0.13 --- providers/netty/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/netty/pom.xml b/providers/netty/pom.xml index 9b884d134d..818df789ec 100644 --- a/providers/netty/pom.xml +++ b/providers/netty/pom.xml @@ -39,7 +39,7 @@ io.netty netty-all - 4.0.12.Final + 4.0.13.Final org.javassist From 19644726ce7f7fe10d14a1e560d0cdc325cf973f Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Fri, 29 Nov 2013 15:35:22 +0100 Subject: [PATCH 0011/2211] Allow Multipart with unknown content length, close #427 --- .../org/asynchttpclient/multipart/MultipartBody.java | 4 ++-- .../providers/netty/request/NettyRequestSender.java | 9 ++++++++- .../providers/netty/request/NettyRequests.java | 4 +++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/org/asynchttpclient/multipart/MultipartBody.java b/api/src/main/java/org/asynchttpclient/multipart/MultipartBody.java index f72aaf62c1..99525d0a21 100644 --- a/api/src/main/java/org/asynchttpclient/multipart/MultipartBody.java +++ b/api/src/main/java/org/asynchttpclient/multipart/MultipartBody.java @@ -49,9 +49,9 @@ public class MultipartBody implements RandomAccessBody { enum FileLocation {NONE, START, MIDDLE, END} - public MultipartBody(List parts, String contentType, String contentLength) { + public MultipartBody(List parts, String contentType, long contentLength) { this.boundary = MultipartEncodingUtil.getAsciiBytes(contentType.substring(contentType.indexOf("boundary=") + "boundary=".length())); - this.contentLength = Long.parseLong(contentLength); + this.contentLength = contentLength; this.parts = parts; files = new ArrayList(); diff --git a/providers/netty/src/main/java/org/asynchttpclient/providers/netty/request/NettyRequestSender.java b/providers/netty/src/main/java/org/asynchttpclient/providers/netty/request/NettyRequestSender.java index 5c049d70ba..3a83fcc695 100644 --- a/providers/netty/src/main/java/org/asynchttpclient/providers/netty/request/NettyRequestSender.java +++ b/providers/netty/src/main/java/org/asynchttpclient/providers/netty/request/NettyRequestSender.java @@ -439,7 +439,14 @@ private Body computeBody(HttpRequest nettyRequest, NettyResponseFuture future } } else if (future.getRequest().getParts() != null) { String contentType = headers.get(HttpHeaders.Names.CONTENT_TYPE); - String length = headers.get(HttpHeaders.Names.CONTENT_LENGTH); + String contentLength = nettyRequest.headers().get(HttpHeaders.Names.CONTENT_LENGTH); + + long length = -1; + if (contentLength != null) { + length = Long.parseLong(contentLength); + } else { + nettyRequest.headers().add(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED); + } body = new MultipartBody(future.getRequest().getParts(), contentType, length); } diff --git a/providers/netty/src/main/java/org/asynchttpclient/providers/netty/request/NettyRequests.java b/providers/netty/src/main/java/org/asynchttpclient/providers/netty/request/NettyRequests.java index d5821471eb..3827789185 100644 --- a/providers/netty/src/main/java/org/asynchttpclient/providers/netty/request/NettyRequests.java +++ b/providers/netty/src/main/java/org/asynchttpclient/providers/netty/request/NettyRequests.java @@ -260,7 +260,9 @@ else if (uri.getRawQuery() != null) MultipartRequestEntity mre = AsyncHttpProviderUtils.createMultipartRequestEntity(request.getParts(), request.getHeaders()); headers.put(HttpHeaders.Names.CONTENT_TYPE, mre.getContentType()); - headers.put(HttpHeaders.Names.CONTENT_LENGTH, mre.getContentLength()); + if (mre.getContentLength() >= 0) { + headers.put(HttpHeaders.Names.CONTENT_LENGTH, mre.getContentLength()); + } hasDeferredContent = true; } else if (request.getFile() != null) { From 895adc259fd3b9ef2f1f63b7dd026a92b6e82b15 Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Fri, 29 Nov 2013 15:38:33 +0100 Subject: [PATCH 0012/2211] dammit --- .../providers/grizzly/bodyhandler/PartsBodyHandler.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/bodyhandler/PartsBodyHandler.java b/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/bodyhandler/PartsBodyHandler.java index f4491f391f..5dcc1bc710 100644 --- a/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/bodyhandler/PartsBodyHandler.java +++ b/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/bodyhandler/PartsBodyHandler.java @@ -67,8 +67,7 @@ public boolean doHandle(final FilterChainContext ctx, final FeedableBodyGenerator generator = new FeedableBodyGenerator() { @Override public Body createBody() throws IOException { - return new MultipartBody(parts, contentType, - String.valueOf(contentLength)); + return new MultipartBody(parts, contentType, contentLength); } }; generator.setFeeder(new FeedableBodyGenerator.BaseFeeder(generator) { From 81d59c1dd45a51359913d0b907542f6fe40a37c0 Mon Sep 17 00:00:00 2001 From: Jeanfrancois Arcand Date: Tue, 3 Dec 2013 16:24:29 -0500 Subject: [PATCH 0013/2211] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 82921e4652..1eb2503dad 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Async Http Client library purpose is to allow Java applications to easily execut com.ning async-http-client - 1.7.20 + 1.7.22 ``` From 01aa462959ef98e64f8019856d5009813fcb09d8 Mon Sep 17 00:00:00 2001 From: Ryan Lubke Date: Tue, 3 Dec 2013 19:41:54 -0800 Subject: [PATCH 0014/2211] Fix compilation error. --- .../java/org/asynchttpclient/multipart/MultipartBodyTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/test/java/org/asynchttpclient/multipart/MultipartBodyTest.java b/api/src/test/java/org/asynchttpclient/multipart/MultipartBodyTest.java index 1e8ba2860c..ceb62a8dd0 100644 --- a/api/src/test/java/org/asynchttpclient/multipart/MultipartBodyTest.java +++ b/api/src/test/java/org/asynchttpclient/multipart/MultipartBodyTest.java @@ -79,7 +79,7 @@ private static void compareContentLength(final List parts) { final long expectedContentLength = mre.getContentLength(); // get real bytes - final Body multipartBody = new MultipartBody(parts, mre.getContentType(), String.valueOf(expectedContentLength)); + final Body multipartBody = new MultipartBody(parts, mre.getContentType(), expectedContentLength); try { final ByteBuffer buffer = ByteBuffer.allocate(8192); boolean last = false; From 22cb35acf9f55526917a93c7bb5d37933d6e44da Mon Sep 17 00:00:00 2001 From: Ryan Lubke Date: Tue, 3 Dec 2013 19:45:17 -0800 Subject: [PATCH 0015/2211] Fix for #429. --- .../providers/grizzly/bodyhandler/ByteArrayBodyHandler.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/bodyhandler/ByteArrayBodyHandler.java b/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/bodyhandler/ByteArrayBodyHandler.java index 9ed538e5fe..c995adef2c 100644 --- a/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/bodyhandler/ByteArrayBodyHandler.java +++ b/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/bodyhandler/ByteArrayBodyHandler.java @@ -46,11 +46,7 @@ public boolean doHandle(final FilterChainContext ctx, final HttpRequestPacket requestPacket) throws IOException { - String charset = request.getBodyEncoding(); - if (charset == null) { - charset = Charsets.ASCII_CHARSET.name(); - } - final byte[] data = new String(request.getByteData(), charset).getBytes(charset); + final byte[] data = request.getByteData(); final MemoryManager mm = ctx.getMemoryManager(); final Buffer gBuffer = Buffers.wrap(mm, data); if (requestPacket.getContentLength() == -1) { From ed9f9b3b2758ab6b545ff8f294f4eabb604bd338 Mon Sep 17 00:00:00 2001 From: Lukasz Kryger Date: Sun, 8 Dec 2013 22:32:07 +0000 Subject: [PATCH 0016/2211] Nitpicking: "it's" -> "its" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1eb2503dad..561933cd2f 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ Future f = c.prepareGet("http://www.ning.com/").execute(new AsyncHandler String bodyResponse = f.get(); ``` -Finally, you can also configure the AsyncHttpClient via it's AsyncHttpClientConfig object: +Finally, you can also configure the AsyncHttpClient via its AsyncHttpClientConfig object: ```java AsyncHttpClientConfig cf = new AsyncHttpClientConfig.Builder() From 8373cc31ac81498c45a6471163df43c6a0db6029 Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Thu, 12 Dec 2013 12:14:26 +0100 Subject: [PATCH 0017/2211] Implement onRequestSent for Netty provider, close #434 --- .../AsyncHandlerExtensions.java | 38 +++++++++++++++++++ .../netty/request/NettyRequestSender.java | 4 ++ 2 files changed, 42 insertions(+) create mode 100644 api/src/main/java/org/asynchttpclient/AsyncHandlerExtensions.java diff --git a/api/src/main/java/org/asynchttpclient/AsyncHandlerExtensions.java b/api/src/main/java/org/asynchttpclient/AsyncHandlerExtensions.java new file mode 100644 index 0000000000..858d14201f --- /dev/null +++ b/api/src/main/java/org/asynchttpclient/AsyncHandlerExtensions.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010-2012 Sonatype, Inc. All rights reserved. + * + * This program is licensed to you under the Apache License Version 2.0, + * and you may not use this file except in compliance with the Apache License Version 2.0. + * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the Apache License Version 2.0 is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. + */ +package org.asynchttpclient; + +/** + * This interface hosts new low level callback methods on {@link AsyncHandler}. + * For now, those methods are in a dedicated interface in order not to break the existing API, + * but could be merged into one of the existing ones in AHC 2. + * + * More additional hooks might come, such as: + *
    + *
  • onRetry()
  • + *
  • onConnected()
  • + *
  • onConnectionClosed()
  • + *
  • onBytesSent(long numberOfBytes)
  • + *
  • onBytesReceived(long numberOfBytes)
  • + *
+ */ +public interface AsyncHandlerExtensions { + + /** + * Notify the callback when a request is being written on the wire. + * If the original request causes multiple requests to be sent, for example, because of authorization or retry, + * it will be notified multiple times. + * Currently only supported by the Netty provider. + */ + void onRequestSent(); +} \ No newline at end of file diff --git a/providers/netty/src/main/java/org/asynchttpclient/providers/netty/request/NettyRequestSender.java b/providers/netty/src/main/java/org/asynchttpclient/providers/netty/request/NettyRequestSender.java index 3a83fcc695..364c36567a 100644 --- a/providers/netty/src/main/java/org/asynchttpclient/providers/netty/request/NettyRequestSender.java +++ b/providers/netty/src/main/java/org/asynchttpclient/providers/netty/request/NettyRequestSender.java @@ -45,6 +45,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.asynchttpclient.AsyncHandler; +import org.asynchttpclient.AsyncHandlerExtensions; import org.asynchttpclient.AsyncHttpClientConfig; import org.asynchttpclient.Body; import org.asynchttpclient.BodyGenerator; @@ -501,6 +502,9 @@ public final void writeRequest(final Channel channel, final AsyncHttpClientC // FIXME That doesn't just leave to true, the set is always done? and what's the point of not having a is/get? if (future.getAndSetWriteHeaders(true)) { try { + if (future.getAsyncHandler() instanceof AsyncHandlerExtensions) { + AsyncHandlerExtensions.class.cast(future.getAsyncHandler()).onRequestSent(); + } channel.writeAndFlush(nettyRequest, channel.newProgressivePromise()).addListener(new ProgressListener(config, true, future.getAsyncHandler(), future)); } catch (Throwable cause) { // FIXME why not notify? From 31b3f44079ff5cfa7be277d6e793e1c129e4189a Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Thu, 12 Dec 2013 12:17:07 +0100 Subject: [PATCH 0018/2211] Fix deprecation --- .../org/asynchttpclient/providers/netty/channel/Channels.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/netty/src/main/java/org/asynchttpclient/providers/netty/channel/Channels.java b/providers/netty/src/main/java/org/asynchttpclient/providers/netty/channel/Channels.java index bc1f091d02..b5bbbb51dc 100644 --- a/providers/netty/src/main/java/org/asynchttpclient/providers/netty/channel/Channels.java +++ b/providers/netty/src/main/java/org/asynchttpclient/providers/netty/channel/Channels.java @@ -77,7 +77,7 @@ public class Channels { public static final String WS_DECODER_HANDLER = "ws-decoder"; public static final String WS_ENCODER_HANDLER = "ws-encoder"; - private static final AttributeKey DEFAULT_ATTRIBUTE = new AttributeKey("default"); + private static final AttributeKey DEFAULT_ATTRIBUTE = AttributeKey.valueOf("default"); private final AsyncHttpClientConfig config; private final NettyAsyncHttpProviderConfig asyncHttpProviderConfig; From 18b73b7433977f6a58cd17b67599342e99bbb8cb Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Thu, 12 Dec 2013 12:20:41 +0100 Subject: [PATCH 0019/2211] Add new onRetry callback method for #435 --- .../java/org/asynchttpclient/AsyncHandlerExtensions.java | 8 ++++++-- .../providers/netty/request/NettyRequestSender.java | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/org/asynchttpclient/AsyncHandlerExtensions.java b/api/src/main/java/org/asynchttpclient/AsyncHandlerExtensions.java index 858d14201f..1991d0aba1 100644 --- a/api/src/main/java/org/asynchttpclient/AsyncHandlerExtensions.java +++ b/api/src/main/java/org/asynchttpclient/AsyncHandlerExtensions.java @@ -19,7 +19,6 @@ * * More additional hooks might come, such as: *