diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index b55e0465d..6a59bde6c 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -8,10 +8,10 @@ jobs: RunOnLinux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Grant Permission run: sudo chmod +x ./mvnw - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: 'corretto' java-version: '11' @@ -21,10 +21,10 @@ jobs: RunOnMacOs: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Grant Permission run: sudo chmod +x ./mvnw - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: 'corretto' java-version: '11' @@ -34,8 +34,8 @@ jobs: RunOnWindows: runs-on: windows-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: distribution: 'corretto' java-version: '11' diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 9b57efa7f..1f49d3112 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -20,10 +20,10 @@ jobs: RunOnLinux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Grant Permission run: sudo chmod +x ./mvnw - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: 'corretto' java-version: '11' @@ -33,10 +33,10 @@ jobs: RunOnMacOs: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Grant Permission run: sudo chmod +x ./mvnw - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: 'corretto' java-version: '11' @@ -46,8 +46,8 @@ jobs: RunOnWindows: runs-on: windows-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: distribution: 'corretto' java-version: '11' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3851c42e5..51dc38f90 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,12 +13,12 @@ jobs: Publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Grant Permission run: sudo chmod +x ./mvnw - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: 'corretto' java-version: '11' diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar index bf82ff01c..c1dd12f17 100644 Binary files a/.mvn/wrapper/maven-wrapper.jar and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index df0cd8a0f..4a95a1367 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -5,14 +5,14 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# +# +# http://www.apache.org/licenses/LICENSE-2.0 +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -distributionUrl=https://maven-central.storage-download.googleapis.com/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip -wrapperUrl=https://maven-central.storage-download.googleapis.com/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/LICENSE.txt b/LICENSE.txt index d8e4ed073..85a16d3d0 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ - Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/LICENSES/LICENSE.zstd-jni.txt b/LICENSES/LICENSE.zstd-jni.txt new file mode 100644 index 000000000..66abb8ae7 --- /dev/null +++ b/LICENSES/LICENSE.zstd-jni.txt @@ -0,0 +1,26 @@ +Zstd-jni: JNI bindings to Zstd Library + +Copyright (c) 2015-present, Luben Karavelov/ All rights reserved. + +BSD License + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 111dc9643..21a20ebbe 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Maven: org.asynchttpclient async-http-client - 3.0.0.Beta3 + 3.0.0 ``` @@ -28,24 +28,10 @@ Maven: Gradle: ```groovy dependencies { - implementation 'org.asynchttpclient:async-http-client:3.0.0.Beta3' + implementation 'org.asynchttpclient:async-http-client:3.0.0' } ``` -## Version - -AHC doesn't use SEMVER, and won't. - -* MAJOR = huge refactoring -* MINOR = new features and minor API changes, upgrading should require 1 hour of work to adapt sources -* FIX = no API change, just bug fixes, only those are source and binary compatible with same minor version - -Check CHANGES.md for migration path between versions. - -## Basics - -Feel free to check the [Javadoc](http://www.javadoc.io/doc/org.asynchttpclient/async-http-client/) or the code for more information. - ### Dsl Import the Dsl helpers to use convenient methods to bootstrap components: @@ -112,7 +98,7 @@ This body can be of type: * `String` * `java.nio.ByteBuffer` * `java.io.InputStream` -* `Publisher` +* `Publisher` * `org.asynchttpclient.request.body.generator.BodyGenerator` `BodyGenerator` is a generic abstraction that let you create request bodies on the fly. @@ -244,75 +230,34 @@ Async Http Client also supports WebSocket. You need to pass a `WebSocketUpgradeHandler` where you would register a `WebSocketListener`. ```java -WebSocket websocket=c.prepareGet("ws://demos.kaazing.com/echo") +WebSocket websocket = c.prepareGet("ws://demos.kaazing.com/echo") .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener( - new WebSocketListener(){ - - @Override - public void onOpen(WebSocket websocket){ - websocket.sendTextFrame("...").sendTextFrame("..."); - } - - @Override - public void onClose(WebSocket websocket) { - // ... - } - - @Override - public void onTextFrame(String payload,boolean finalFragment,int rsv){ - System.out.println(payload); - } - - @Override - public void onError(Throwable t){ - t.printStackTrace(); - } - }).build()).get(); + new WebSocketListener() { + + @Override + public void onOpen(WebSocket websocket) { + websocket.sendTextFrame("...").sendTextFrame("..."); + } + + @Override + public void onClose(WebSocket websocket) { + // ... + } + + @Override + public void onTextFrame(String payload, boolean finalFragment, int rsv) { + System.out.println(payload); + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + } + }).build()).get(); ``` -## WebDAV - -AsyncHttpClient has build in support for the WebDAV protocol. -The API can be used the same way normal HTTP request are made: - -```java - Request mkcolRequest=new RequestBuilder("MKCOL").setUrl("http://host:port/folder1").build(); - Response response=c.executeRequest(mkcolRequest).get(); -``` - -or - -```java - Request propFindRequest=new RequestBuilder("PROPFIND").setUrl("http://host:port").build(); - Response response=c.executeRequest(propFindRequest,new AsyncHandler() { - // ... - }).get(); -``` - -## More - -You can find more information on Jean-François Arcand's blog. Jean-François is the original author of this library. -Code is sometimes not up-to-date but gives a pretty good idea of advanced features. - -* http://web.archive.org/web/20111224171448/http://jfarcand.wordpress.com/2011/01/12/going-asynchronous-using-asynchttpclient-for-dummies/ -* http://web.archive.org/web/20111224171241/http://jfarcand.wordpress.com/2010/12/21/going-asynchronous-using-asynchttpclient-the-basic/ -* http://web.archive.org/web/20111224162752/http://jfarcand.wordpress.com/2011/01/04/going-asynchronous-using-asynchttpclient-the-complex/ -* http://web.archive.org/web/20120218183108/http://jfarcand.wordpress.com/2011/12/21/writing-websocket-clients-using-asynchttpclient/ - ## User Group Keep up to date on the library development by joining the Asynchronous HTTP Client discussion group [GitHub Discussions](https://github.com/AsyncHttpClient/async-http-client/discussions) - -## Contributing - -Of course, Pull Requests are welcome. - -Here are the few rules we'd like you to respect if you do so: - -* Only edit the code related to the suggested change, so DON'T automatically format the classes you've edited. -* Use IntelliJ default formatting rules. -* Regarding licensing: - * You must be the original author of the code you suggest. - * You must give the copyright to "the AsyncHttpClient Project" diff --git a/client/pom.xml b/client/pom.xml index f3b042da8..7530b50f1 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -19,7 +19,7 @@ org.asynchttpclient async-http-client-project - 3.0.0.Beta3 + 3.0.0 4.0.0 @@ -31,13 +31,21 @@ org.asynchttpclient.client 11.0.16 - 10.1.13 + 10.1.25 2.11.0 4.11.0 2.2 2.0.2 + + + hyperxpro + Aayush Atharva + aayush@shieldblaze.com + + + @@ -79,7 +87,6 @@ org.junit.jupiter junit-jupiter-api - 5.9.0 test @@ -87,7 +94,6 @@ org.junit.jupiter junit-jupiter-engine - 5.9.0 test @@ -95,7 +101,12 @@ org.junit.jupiter junit-jupiter-params - 5.9.0 + test + + + + io.github.nettyplus + netty-leak-detector-junit-extension test diff --git a/client/src/main/java/org/asynchttpclient/AsyncHandler.java b/client/src/main/java/org/asynchttpclient/AsyncHandler.java index a912ad9c5..22451fe09 100644 --- a/client/src/main/java/org/asynchttpclient/AsyncHandler.java +++ b/client/src/main/java/org/asynchttpclient/AsyncHandler.java @@ -116,7 +116,8 @@ default State onTrailingHeadersReceived(HttpHeaders headers) throws Exception { * @return T Value that will be returned by the associated {@link Future} * @throws Exception if something wrong happens */ - @Nullable T onCompleted() throws Exception; + @Nullable + T onCompleted() throws Exception; /** * Notify the callback before hostname resolution diff --git a/client/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java b/client/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java index 484cd0029..12dc93d7d 100644 --- a/client/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java +++ b/client/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java @@ -160,7 +160,8 @@ public interface AsyncHttpClientConfig { * @return the {@link ThreadFactory} an {@link AsyncHttpClient} use for handling asynchronous response. If no {@link ThreadFactory} has been explicitly * provided, this method will return {@code null} */ - @Nullable ThreadFactory getThreadFactory(); + @Nullable + ThreadFactory getThreadFactory(); /** * An instance of {@link ProxyServer} used by an {@link AsyncHttpClient} @@ -174,14 +175,16 @@ public interface AsyncHttpClientConfig { * * @return an instance of {@link SslContext} used for SSL connection. */ - @Nullable SslContext getSslContext(); + @Nullable + SslContext getSslContext(); /** * Return the current {@link Realm} * * @return the current {@link Realm} */ - @Nullable Realm getRealm(); + @Nullable + Realm getRealm(); /** * Return the list of {@link RequestFilter} @@ -260,12 +263,14 @@ public interface AsyncHttpClientConfig { /** * @return the array of enabled protocols */ - @Nullable String[] getEnabledProtocols(); + @Nullable + String[] getEnabledProtocols(); /** * @return the array of enabled cipher suites */ - @Nullable String[] getEnabledCipherSuites(); + @Nullable + String[] getEnabledCipherSuites(); /** * @return if insecure cipher suites must be filtered out (only used when not explicitly passing enabled cipher suites) @@ -294,7 +299,8 @@ public interface AsyncHttpClientConfig { int getHandshakeTimeout(); - @Nullable SslEngineFactory getSslEngineFactory(); + @Nullable + SslEngineFactory getSslEngineFactory(); int getChunkedFileChunkSize(); @@ -310,23 +316,29 @@ public interface AsyncHttpClientConfig { Map, Object> getChannelOptions(); - @Nullable EventLoopGroup getEventLoopGroup(); + @Nullable + EventLoopGroup getEventLoopGroup(); boolean isUseNativeTransport(); boolean isUseOnlyEpollNativeTransport(); - @Nullable Consumer getHttpAdditionalChannelInitializer(); + @Nullable + Consumer getHttpAdditionalChannelInitializer(); - @Nullable Consumer getWsAdditionalChannelInitializer(); + @Nullable + Consumer getWsAdditionalChannelInitializer(); ResponseBodyPartFactory getResponseBodyPartFactory(); - @Nullable ChannelPool getChannelPool(); + @Nullable + ChannelPool getChannelPool(); - @Nullable ConnectionSemaphoreFactory getConnectionSemaphoreFactory(); + @Nullable + ConnectionSemaphoreFactory getConnectionSemaphoreFactory(); - @Nullable Timer getNettyTimer(); + @Nullable + Timer getNettyTimer(); /** * @return the duration between tick of {@link HashedWheelTimer} @@ -358,7 +370,8 @@ public interface AsyncHttpClientConfig { int getSoRcvBuf(); - @Nullable ByteBufAllocator getAllocator(); + @Nullable + ByteBufAllocator getAllocator(); int getIoThreadsCount(); diff --git a/client/src/main/java/org/asynchttpclient/BoundRequestBuilder.java b/client/src/main/java/org/asynchttpclient/BoundRequestBuilder.java index c2e2bce29..99b3cc5d0 100644 --- a/client/src/main/java/org/asynchttpclient/BoundRequestBuilder.java +++ b/client/src/main/java/org/asynchttpclient/BoundRequestBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/ClientStats.java b/client/src/main/java/org/asynchttpclient/ClientStats.java index 7d450ad96..eef529221 100644 --- a/client/src/main/java/org/asynchttpclient/ClientStats.java +++ b/client/src/main/java/org/asynchttpclient/ClientStats.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java index fb5dad6ff..1f616c328 100644 --- a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java +++ b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java @@ -24,8 +24,8 @@ import org.asynchttpclient.channel.ChannelPool; import org.asynchttpclient.cookie.CookieEvictionTask; import org.asynchttpclient.cookie.CookieStore; -import org.asynchttpclient.filter.FilterContext; import org.asynchttpclient.exception.FilterException; +import org.asynchttpclient.filter.FilterContext; import org.asynchttpclient.filter.RequestFilter; import org.asynchttpclient.handler.resumable.ResumableAsyncHandler; import org.asynchttpclient.netty.channel.ChannelManager; diff --git a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java index 0357592bd..e72235c17 100644 --- a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java +++ b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java @@ -890,6 +890,7 @@ public Builder(AsyncHttpClientConfig config) { disableZeroCopy = config.isDisableZeroCopy(); keepEncodingHeader = config.isKeepEncodingHeader(); proxyServerSelector = config.getProxyServerSelector(); + validateResponseHeaders = config.isValidateResponseHeaders(); // websocket aggregateWebSocketFrameFragments = config.isAggregateWebSocketFrameFragments(); @@ -907,15 +908,19 @@ public Builder(AsyncHttpClientConfig config) { // keep-alive keepAlive = config.isKeepAlive(); pooledConnectionIdleTimeout = config.getPooledConnectionIdleTimeout(); + connectionPoolCleanerPeriod = config.getConnectionPoolCleanerPeriod(); connectionTtl = config.getConnectionTtl(); maxConnections = config.getMaxConnections(); maxConnectionsPerHost = config.getMaxConnectionsPerHost(); channelPool = config.getChannelPool(); connectionSemaphoreFactory = config.getConnectionSemaphoreFactory(); keepAliveStrategy = config.getKeepAliveStrategy(); + acquireFreeChannelTimeout = config.getAcquireFreeChannelTimeout(); // ssl + useOpenSsl = config.isUseOpenSsl(); useInsecureTrustManager = config.isUseInsecureTrustManager(); + disableHttpsEndpointIdentificationAlgorithm = config.isDisableHttpsEndpointIdentificationAlgorithm(); handshakeTimeout = config.getHandshakeTimeout(); enabledProtocols = config.getEnabledProtocols(); enabledCipherSuites = config.getEnabledCipherSuites(); @@ -930,6 +935,10 @@ public Builder(AsyncHttpClientConfig config) { responseFilters.addAll(config.getResponseFilters()); ioExceptionFilters.addAll(config.getIoExceptionFilters()); + // cookie store + cookieStore = config.getCookieStore(); + expiredCookieEvictionDelay = config.expiredCookieEvictionDelay(); + // tuning tcpNoDelay = config.isTcpNoDelay(); soReuseAddress = config.isSoReuseAddress(); @@ -943,6 +952,7 @@ public Builder(AsyncHttpClientConfig config) { httpClientCodecMaxInitialLineLength = config.getHttpClientCodecMaxInitialLineLength(); httpClientCodecMaxHeaderSize = config.getHttpClientCodecMaxHeaderSize(); httpClientCodecMaxChunkSize = config.getHttpClientCodecMaxChunkSize(); + httpClientCodecInitialBufferSize = config.getHttpClientCodecInitialBufferSize(); chunkedFileChunkSize = config.getChunkedFileChunkSize(); channelOptions.putAll(config.getChannelOptions()); eventLoopGroup = config.getEventLoopGroup(); diff --git a/client/src/main/java/org/asynchttpclient/DefaultRequest.java b/client/src/main/java/org/asynchttpclient/DefaultRequest.java index 4170c33e2..09c615d2a 100644 --- a/client/src/main/java/org/asynchttpclient/DefaultRequest.java +++ b/client/src/main/java/org/asynchttpclient/DefaultRequest.java @@ -15,6 +15,7 @@ */ package org.asynchttpclient; +import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.cookie.Cookie; import io.netty.resolver.NameResolver; @@ -51,6 +52,7 @@ public class DefaultRequest implements Request { private final @Nullable List compositeByteData; private final @Nullable String stringData; private final @Nullable ByteBuffer byteBufferData; + private final @Nullable ByteBuf byteBufData; private final @Nullable InputStream streamData; private final @Nullable BodyGenerator bodyGenerator; private final List formParams; @@ -79,6 +81,7 @@ public DefaultRequest(String method, @Nullable List compositeByteData, @Nullable String stringData, @Nullable ByteBuffer byteBufferData, + @Nullable ByteBuf byteBufData, @Nullable InputStream streamData, @Nullable BodyGenerator bodyGenerator, List formParams, @@ -104,6 +107,7 @@ public DefaultRequest(String method, this.compositeByteData = compositeByteData; this.stringData = stringData; this.byteBufferData = byteBufferData; + this.byteBufData = byteBufData; this.streamData = streamData; this.bodyGenerator = bodyGenerator; this.formParams = formParams; @@ -176,6 +180,11 @@ public List getCookies() { return byteBufferData; } + @Override + public @Nullable ByteBuf getByteBufData() { + return byteBufData; + } + @Override public @Nullable InputStream getStreamData() { return streamData; diff --git a/client/src/main/java/org/asynchttpclient/HostStats.java b/client/src/main/java/org/asynchttpclient/HostStats.java index 9ec52805a..3470ea4e1 100644 --- a/client/src/main/java/org/asynchttpclient/HostStats.java +++ b/client/src/main/java/org/asynchttpclient/HostStats.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/HttpResponseBodyPart.java b/client/src/main/java/org/asynchttpclient/HttpResponseBodyPart.java index 0be4dedb5..0df78f7b2 100644 --- a/client/src/main/java/org/asynchttpclient/HttpResponseBodyPart.java +++ b/client/src/main/java/org/asynchttpclient/HttpResponseBodyPart.java @@ -15,6 +15,8 @@ */ package org.asynchttpclient; +import io.netty.buffer.ByteBuf; + import java.nio.ByteBuffer; /** @@ -44,6 +46,12 @@ protected HttpResponseBodyPart(boolean last) { */ public abstract ByteBuffer getBodyByteBuffer(); + /** + * @return the {@link ByteBuf} of the bytes read from the response's chunk. + * The {@link ByteBuf}'s capacity is equal to the number of bytes available. + */ + public abstract ByteBuf getBodyByteBuf(); + /** * @return true if this is the last part. */ diff --git a/client/src/main/java/org/asynchttpclient/Param.java b/client/src/main/java/org/asynchttpclient/Param.java index cbc35e196..4f7a5530a 100644 --- a/client/src/main/java/org/asynchttpclient/Param.java +++ b/client/src/main/java/org/asynchttpclient/Param.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/Request.java b/client/src/main/java/org/asynchttpclient/Request.java index 64977cf71..1d95016b3 100644 --- a/client/src/main/java/org/asynchttpclient/Request.java +++ b/client/src/main/java/org/asynchttpclient/Request.java @@ -16,6 +16,7 @@ */ package org.asynchttpclient; +import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.cookie.Cookie; import io.netty.resolver.NameResolver; @@ -66,12 +67,14 @@ public interface Request { /** * @return the InetAddress to be used to bypass uri's hostname resolution */ - @Nullable InetAddress getAddress(); + @Nullable + InetAddress getAddress(); /** * @return the local address to bind from */ - @Nullable InetAddress getLocalAddress(); + @Nullable + InetAddress getLocalAddress(); /** * @return the HTTP headers @@ -91,27 +94,38 @@ public interface Request { /** * @return the request's body array of byte arrays (only non-null if it was set this way) */ - @Nullable List getCompositeByteData(); + @Nullable + List getCompositeByteData(); /** * @return the request's body string (only non-null if it was set this way) */ - @Nullable String getStringData(); + @Nullable + String getStringData(); /** * @return the request's body ByteBuffer (only non-null if it was set this way) */ - @Nullable ByteBuffer getByteBufferData(); + @Nullable + ByteBuffer getByteBufferData(); + + /** + * @return the request's body ByteBuf (only non-null if it was set this way) + */ + @Nullable + ByteBuf getByteBufData(); /** * @return the request's body InputStream (only non-null if it was set this way) */ - @Nullable InputStream getStreamData(); + @Nullable + InputStream getStreamData(); /** * @return the request's body BodyGenerator (only non-null if it was set this way) */ - @Nullable BodyGenerator getBodyGenerator(); + @Nullable + BodyGenerator getBodyGenerator(); /** * @return the request's form parameters @@ -126,7 +140,8 @@ public interface Request { /** * @return the virtual host to connect to */ - @Nullable String getVirtualHost(); + @Nullable + String getVirtualHost(); /** * @return the query params resolved from the url/uri @@ -136,22 +151,26 @@ public interface Request { /** * @return the proxy server to be used to perform this request (overrides the one defined in config) */ - @Nullable ProxyServer getProxyServer(); + @Nullable + ProxyServer getProxyServer(); /** * @return the realm to be used to perform this request (overrides the one defined in config) */ - @Nullable Realm getRealm(); + @Nullable + Realm getRealm(); /** * @return the file to be uploaded */ - @Nullable File getFile(); + @Nullable + File getFile(); /** * @return if this request is to follow redirects. Non null values means "override config value". */ - @Nullable Boolean getFollowRedirect(); + @Nullable + Boolean getFollowRedirect(); /** * @return the request timeout. Non zero values means "override config value". @@ -171,7 +190,8 @@ public interface Request { /** * @return the charset value used when decoding the request's body. */ - @Nullable Charset getCharset(); + @Nullable + Charset getCharset(); /** * @return the strategy to compute ChannelPool's keys diff --git a/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java b/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java index dbfd58f5b..9f5cf9e5e 100644 --- a/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java +++ b/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java @@ -15,6 +15,7 @@ */ package org.asynchttpclient; +import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.cookie.Cookie; @@ -76,6 +77,7 @@ public abstract class RequestBuilderBase> { protected @Nullable List compositeByteData; protected @Nullable String stringData; protected @Nullable ByteBuffer byteBufferData; + protected @Nullable ByteBuf byteBufData; protected @Nullable InputStream streamData; protected @Nullable BodyGenerator bodyGenerator; protected @Nullable List formParams; @@ -121,6 +123,7 @@ protected RequestBuilderBase(Request prototype, boolean disableUrlEncoding, bool compositeByteData = prototype.getCompositeByteData(); stringData = prototype.getStringData(); byteBufferData = prototype.getByteBufferData(); + byteBufData = prototype.getByteBufData(); streamData = prototype.getStreamData(); bodyGenerator = prototype.getBodyGenerator(); if (isNonEmpty(prototype.getFormParams())) { @@ -361,6 +364,7 @@ public void resetNonMultipartData() { byteData = null; compositeByteData = null; byteBufferData = null; + byteBufData = null; stringData = null; streamData = null; bodyGenerator = null; @@ -405,6 +409,12 @@ public T setBody(ByteBuffer data) { return asDerivedType(); } + public T setBody(ByteBuf data) { + resetBody(); + byteBufData = data; + return asDerivedType(); + } + public T setBody(InputStream stream) { resetBody(); streamData = stream; @@ -586,6 +596,7 @@ private RequestBuilderBase executeSignatureCalculator() { rb.compositeByteData = compositeByteData; rb.stringData = stringData; rb.byteBufferData = byteBufferData; + rb.byteBufData = byteBufData; rb.streamData = streamData; rb.bodyGenerator = bodyGenerator; rb.virtualHost = virtualHost; @@ -647,6 +658,7 @@ public Request build() { rb.compositeByteData, rb.stringData, rb.byteBufferData, + rb.byteBufData, rb.streamData, rb.bodyGenerator, formParamsCopy, diff --git a/client/src/main/java/org/asynchttpclient/Response.java b/client/src/main/java/org/asynchttpclient/Response.java index 8b9c9a6f1..220d989b0 100644 --- a/client/src/main/java/org/asynchttpclient/Response.java +++ b/client/src/main/java/org/asynchttpclient/Response.java @@ -16,6 +16,7 @@ */ package org.asynchttpclient; +import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.cookie.Cookie; import org.asynchttpclient.netty.NettyResponse; @@ -61,6 +62,13 @@ public interface Response { */ ByteBuffer getResponseBodyAsByteBuffer(); + /** + * Return the entire response body as a ByteBuf. + * + * @return the entire response body as a ByteBuf. + */ + ByteBuf getResponseBodyAsByteBuf(); + /** * Returns an input stream for the response body. Note that you should not try to get this more than once, and that you should not close the stream. * diff --git a/client/src/main/java/org/asynchttpclient/SslEngineFactory.java b/client/src/main/java/org/asynchttpclient/SslEngineFactory.java index d007106f7..15ec9748e 100644 --- a/client/src/main/java/org/asynchttpclient/SslEngineFactory.java +++ b/client/src/main/java/org/asynchttpclient/SslEngineFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java b/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java index 1b38f09a3..4f2bc3b9b 100755 --- a/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java +++ b/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,8 @@ public interface ChannelPool { * @param partitionKey the partition used when invoking offer * @return the channel associated with the uri */ - @Nullable Channel poll(Object partitionKey); + @Nullable + Channel poll(Object partitionKey); /** * Remove all channels from the cache. A channel might have been associated diff --git a/client/src/main/java/org/asynchttpclient/channel/ChannelPoolPartitioning.java b/client/src/main/java/org/asynchttpclient/channel/ChannelPoolPartitioning.java index fd9a51b23..324a4ce34 100644 --- a/client/src/main/java/org/asynchttpclient/channel/ChannelPoolPartitioning.java +++ b/client/src/main/java/org/asynchttpclient/channel/ChannelPoolPartitioning.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/channel/KeepAliveStrategy.java b/client/src/main/java/org/asynchttpclient/channel/KeepAliveStrategy.java index 106799ddb..e72cc8c13 100644 --- a/client/src/main/java/org/asynchttpclient/channel/KeepAliveStrategy.java +++ b/client/src/main/java/org/asynchttpclient/channel/KeepAliveStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java b/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java index 3a5b2e93d..ae3aab81a 100644 --- a/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java +++ b/client/src/main/java/org/asynchttpclient/channel/NoopChannelPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,6 @@ public enum NoopChannelPool implements ChannelPool { INSTANCE; /** - * * @return always false since this is a {@link NoopChannelPool} */ @Override @@ -39,7 +38,6 @@ public boolean offer(Channel channel, Object partitionKey) { } /** - * * @return always null since this is a {@link NoopChannelPool} */ @Override @@ -48,7 +46,6 @@ public boolean offer(Channel channel, Object partitionKey) { } /** - * * @return always false since this is a {@link NoopChannelPool} */ @Override @@ -57,7 +54,6 @@ public boolean removeAll(Channel channel) { } /** - * * @return always true since this is a {@link NoopChannelPool} */ @Override @@ -66,7 +62,6 @@ public boolean isOpen() { } /** - * * Does nothing since this is a {@link NoopChannelPool} */ @Override @@ -74,7 +69,6 @@ public void destroy() { } /** - * * Does nothing since this is a {@link NoopChannelPool} */ @Override @@ -82,7 +76,6 @@ public void flushPartitions(Predicate predicate) { } /** - * * @return always {@link Collections#emptyMap()} since this is a {@link NoopChannelPool} */ @Override diff --git a/client/src/main/java/org/asynchttpclient/config/AsyncHttpClientConfigDefaults.java b/client/src/main/java/org/asynchttpclient/config/AsyncHttpClientConfigDefaults.java index 3d4cb6106..3596c67a9 100644 --- a/client/src/main/java/org/asynchttpclient/config/AsyncHttpClientConfigDefaults.java +++ b/client/src/main/java/org/asynchttpclient/config/AsyncHttpClientConfigDefaults.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/cookie/ThreadSafeCookieStore.java b/client/src/main/java/org/asynchttpclient/cookie/ThreadSafeCookieStore.java index 35a620e31..5832185cc 100644 --- a/client/src/main/java/org/asynchttpclient/cookie/ThreadSafeCookieStore.java +++ b/client/src/main/java/org/asynchttpclient/cookie/ThreadSafeCookieStore.java @@ -229,7 +229,7 @@ private void removeExpired() { cookieJar.values() .forEach(cookieMap -> removed[0] |= cookieMap.entrySet() - .removeIf(v -> hasCookieExpired(v.getValue().cookie, v.getValue().createdAt))); + .removeIf(v -> hasCookieExpired(v.getValue().cookie, v.getValue().createdAt))); if (removed[0]) { cookieJar.entrySet().removeIf(entry -> entry.getValue() == null || entry.getValue().isEmpty()); diff --git a/client/src/main/java/org/asynchttpclient/handler/MaxRedirectException.java b/client/src/main/java/org/asynchttpclient/handler/MaxRedirectException.java index cf09bc742..993f87d93 100644 --- a/client/src/main/java/org/asynchttpclient/handler/MaxRedirectException.java +++ b/client/src/main/java/org/asynchttpclient/handler/MaxRedirectException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/DiscardEvent.java b/client/src/main/java/org/asynchttpclient/netty/DiscardEvent.java index b8ca2bd55..798312257 100644 --- a/client/src/main/java/org/asynchttpclient/netty/DiscardEvent.java +++ b/client/src/main/java/org/asynchttpclient/netty/DiscardEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/EagerResponseBodyPart.java b/client/src/main/java/org/asynchttpclient/netty/EagerResponseBodyPart.java index 44045e80b..8247379b0 100755 --- a/client/src/main/java/org/asynchttpclient/netty/EagerResponseBodyPart.java +++ b/client/src/main/java/org/asynchttpclient/netty/EagerResponseBodyPart.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; import org.asynchttpclient.HttpResponseBodyPart; import java.nio.ByteBuffer; @@ -53,4 +54,9 @@ public int length() { public ByteBuffer getBodyByteBuffer() { return ByteBuffer.wrap(bytes); } + + @Override + public ByteBuf getBodyByteBuf() { + return Unpooled.wrappedBuffer(bytes); + } } diff --git a/client/src/main/java/org/asynchttpclient/netty/LazyResponseBodyPart.java b/client/src/main/java/org/asynchttpclient/netty/LazyResponseBodyPart.java index 3732669d9..e47277006 100755 --- a/client/src/main/java/org/asynchttpclient/netty/LazyResponseBodyPart.java +++ b/client/src/main/java/org/asynchttpclient/netty/LazyResponseBodyPart.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,8 @@ public LazyResponseBodyPart(ByteBuf buf, boolean last) { this.buf = buf; } - public ByteBuf getBuf() { + @Override + public ByteBuf getBodyByteBuf() { return buf; } diff --git a/client/src/main/java/org/asynchttpclient/netty/NettyResponse.java b/client/src/main/java/org/asynchttpclient/netty/NettyResponse.java index 179d796c1..61fb15161 100755 --- a/client/src/main/java/org/asynchttpclient/netty/NettyResponse.java +++ b/client/src/main/java/org/asynchttpclient/netty/NettyResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,9 @@ */ package org.asynchttpclient.netty; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.CompositeByteBuf; import io.netty.handler.codec.http.EmptyHttpHeaders; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.cookie.ClientCookieDecoder; @@ -192,6 +195,15 @@ public ByteBuffer getResponseBodyAsByteBuffer() { return target; } + @Override + public ByteBuf getResponseBodyAsByteBuf() { + CompositeByteBuf compositeByteBuf = ByteBufAllocator.DEFAULT.compositeBuffer(bodyParts.size()); + for (HttpResponseBodyPart part : bodyParts) { + compositeByteBuf.addComponent(true, part.getBodyByteBuf()); + } + return compositeByteBuf; + } + @Override public String getResponseBody() { return getResponseBody(withDefault(extractContentTypeCharsetAttribute(getContentType()), UTF_8)); diff --git a/client/src/main/java/org/asynchttpclient/netty/NettyResponseFuture.java b/client/src/main/java/org/asynchttpclient/netty/NettyResponseFuture.java index f66c9b9d7..c5e4a97d0 100755 --- a/client/src/main/java/org/asynchttpclient/netty/NettyResponseFuture.java +++ b/client/src/main/java/org/asynchttpclient/netty/NettyResponseFuture.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/NettyResponseStatus.java b/client/src/main/java/org/asynchttpclient/netty/NettyResponseStatus.java index 49af6b160..567432af3 100755 --- a/client/src/main/java/org/asynchttpclient/netty/NettyResponseStatus.java +++ b/client/src/main/java/org/asynchttpclient/netty/NettyResponseStatus.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/OnLastHttpContentCallback.java b/client/src/main/java/org/asynchttpclient/netty/OnLastHttpContentCallback.java index e72a5c2be..15c0c9617 100644 --- a/client/src/main/java/org/asynchttpclient/netty/OnLastHttpContentCallback.java +++ b/client/src/main/java/org/asynchttpclient/netty/OnLastHttpContentCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java b/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java index fe85734c7..c5c94c551 100755 --- a/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java +++ b/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/channel/Channels.java b/client/src/main/java/org/asynchttpclient/netty/channel/Channels.java index e0d4acede..c56a05ba5 100755 --- a/client/src/main/java/org/asynchttpclient/netty/channel/Channels.java +++ b/client/src/main/java/org/asynchttpclient/netty/channel/Channels.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java b/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java index 0ae2f68f7..c4042fdfc 100755 --- a/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java +++ b/client/src/main/java/org/asynchttpclient/netty/channel/DefaultChannelPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/channel/NettyConnectListener.java b/client/src/main/java/org/asynchttpclient/netty/channel/NettyConnectListener.java index 86d48e451..719733f8a 100755 --- a/client/src/main/java/org/asynchttpclient/netty/channel/NettyConnectListener.java +++ b/client/src/main/java/org/asynchttpclient/netty/channel/NettyConnectListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/future/StackTraceInspector.java b/client/src/main/java/org/asynchttpclient/netty/future/StackTraceInspector.java index 919bd3cfa..28a0f359d 100755 --- a/client/src/main/java/org/asynchttpclient/netty/future/StackTraceInspector.java +++ b/client/src/main/java/org/asynchttpclient/netty/future/StackTraceInspector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/AsyncHttpClientHandler.java b/client/src/main/java/org/asynchttpclient/netty/handler/AsyncHttpClientHandler.java index 71d48f408..aeecbef55 100755 --- a/client/src/main/java/org/asynchttpclient/netty/handler/AsyncHttpClientHandler.java +++ b/client/src/main/java/org/asynchttpclient/netty/handler/AsyncHttpClientHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/HttpHandler.java b/client/src/main/java/org/asynchttpclient/netty/handler/HttpHandler.java index 5990cac42..06ec46a2b 100755 --- a/client/src/main/java/org/asynchttpclient/netty/handler/HttpHandler.java +++ b/client/src/main/java/org/asynchttpclient/netty/handler/HttpHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/WebSocketHandler.java b/client/src/main/java/org/asynchttpclient/netty/handler/WebSocketHandler.java index 9d26c49b2..1cf19d0ef 100755 --- a/client/src/main/java/org/asynchttpclient/netty/handler/WebSocketHandler.java +++ b/client/src/main/java/org/asynchttpclient/netty/handler/WebSocketHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/ResponseFiltersInterceptor.java b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/ResponseFiltersInterceptor.java index 19b41f502..5f905d94f 100644 --- a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/ResponseFiltersInterceptor.java +++ b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/ResponseFiltersInterceptor.java @@ -20,8 +20,8 @@ import org.asynchttpclient.AsyncHandler; import org.asynchttpclient.AsyncHttpClientConfig; import org.asynchttpclient.HttpResponseStatus; -import org.asynchttpclient.filter.FilterContext; import org.asynchttpclient.exception.FilterException; +import org.asynchttpclient.filter.FilterContext; import org.asynchttpclient.filter.ResponseFilter; import org.asynchttpclient.netty.NettyResponseFuture; import org.asynchttpclient.netty.request.NettyRequestSender; diff --git a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequest.java b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequest.java index 785ffad6a..71cc658a0 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequest.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java index 317c4522f..67d9a67be 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.handler.codec.compression.Brotli; +import io.netty.handler.codec.compression.Zstd; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.HttpHeaderValues; @@ -31,6 +33,7 @@ import org.asynchttpclient.netty.request.body.NettyBody; import org.asynchttpclient.netty.request.body.NettyBodyBody; import org.asynchttpclient.netty.request.body.NettyByteArrayBody; +import org.asynchttpclient.netty.request.body.NettyByteBufBody; import org.asynchttpclient.netty.request.body.NettyByteBufferBody; import org.asynchttpclient.netty.request.body.NettyCompositeByteArrayBody; import org.asynchttpclient.netty.request.body.NettyDirectBody; @@ -65,6 +68,7 @@ import static org.asynchttpclient.util.HttpUtils.ACCEPT_ALL_HEADER_VALUE; import static org.asynchttpclient.util.HttpUtils.GZIP_DEFLATE; import static org.asynchttpclient.util.HttpUtils.filterOutBrotliFromAcceptEncoding; +import static org.asynchttpclient.util.HttpUtils.filterOutZstdFromAcceptEncoding; import static org.asynchttpclient.util.HttpUtils.hostHeader; import static org.asynchttpclient.util.HttpUtils.originHeader; import static org.asynchttpclient.util.HttpUtils.urlEncodeFormParams; @@ -95,6 +99,8 @@ private NettyBody body(Request request) { nettyBody = new NettyByteBufferBody(StringUtils.charSequence2ByteBuffer(request.getStringData(), bodyCharset)); } else if (request.getByteBufferData() != null) { nettyBody = new NettyByteBufferBody(request.getByteBufferData()); + } else if (request.getByteBufData() != null) { + nettyBody = new NettyByteBufBody(request.getByteBufData()); } else if (request.getStreamData() != null) { nettyBody = new NettyInputStreamBody(request.getStreamData()); } else if (isNonEmpty(request.getFormParams())) { @@ -173,13 +179,26 @@ public NettyRequest newNettyRequest(Request request, boolean performConnectReque String userDefinedAcceptEncoding = headers.get(ACCEPT_ENCODING); if (userDefinedAcceptEncoding != null) { if (config.isEnableAutomaticDecompression()) { - // we don't support Brotli ATM, for automatic decompression. - // For manual decompression by user, any encoding may suite, so leave untouched - headers.set(ACCEPT_ENCODING, filterOutBrotliFromAcceptEncoding(userDefinedAcceptEncoding)); + if (!Brotli.isAvailable()) { + // Brotli is not available. + // For manual decompression by user, any encoding may suite, so leave untouched + headers.set(ACCEPT_ENCODING, filterOutBrotliFromAcceptEncoding(userDefinedAcceptEncoding)); + } + if (!Zstd.isAvailable()) { + // zstd is not available. + // For manual decompression by user, any encoding may suit, so leave untouched + headers.set(ACCEPT_ENCODING, filterOutZstdFromAcceptEncoding(userDefinedAcceptEncoding)); + } } } else if (config.isCompressionEnforced()) { // Add Accept Encoding header if compression is enforced headers.set(ACCEPT_ENCODING, GZIP_DEFLATE); + if (Brotli.isAvailable()) { + headers.add(ACCEPT_ENCODING, HttpHeaderValues.BR); + } + if (Zstd.isAvailable()) { + headers.add(ACCEPT_ENCODING, HttpHeaderValues.ZSTD); + } } } diff --git a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java index 2c0314325..9fff868b2 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,10 +36,10 @@ import org.asynchttpclient.Realm; import org.asynchttpclient.Realm.AuthScheme; import org.asynchttpclient.Request; +import org.asynchttpclient.exception.FilterException; import org.asynchttpclient.exception.PoolAlreadyClosedException; import org.asynchttpclient.exception.RemotelyClosedException; import org.asynchttpclient.filter.FilterContext; -import org.asynchttpclient.exception.FilterException; import org.asynchttpclient.filter.IOExceptionFilter; import org.asynchttpclient.handler.TransferCompletionHandler; import org.asynchttpclient.netty.NettyResponseFuture; diff --git a/client/src/main/java/org/asynchttpclient/netty/request/WriteProgressListener.java b/client/src/main/java/org/asynchttpclient/netty/request/WriteProgressListener.java index 4a6c330d0..98f669eae 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/WriteProgressListener.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/WriteProgressListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/request/body/BodyChunkedInput.java b/client/src/main/java/org/asynchttpclient/netty/request/body/BodyChunkedInput.java index cd0434146..772baca43 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/body/BodyChunkedInput.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/body/BodyChunkedInput.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/request/body/BodyFileRegion.java b/client/src/main/java/org/asynchttpclient/netty/request/body/BodyFileRegion.java index 9326d717d..91f2b1ecf 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/body/BodyFileRegion.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/body/BodyFileRegion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyBody.java b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyBody.java index 2dce64353..f38ef3939 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyBody.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyBody.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyBodyBody.java b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyBodyBody.java index 610069a01..efe337bfe 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyBodyBody.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyBodyBody.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyByteArrayBody.java b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyByteArrayBody.java index 5714d1396..b794ab6e9 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyByteArrayBody.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyByteArrayBody.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyByteBufBody.java b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyByteBufBody.java new file mode 100644 index 000000000..d236cdade --- /dev/null +++ b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyByteBufBody.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015-2023 AsyncHttpClient Project. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asynchttpclient.netty.request.body; + +import io.netty.buffer.ByteBuf; + +public class NettyByteBufBody extends NettyDirectBody { + + private final ByteBuf bb; + private final CharSequence contentTypeOverride; + private final long length; + + public NettyByteBufBody(ByteBuf bb) { + this(bb, null); + } + + public NettyByteBufBody(ByteBuf bb, CharSequence contentTypeOverride) { + this.bb = bb; + length = bb.readableBytes(); + this.contentTypeOverride = contentTypeOverride; + } + + @Override + public long getContentLength() { + return length; + } + + @Override + public CharSequence getContentTypeOverride() { + return contentTypeOverride; + } + + @Override + public ByteBuf byteBuf() { + return bb; + } +} diff --git a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyCompositeByteArrayBody.java b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyCompositeByteArrayBody.java index 74bd09c4f..e852528fd 100644 --- a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyCompositeByteArrayBody.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyCompositeByteArrayBody.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyDirectBody.java b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyDirectBody.java index 7f8dc69f8..55dfcb8bc 100644 --- a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyDirectBody.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyDirectBody.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyFileBody.java b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyFileBody.java index e83b7b8cd..a3c40322d 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyFileBody.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyFileBody.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyInputStreamBody.java b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyInputStreamBody.java index 0096cc772..4dba9d951 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyInputStreamBody.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyInputStreamBody.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyMultipartBody.java b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyMultipartBody.java index fe889e383..7fa23c07f 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/body/NettyMultipartBody.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/body/NettyMultipartBody.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/timeout/ReadTimeoutTimerTask.java b/client/src/main/java/org/asynchttpclient/netty/timeout/ReadTimeoutTimerTask.java index 213b539a9..8b0d4373a 100755 --- a/client/src/main/java/org/asynchttpclient/netty/timeout/ReadTimeoutTimerTask.java +++ b/client/src/main/java/org/asynchttpclient/netty/timeout/ReadTimeoutTimerTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/timeout/RequestTimeoutTimerTask.java b/client/src/main/java/org/asynchttpclient/netty/timeout/RequestTimeoutTimerTask.java index 5a72a6b70..74c5d0197 100755 --- a/client/src/main/java/org/asynchttpclient/netty/timeout/RequestTimeoutTimerTask.java +++ b/client/src/main/java/org/asynchttpclient/netty/timeout/RequestTimeoutTimerTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/timeout/TimeoutTimerTask.java b/client/src/main/java/org/asynchttpclient/netty/timeout/TimeoutTimerTask.java index 67b31c19b..3c9a3675e 100755 --- a/client/src/main/java/org/asynchttpclient/netty/timeout/TimeoutTimerTask.java +++ b/client/src/main/java/org/asynchttpclient/netty/timeout/TimeoutTimerTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/timeout/TimeoutsHolder.java b/client/src/main/java/org/asynchttpclient/netty/timeout/TimeoutsHolder.java index 6c2a55ff2..acce84b6d 100755 --- a/client/src/main/java/org/asynchttpclient/netty/timeout/TimeoutsHolder.java +++ b/client/src/main/java/org/asynchttpclient/netty/timeout/TimeoutsHolder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/netty/ws/NettyWebSocket.java b/client/src/main/java/org/asynchttpclient/netty/ws/NettyWebSocket.java index 35847dc78..2329edacf 100755 --- a/client/src/main/java/org/asynchttpclient/netty/ws/NettyWebSocket.java +++ b/client/src/main/java/org/asynchttpclient/netty/ws/NettyWebSocket.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/ntlm/NtlmEngine.java b/client/src/main/java/org/asynchttpclient/ntlm/NtlmEngine.java index ce7624d2b..c2338c46a 100644 --- a/client/src/main/java/org/asynchttpclient/ntlm/NtlmEngine.java +++ b/client/src/main/java/org/asynchttpclient/ntlm/NtlmEngine.java @@ -1083,7 +1083,8 @@ byte[] getChallenge() { /** * Retrieve the target */ - @Nullable String getTarget() { + @Nullable + String getTarget() { return target; } diff --git a/client/src/main/java/org/asynchttpclient/proxy/ProxyServerSelector.java b/client/src/main/java/org/asynchttpclient/proxy/ProxyServerSelector.java index 46391c804..048f2e78e 100644 --- a/client/src/main/java/org/asynchttpclient/proxy/ProxyServerSelector.java +++ b/client/src/main/java/org/asynchttpclient/proxy/ProxyServerSelector.java @@ -35,5 +35,6 @@ public interface ProxyServerSelector { * @param uri The URI to select a proxy server for. * @return The proxy server to use, if any. May return null. */ - @Nullable ProxyServer select(Uri uri); + @Nullable + ProxyServer select(Uri uri); } diff --git a/client/src/main/java/org/asynchttpclient/request/body/generator/FeedableBodyGenerator.java b/client/src/main/java/org/asynchttpclient/request/body/generator/FeedableBodyGenerator.java index dae8acef8..1aa27f0ac 100644 --- a/client/src/main/java/org/asynchttpclient/request/body/generator/FeedableBodyGenerator.java +++ b/client/src/main/java/org/asynchttpclient/request/body/generator/FeedableBodyGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/request/body/generator/UnboundedQueueFeedableBodyGenerator.java b/client/src/main/java/org/asynchttpclient/request/body/generator/UnboundedQueueFeedableBodyGenerator.java index bc72650a7..f55dcbe37 100755 --- a/client/src/main/java/org/asynchttpclient/request/body/generator/UnboundedQueueFeedableBodyGenerator.java +++ b/client/src/main/java/org/asynchttpclient/request/body/generator/UnboundedQueueFeedableBodyGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/request/body/multipart/ByteArrayPart.java b/client/src/main/java/org/asynchttpclient/request/body/multipart/ByteArrayPart.java index 97ed6cc61..203d37a2c 100644 --- a/client/src/main/java/org/asynchttpclient/request/body/multipart/ByteArrayPart.java +++ b/client/src/main/java/org/asynchttpclient/request/body/multipart/ByteArrayPart.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/request/body/multipart/FileLikePart.java b/client/src/main/java/org/asynchttpclient/request/body/multipart/FileLikePart.java index 3370eb761..1d03ad22b 100644 --- a/client/src/main/java/org/asynchttpclient/request/body/multipart/FileLikePart.java +++ b/client/src/main/java/org/asynchttpclient/request/body/multipart/FileLikePart.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/request/body/multipart/FilePart.java b/client/src/main/java/org/asynchttpclient/request/body/multipart/FilePart.java index 777489fd4..a156dd077 100644 --- a/client/src/main/java/org/asynchttpclient/request/body/multipart/FilePart.java +++ b/client/src/main/java/org/asynchttpclient/request/body/multipart/FilePart.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/request/body/multipart/Part.java b/client/src/main/java/org/asynchttpclient/request/body/multipart/Part.java index 9dae23f6a..19266eb25 100644 --- a/client/src/main/java/org/asynchttpclient/request/body/multipart/Part.java +++ b/client/src/main/java/org/asynchttpclient/request/body/multipart/Part.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/request/body/multipart/PartBase.java b/client/src/main/java/org/asynchttpclient/request/body/multipart/PartBase.java index 2dff615e4..65e78286f 100644 --- a/client/src/main/java/org/asynchttpclient/request/body/multipart/PartBase.java +++ b/client/src/main/java/org/asynchttpclient/request/body/multipart/PartBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/request/body/multipart/StringPart.java b/client/src/main/java/org/asynchttpclient/request/body/multipart/StringPart.java index 2427913ea..05c958d35 100644 --- a/client/src/main/java/org/asynchttpclient/request/body/multipart/StringPart.java +++ b/client/src/main/java/org/asynchttpclient/request/body/multipart/StringPart.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/request/body/multipart/part/PartVisitor.java b/client/src/main/java/org/asynchttpclient/request/body/multipart/part/PartVisitor.java index 4bddbd7e1..8e9029c4f 100644 --- a/client/src/main/java/org/asynchttpclient/request/body/multipart/part/PartVisitor.java +++ b/client/src/main/java/org/asynchttpclient/request/body/multipart/part/PartVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/uri/Uri.java b/client/src/main/java/org/asynchttpclient/uri/Uri.java index 1c76ff2ce..e1d53d1ca 100644 --- a/client/src/main/java/org/asynchttpclient/uri/Uri.java +++ b/client/src/main/java/org/asynchttpclient/uri/Uri.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/uri/UriParser.java b/client/src/main/java/org/asynchttpclient/uri/UriParser.java index 40c195630..c65f145dd 100644 --- a/client/src/main/java/org/asynchttpclient/uri/UriParser.java +++ b/client/src/main/java/org/asynchttpclient/uri/UriParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/util/HttpUtils.java b/client/src/main/java/org/asynchttpclient/util/HttpUtils.java index f62e2f235..3cca41e61 100644 --- a/client/src/main/java/org/asynchttpclient/util/HttpUtils.java +++ b/client/src/main/java/org/asynchttpclient/util/HttpUtils.java @@ -40,6 +40,7 @@ public final class HttpUtils { private static final String CONTENT_TYPE_CHARSET_ATTRIBUTE = "charset="; private static final String CONTENT_TYPE_BOUNDARY_ATTRIBUTE = "boundary="; private static final String BROTLY_ACCEPT_ENCODING_SUFFIX = ", br"; + private static final String ZSTD_ACCEPT_ENCODING_SUFFIX = ", zstd"; private HttpUtils() { // Prevent outside initialization @@ -173,4 +174,12 @@ public static CharSequence filterOutBrotliFromAcceptEncoding(String acceptEncodi } return acceptEncoding; } + + public static CharSequence filterOutZstdFromAcceptEncoding(String acceptEncoding) { + // we don't support zstd ATM + if (acceptEncoding.endsWith(ZSTD_ACCEPT_ENCODING_SUFFIX)) { + return acceptEncoding.subSequence(0, acceptEncoding.length() - ZSTD_ACCEPT_ENCODING_SUFFIX.length()); + } + return acceptEncoding; + } } diff --git a/client/src/main/java/org/asynchttpclient/util/StringUtils.java b/client/src/main/java/org/asynchttpclient/util/StringUtils.java index bccdd8dd2..0abf0b686 100644 --- a/client/src/main/java/org/asynchttpclient/util/StringUtils.java +++ b/client/src/main/java/org/asynchttpclient/util/StringUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/util/UriEncoder.java b/client/src/main/java/org/asynchttpclient/util/UriEncoder.java index 126edee3d..92706d292 100644 --- a/client/src/main/java/org/asynchttpclient/util/UriEncoder.java +++ b/client/src/main/java/org/asynchttpclient/util/UriEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/java/org/asynchttpclient/ws/WebSocketUtils.java b/client/src/main/java/org/asynchttpclient/ws/WebSocketUtils.java index 8e020ddac..628cc1d7d 100644 --- a/client/src/main/java/org/asynchttpclient/ws/WebSocketUtils.java +++ b/client/src/main/java/org/asynchttpclient/ws/WebSocketUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/main/resources/org/asynchttpclient/config/ahc-default.properties b/client/src/main/resources/org/asynchttpclient/config/ahc-default.properties index 6450221b0..f74127c23 100644 --- a/client/src/main/resources/org/asynchttpclient/config/ahc-default.properties +++ b/client/src/main/resources/org/asynchttpclient/config/ahc-default.properties @@ -13,7 +13,7 @@ org.asynchttpclient.maxRedirects=5 org.asynchttpclient.compressionEnforced=false org.asynchttpclient.enableAutomaticDecompression=true org.asynchttpclient.userAgent=AHC/2.1 -org.asynchttpclient.enabledProtocols=TLSv1.2, TLSv1.1, TLSv1 +org.asynchttpclient.enabledProtocols=TLSv1.3, TLSv1.2 org.asynchttpclient.enabledCipherSuites= org.asynchttpclient.filterInsecureCipherSuites=true org.asynchttpclient.useProxySelector=false diff --git a/client/src/test/java/org/asynchttpclient/AbstractBasicTest.java b/client/src/test/java/org/asynchttpclient/AbstractBasicTest.java index f556d7486..2dcfa859d 100644 --- a/client/src/test/java/org/asynchttpclient/AbstractBasicTest.java +++ b/client/src/test/java/org/asynchttpclient/AbstractBasicTest.java @@ -15,21 +15,22 @@ */ package org.asynchttpclient; +import io.github.nettyplus.leakdetector.junit.NettyLeakDetectorExtension; import org.asynchttpclient.test.EchoHandler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.AbstractHandler; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.asynchttpclient.test.TestUtils.addHttpConnector; @TestInstance(TestInstance.Lifecycle.PER_CLASS) +@ExtendWith(NettyLeakDetectorExtension.class) public abstract class AbstractBasicTest { protected static final Logger logger = LoggerFactory.getLogger(AbstractBasicTest.class); protected static final int TIMEOUT = 30; diff --git a/client/src/test/java/org/asynchttpclient/AsyncStreamHandlerTest.java b/client/src/test/java/org/asynchttpclient/AsyncStreamHandlerTest.java index 954ae2eac..90a515fca 100644 --- a/client/src/test/java/org/asynchttpclient/AsyncStreamHandlerTest.java +++ b/client/src/test/java/org/asynchttpclient/AsyncStreamHandlerTest.java @@ -441,7 +441,7 @@ public void asyncOptionsTest() throws Throwable { // Some responses contain the TRACE method, some do not - account for both final String[] expected = {"GET", "HEAD", "OPTIONS", "POST"}; final String[] expectedWithTrace = {"GET", "HEAD", "OPTIONS", "POST", "TRACE"}; - Future f = client.prepareOptions("https://www.shieldblaze.com/").execute(new AsyncHandlerAdapter() { + Future f = client.prepareOptions("https://www.google.com/").execute(new AsyncHandlerAdapter() { @Override public State onHeadersReceived(HttpHeaders headers) { diff --git a/client/src/test/java/org/asynchttpclient/AutomaticDecompressionTest.java b/client/src/test/java/org/asynchttpclient/AutomaticDecompressionTest.java new file mode 100644 index 000000000..dfd0a9446 --- /dev/null +++ b/client/src/test/java/org/asynchttpclient/AutomaticDecompressionTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2015-2024 AsyncHttpClient Project. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asynchttpclient; + +import com.aayushatharva.brotli4j.encoder.BrotliOutputStream; +import com.aayushatharva.brotli4j.encoder.Encoder; +import com.github.luben.zstd.Zstd; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import io.netty.handler.codec.compression.Brotli; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.zip.GZIPOutputStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class AutomaticDecompressionTest { + private static final String UNCOMPRESSED_PAYLOAD = "a".repeat(500); + + private static HttpServer HTTP_SERVER; + + private static AsyncHttpClient createClient() { + AsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder() + .setEnableAutomaticDecompression(true) + .setCompressionEnforced(true) + .build(); + return new DefaultAsyncHttpClient(config); + } + + @BeforeAll + static void setupServer() throws Exception { + HTTP_SERVER = HttpServer.create(new InetSocketAddress(0), 0); + + HTTP_SERVER.createContext("/br").setHandler(new HttpHandler() { + @Override + public void handle(HttpExchange exchange) + throws IOException { + validateAcceptEncodingHeader(exchange); + exchange.getResponseHeaders().set("Content-Encoding", "br"); + exchange.sendResponseHeaders(200, 0); + OutputStream out = exchange.getResponseBody(); + Encoder.Parameters params = new Encoder.Parameters(); + BrotliOutputStream brotliOutputStream = new BrotliOutputStream(out, params); + brotliOutputStream.write(UNCOMPRESSED_PAYLOAD.getBytes(StandardCharsets.UTF_8)); + brotliOutputStream.flush(); + brotliOutputStream.close(); + } + }); + + HTTP_SERVER.createContext("/zstd").setHandler(new HttpHandler() { + @Override + public void handle(HttpExchange exchange) + throws IOException { + validateAcceptEncodingHeader(exchange); + exchange.getResponseHeaders().set("Content-Encoding", "zstd"); + byte[] compressedData = new byte[UNCOMPRESSED_PAYLOAD.length()]; + long n = Zstd.compress(compressedData, UNCOMPRESSED_PAYLOAD.getBytes(StandardCharsets.UTF_8), 2, true); + exchange.sendResponseHeaders(200, n); + OutputStream out = exchange.getResponseBody(); + out.write(compressedData, 0, (int) n); + out.flush(); + out.close(); + } + }); + + HTTP_SERVER.createContext("/gzip").setHandler(new HttpHandler() { + @Override + public void handle(HttpExchange exchange) + throws IOException { + validateAcceptEncodingHeader(exchange); + exchange.getResponseHeaders().set("Content-Encoding", "gzip"); + exchange.sendResponseHeaders(200, 0); + OutputStream out = exchange.getResponseBody(); + GZIPOutputStream gzip = new GZIPOutputStream(out); + gzip.write(UNCOMPRESSED_PAYLOAD.getBytes(StandardCharsets.UTF_8)); + gzip.flush(); + gzip.close(); + } + }); + + HTTP_SERVER.start(); + } + + private static void validateAcceptEncodingHeader(HttpExchange exchange) { + Headers requestHeaders = exchange.getRequestHeaders(); + List acceptEncodingList = requestHeaders.get("Accept-Encoding") + .stream() + .flatMap(x -> Arrays.asList(x.split(",")).stream()) + .collect(Collectors.toList()); + assertEquals(List.of("gzip", "deflate", "br", "zstd"), acceptEncodingList); + } + + @AfterAll + static void stopServer() { + if (HTTP_SERVER != null) { + HTTP_SERVER.stop(0); + } + } + + @Test + void zstd() throws Throwable { + io.netty.handler.codec.compression.Zstd.ensureAvailability(); + try (AsyncHttpClient client = createClient()) { + Request request = new RequestBuilder("GET") + .setUrl("http://localhost/" + HTTP_SERVER.getAddress().getPort() + "/zstd") + .build(); + Response response = client.executeRequest(request).get(); + assertEquals(200, response.getStatusCode()); + assertEquals(UNCOMPRESSED_PAYLOAD, response.getResponseBody()); + } + } + + @Test + void brotli() throws Throwable { + Brotli.ensureAvailability(); + try (AsyncHttpClient client = createClient()) { + Request request = new RequestBuilder("GET") + .setUrl("http://localhost/" + HTTP_SERVER.getAddress().getPort() + "/br") + .build(); + Response response = client.executeRequest(request).get(); + assertEquals(200, response.getStatusCode()); + assertEquals(UNCOMPRESSED_PAYLOAD, response.getResponseBody()); + } + } + + @Test + void gzip() throws Throwable { + try (AsyncHttpClient client = createClient()) { + Request request = new RequestBuilder("GET") + .setUrl("http://localhost/" + HTTP_SERVER.getAddress().getPort() + "/gzip") + .build(); + Response response = client.executeRequest(request).get(); + assertEquals(200, response.getStatusCode()); + assertEquals(UNCOMPRESSED_PAYLOAD, response.getResponseBody()); + } + } + + +} diff --git a/client/src/test/java/org/asynchttpclient/BasicHttpTest.java b/client/src/test/java/org/asynchttpclient/BasicHttpTest.java index cc75c03c1..f83cac80f 100755 --- a/client/src/test/java/org/asynchttpclient/BasicHttpTest.java +++ b/client/src/test/java/org/asynchttpclient/BasicHttpTest.java @@ -79,6 +79,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -559,7 +560,7 @@ public void connectFailureNotifiesHandlerWithConnectException() throws Throwable @Override public void onThrowable(Throwable t) { try { - assertTrue(t instanceof ConnectException); + assertInstanceOf(ConnectException.class, t); } finally { l.countDown(); } @@ -962,8 +963,8 @@ public void requestingPlainHttpEndpointOverHttpsThrowsSslException() throws Thro client.prepareGet(getTargetUrl().replace("http", "https")).execute().get(); fail("Request shouldn't succeed"); } catch (ExecutionException e) { - assertTrue(e.getCause() instanceof ConnectException, "Cause should be a ConnectException"); - assertTrue(e.getCause().getCause() instanceof SSLException, "Root cause should be a SslException"); + assertInstanceOf(ConnectException.class, e.getCause(), "Cause should be a ConnectException"); + assertInstanceOf(SSLException.class, e.getCause().getCause(), "Root cause should be a SslException"); } })); } diff --git a/client/src/test/java/org/asynchttpclient/ClientStatsTest.java b/client/src/test/java/org/asynchttpclient/ClientStatsTest.java index bda1aa3a2..2d8d324de 100644 --- a/client/src/test/java/org/asynchttpclient/ClientStatsTest.java +++ b/client/src/test/java/org/asynchttpclient/ClientStatsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/test/java/org/asynchttpclient/DefaultAsyncHttpClientTest.java b/client/src/test/java/org/asynchttpclient/DefaultAsyncHttpClientTest.java index 0bfd6842c..fc7a1c2db 100644 --- a/client/src/test/java/org/asynchttpclient/DefaultAsyncHttpClientTest.java +++ b/client/src/test/java/org/asynchttpclient/DefaultAsyncHttpClientTest.java @@ -50,7 +50,7 @@ public void testNativeTransportWithEpollOnly() throws Exception { AsyncHttpClientConfig config = config().setUseNativeTransport(true).setUseOnlyEpollNativeTransport(true).build(); try (DefaultAsyncHttpClient client = (DefaultAsyncHttpClient) asyncHttpClient(config)) { - assertDoesNotThrow(() -> client.prepareGet("https://www.shieldblaze.com/").execute().get()); + assertDoesNotThrow(() -> client.prepareGet("https://www.google.com/").execute().get()); assertInstanceOf(EpollEventLoopGroup.class, client.channelManager().getEventLoopGroup()); } } @@ -60,7 +60,7 @@ public void testNativeTransportWithEpollOnly() throws Exception { public void testNativeTransportWithoutEpollOnly() throws Exception { AsyncHttpClientConfig config = config().setUseNativeTransport(true).setUseOnlyEpollNativeTransport(false).build(); try (DefaultAsyncHttpClient client = (DefaultAsyncHttpClient) asyncHttpClient(config)) { - assertDoesNotThrow(() -> client.prepareGet("https://www.shieldblaze.com/").execute().get()); + assertDoesNotThrow(() -> client.prepareGet("https://www.google.com/").execute().get()); assertInstanceOf(IOUringEventLoopGroup.class, client.channelManager().getEventLoopGroup()); } } @@ -70,7 +70,7 @@ public void testNativeTransportWithoutEpollOnly() throws Exception { public void testNativeTransportKQueueOnMacOs() throws Exception { AsyncHttpClientConfig config = config().setUseNativeTransport(true).build(); try (DefaultAsyncHttpClient client = (DefaultAsyncHttpClient) asyncHttpClient(config)) { - assertDoesNotThrow(() -> client.prepareGet("https://www.shieldblaze.com/").execute().get()); + assertDoesNotThrow(() -> client.prepareGet("https://www.google.com/").execute().get()); assertInstanceOf(KQueueEventLoopGroup.class, client.channelManager().getEventLoopGroup()); } } diff --git a/client/src/test/java/org/asynchttpclient/PerRequestRelative302Test.java b/client/src/test/java/org/asynchttpclient/PerRequestRelative302Test.java index 4c119779c..ae3eccf85 100644 --- a/client/src/test/java/org/asynchttpclient/PerRequestRelative302Test.java +++ b/client/src/test/java/org/asynchttpclient/PerRequestRelative302Test.java @@ -38,6 +38,7 @@ import static org.asynchttpclient.test.TestUtils.addHttpConnector; import static org.asynchttpclient.test.TestUtils.findFreePort; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -125,7 +126,7 @@ public void redirected302InvalidTest() throws Exception { assertNotNull(e); Throwable cause = e.getCause(); - assertTrue(cause instanceof ConnectException); + assertInstanceOf(ConnectException.class, cause); assertTrue(cause.getMessage().contains(":" + port2)); } diff --git a/client/src/test/java/org/asynchttpclient/PerRequestTimeoutTest.java b/client/src/test/java/org/asynchttpclient/PerRequestTimeoutTest.java index b04f16533..bee7d0b67 100644 --- a/client/src/test/java/org/asynchttpclient/PerRequestTimeoutTest.java +++ b/client/src/test/java/org/asynchttpclient/PerRequestTimeoutTest.java @@ -34,6 +34,7 @@ import static org.asynchttpclient.Dsl.config; import static org.asynchttpclient.util.DateUtils.unpreciseMillisTime; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -73,7 +74,7 @@ public void testRequestTimeout() throws IOException { } catch (InterruptedException e) { fail("Interrupted.", e); } catch (ExecutionException e) { - assertTrue(e.getCause() instanceof TimeoutException); + assertInstanceOf(TimeoutException.class, e.getCause()); checkTimeoutMessage(e.getCause().getMessage(), true); } catch (TimeoutException e) { fail("Timeout.", e); @@ -89,7 +90,7 @@ public void testReadTimeout() throws IOException { } catch (InterruptedException e) { fail("Interrupted.", e); } catch (ExecutionException e) { - assertTrue(e.getCause() instanceof TimeoutException); + assertInstanceOf(TimeoutException.class, e.getCause()); checkTimeoutMessage(e.getCause().getMessage(), false); } catch (TimeoutException e) { fail("Timeout.", e); @@ -107,7 +108,7 @@ public void testGlobalDefaultPerRequestInfiniteTimeout() throws IOException { } catch (InterruptedException e) { fail("Interrupted.", e); } catch (ExecutionException e) { - assertTrue(e.getCause() instanceof TimeoutException); + assertInstanceOf(TimeoutException.class, e.getCause()); checkTimeoutMessage(e.getCause().getMessage(), true); } } @@ -121,7 +122,7 @@ public void testGlobalRequestTimeout() throws IOException { } catch (InterruptedException e) { fail("Interrupted.", e); } catch (ExecutionException e) { - assertTrue(e.getCause() instanceof TimeoutException); + assertInstanceOf(TimeoutException.class, e.getCause()); checkTimeoutMessage(e.getCause().getMessage(), true); } catch (TimeoutException e) { fail("Timeout.", e); diff --git a/client/src/test/java/org/asynchttpclient/Relative302Test.java b/client/src/test/java/org/asynchttpclient/Relative302Test.java index b4d254bfb..074930791 100644 --- a/client/src/test/java/org/asynchttpclient/Relative302Test.java +++ b/client/src/test/java/org/asynchttpclient/Relative302Test.java @@ -39,6 +39,7 @@ import static org.asynchttpclient.test.TestUtils.addHttpConnector; import static org.asynchttpclient.test.TestUtils.findFreePort; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -101,7 +102,7 @@ public void redirected302InvalidTest() throws Exception { assertNotNull(e); Throwable cause = e.getCause(); - assertTrue(cause instanceof ConnectException); + assertInstanceOf(ConnectException.class, cause); assertTrue(cause.getMessage().contains(":" + port2)); } diff --git a/client/src/test/java/org/asynchttpclient/netty/NettyAsyncResponseTest.java b/client/src/test/java/org/asynchttpclient/netty/NettyAsyncResponseTest.java index 30eded248..5172bae7a 100644 --- a/client/src/test/java/org/asynchttpclient/netty/NettyAsyncResponseTest.java +++ b/client/src/test/java/org/asynchttpclient/netty/NettyAsyncResponseTest.java @@ -13,12 +13,17 @@ package org.asynchttpclient.netty; import io.github.artsok.RepeatedIfExceptionsTest; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.cookie.Cookie; +import org.asynchttpclient.HttpResponseBodyPart; +import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.TimeZone; @@ -73,4 +78,16 @@ public void testCookieParseWeirdExpiresValue() { Cookie cookie = cookies.get(0); assertEquals(Long.MIN_VALUE, cookie.maxAge()); } + + @RepeatedIfExceptionsTest(repeats = 5) + public void testGetResponseBodyAsByteBuffer() { + List bodyParts = new LinkedList<>(); + bodyParts.add(new LazyResponseBodyPart(Unpooled.wrappedBuffer("Hello ".getBytes()), false)); + bodyParts.add(new LazyResponseBodyPart(Unpooled.wrappedBuffer("World".getBytes()), true)); + NettyResponse response = new NettyResponse(new NettyResponseStatus(null, null, null), null, bodyParts); + + ByteBuf body = response.getResponseBodyAsByteBuf(); + assertEquals("Hello World", body.toString(StandardCharsets.UTF_8)); + body.release(); + } } diff --git a/client/src/test/java/org/asynchttpclient/netty/NettyConnectionResetByPeerTest.java b/client/src/test/java/org/asynchttpclient/netty/NettyConnectionResetByPeerTest.java index a315017a1..484b074a3 100644 --- a/client/src/test/java/org/asynchttpclient/netty/NettyConnectionResetByPeerTest.java +++ b/client/src/test/java/org/asynchttpclient/netty/NettyConnectionResetByPeerTest.java @@ -16,11 +16,13 @@ package org.asynchttpclient.netty; import io.github.artsok.RepeatedIfExceptionsTest; +import io.github.nettyplus.leakdetector.junit.NettyLeakDetectorExtension; import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.DefaultAsyncHttpClient; import org.asynchttpclient.DefaultAsyncHttpClientConfig; import org.asynchttpclient.RequestBuilder; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; import java.io.IOException; import java.io.InputStream; @@ -34,6 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertInstanceOf; +@ExtendWith(NettyLeakDetectorExtension.class) public class NettyConnectionResetByPeerTest { private String resettingServerAddress; diff --git a/client/src/test/java/org/asynchttpclient/netty/NettyTest.java b/client/src/test/java/org/asynchttpclient/netty/NettyTest.java new file mode 100644 index 000000000..f80c0911e --- /dev/null +++ b/client/src/test/java/org/asynchttpclient/netty/NettyTest.java @@ -0,0 +1,56 @@ +package org.asynchttpclient.netty; + +import io.netty.channel.epoll.Epoll; +import io.netty.channel.kqueue.KQueue; +import io.netty.handler.codec.compression.Brotli; +import io.netty.handler.codec.compression.Zstd; +import io.netty.incubator.channel.uring.IOUring; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class NettyTest { + @Test + @EnabledOnOs(OS.LINUX) + public void epollIsAvailableOnLinux() { + assertTrue(Epoll.isAvailable()); + } + + @Test + @EnabledOnOs(OS.LINUX) + public void ioUringIsAvailableOnLinux() { + assertTrue(IOUring.isAvailable()); + } + + @Test + @EnabledOnOs(OS.MAC) + public void kqueueIsAvailableOnMac() { + assertTrue(KQueue.isAvailable()); + } + + @Test + @EnabledOnOs(OS.LINUX) + public void brotliIsAvailableOnLinux() { + assertTrue(Brotli.isAvailable()); + } + + @Test + @EnabledOnOs(OS.MAC) + public void brotliIsAvailableOnMac() { + assertTrue(Brotli.isAvailable()); + } + + @Test + @EnabledOnOs(OS.LINUX) + public void zstdIsAvailableOnLinux() { + assertTrue(Zstd.isAvailable()); + } + + @Test + @EnabledOnOs(OS.MAC) + public void zstdIsAvailableOnMac() { + assertTrue(Zstd.isAvailable()); + } +} diff --git a/client/src/test/java/org/asynchttpclient/ntlm/NtlmTest.java b/client/src/test/java/org/asynchttpclient/ntlm/NtlmTest.java index 93506925e..0bce17d4c 100644 --- a/client/src/test/java/org/asynchttpclient/ntlm/NtlmTest.java +++ b/client/src/test/java/org/asynchttpclient/ntlm/NtlmTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -100,7 +100,7 @@ public void testGenerateType1Msg() { @RepeatedIfExceptionsTest(repeats = 5) public void testGenerateType3MsgThrowsExceptionWhenChallengeTooShort() { NtlmEngine engine = new NtlmEngine(); - assertThrows(NtlmEngineException.class, () -> engine.generateType3Msg("username", "password", "localhost", "workstation", + assertThrows(NtlmEngineException.class, () -> NtlmEngine.generateType3Msg("username", "password", "localhost", "workstation", Base64.getEncoder().encodeToString("a".getBytes())), "An NtlmEngineException must have occurred as challenge length is too short"); } @@ -108,7 +108,7 @@ public void testGenerateType3MsgThrowsExceptionWhenChallengeTooShort() { @RepeatedIfExceptionsTest(repeats = 5) public void testGenerateType3MsgThrowsExceptionWhenChallengeDoesNotFollowCorrectFormat() { NtlmEngine engine = new NtlmEngine(); - assertThrows(NtlmEngineException.class, () -> engine.generateType3Msg("username", "password", "localhost", "workstation", + assertThrows(NtlmEngineException.class, () -> NtlmEngine.generateType3Msg("username", "password", "localhost", "workstation", Base64.getEncoder().encodeToString("challenge".getBytes())), "An NtlmEngineException must have occurred as challenge length is too short"); } @@ -125,7 +125,7 @@ public void testGenerateType3MsgThworsExceptionWhenType2IndicatorNotPresent() th buf.write(0); buf.write("challenge".getBytes()); NtlmEngine engine = new NtlmEngine(); - assertThrows(NtlmEngineException.class, () -> engine.generateType3Msg("username", "password", "localhost", "workstation", + assertThrows(NtlmEngineException.class, () -> NtlmEngine.generateType3Msg("username", "password", "localhost", "workstation", Base64.getEncoder().encodeToString(buf.toByteArray())), "An NtlmEngineException must have occurred as type 2 indicator is incorrect"); } } @@ -151,7 +151,7 @@ public void testGenerateType3MsgThrowsExceptionWhenUnicodeSupportNotIndicated() buf.write(longToBytes(1L));// challenge NtlmEngine engine = new NtlmEngine(); - assertThrows(NtlmEngineException.class, () -> engine.generateType3Msg("username", "password", "localhost", "workstation", + assertThrows(NtlmEngineException.class, () -> NtlmEngine.generateType3Msg("username", "password", "localhost", "workstation", Base64.getEncoder().encodeToString(buf.toByteArray())), "An NtlmEngineException must have occurred as unicode support is not indicated"); } @@ -184,7 +184,7 @@ public void testGenerateType3Msg() throws IOException { buf.write(longToBytes(1L));// challenge NtlmEngine engine = new NtlmEngine(); - String type3Msg = engine.generateType3Msg("username", "password", "localhost", "workstation", + String type3Msg = NtlmEngine.generateType3Msg("username", "password", "localhost", "workstation", Base64.getEncoder().encodeToString(buf.toByteArray())); assertEquals(type3Msg, "TlRMTVNTUAADAAAAGAAYAEgAAAAYABgAYAAAABIAEgB4AAAAEAAQAIoAAAAWABYAmgAAAAAAAACwAAAAAQAAAgUBKAoAAAAP1g6lqqN1HZ0wSSxeQ5riQkyh7/UexwVlCPQm0SHU2vsDQm2wM6NbT2zPonPzLJL0TABPAEMAQQBMAEgATwBTAFQAdQBzAGUAcgBuAGEAbQBlAFcATwBSAEsAUwBUAEEAVABJAE8ATgA=", diff --git a/client/src/test/java/org/asynchttpclient/request/body/PutByteBufTest.java b/client/src/test/java/org/asynchttpclient/request/body/PutByteBufTest.java new file mode 100644 index 000000000..3260604d3 --- /dev/null +++ b/client/src/test/java/org/asynchttpclient/request/body/PutByteBufTest.java @@ -0,0 +1,82 @@ +/* + * 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.request.body; + +import io.github.artsok.RepeatedIfExceptionsTest; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.asynchttpclient.AbstractBasicTest; +import org.asynchttpclient.AsyncHttpClient; +import org.asynchttpclient.Response; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Arrays; + +import static org.asynchttpclient.Dsl.asyncHttpClient; +import static org.asynchttpclient.Dsl.config; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PutByteBufTest extends AbstractBasicTest { + + private void put(String message) throws Exception { + ByteBuf byteBuf = Unpooled.wrappedBuffer(message.getBytes()); + try (AsyncHttpClient client = asyncHttpClient(config().setRequestTimeout(Duration.ofSeconds(2)))) { + Response response = client.preparePut(getTargetUrl()).setBody(byteBuf).execute().get(); + assertEquals(response.getStatusCode(), 200); + assertEquals(response.getResponseBody(), message); + } + } + + @RepeatedIfExceptionsTest(repeats = 5) + public void testPutSmallBody() throws Exception { + put("Hello Test"); + } + + @RepeatedIfExceptionsTest(repeats = 5) + public void testPutBigBody() throws Exception { + byte[] array = new byte[2048]; + Arrays.fill(array, (byte) 97); + String longString = new String(array, StandardCharsets.UTF_8); + + put(longString); + } + + @Override + public AbstractHandler configureHandler() throws Exception { + return new AbstractHandler() { + + @Override + public void handle(String s, Request request, HttpServletRequest httpRequest, HttpServletResponse response) throws IOException { + int size = 1024; + if (request.getContentLength() > 0) { + size = request.getContentLength(); + } + byte[] bytes = new byte[size]; + if (bytes.length > 0) { + final int read = request.getInputStream().read(bytes); + response.getOutputStream().write(bytes, 0, read); + } + + response.setStatus(200); + response.getOutputStream().flush(); + } + }; + } +} diff --git a/client/src/test/java/org/asynchttpclient/request/body/generator/FeedableBodyGeneratorTest.java b/client/src/test/java/org/asynchttpclient/request/body/generator/FeedableBodyGeneratorTest.java index 4c8d14693..7c2a3579b 100755 --- a/client/src/test/java/org/asynchttpclient/request/body/generator/FeedableBodyGeneratorTest.java +++ b/client/src/test/java/org/asynchttpclient/request/body/generator/FeedableBodyGeneratorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/test/java/org/asynchttpclient/test/EventCollectingHandler.java b/client/src/test/java/org/asynchttpclient/test/EventCollectingHandler.java index 1ef8201f6..2568b7ae1 100644 --- a/client/src/test/java/org/asynchttpclient/test/EventCollectingHandler.java +++ b/client/src/test/java/org/asynchttpclient/test/EventCollectingHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/test/java/org/asynchttpclient/testserver/HttpTest.java b/client/src/test/java/org/asynchttpclient/testserver/HttpTest.java index 44d31269f..b41b6ab1b 100644 --- a/client/src/test/java/org/asynchttpclient/testserver/HttpTest.java +++ b/client/src/test/java/org/asynchttpclient/testserver/HttpTest.java @@ -15,15 +15,18 @@ */ package org.asynchttpclient.testserver; +import io.github.nettyplus.leakdetector.junit.NettyLeakDetectorExtension; import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.AsyncHttpClientConfig; import org.asynchttpclient.DefaultAsyncHttpClientConfig; +import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.asynchttpclient.Dsl.asyncHttpClient; import static org.asynchttpclient.Dsl.config; +@ExtendWith(NettyLeakDetectorExtension.class) public abstract class HttpTest { protected static final String COMPLETED_EVENT = "Completed"; diff --git a/client/src/test/java/org/asynchttpclient/testserver/SocksProxy.java b/client/src/test/java/org/asynchttpclient/testserver/SocksProxy.java index d3b193209..b8e67b79f 100644 --- a/client/src/test/java/org/asynchttpclient/testserver/SocksProxy.java +++ b/client/src/test/java/org/asynchttpclient/testserver/SocksProxy.java @@ -39,15 +39,17 @@ public SocksProxy(int runningTime) throws IOException { Set keys = select.selectedKeys(); for (SelectionKey k : keys) { - if (!k.isValid()) + if (!k.isValid()) { continue; + } // new connection? if (k.isAcceptable() && k.channel() == socks) { // server socket SocketChannel csock = socks.accept(); - if (csock == null) + if (csock == null) { continue; + } addClient(csock); csock.register(select, SelectionKey.OP_READ); } else if (k.isReadable()) { @@ -56,14 +58,16 @@ public SocksProxy(int runningTime) throws IOException { SocksClient cl = clients.get(i); try { if (k.channel() == cl.client) // from client (e.g. socks client) + { cl.newClientData(select); - else if (k.channel() == cl.remote) { // from server client is connected to (e.g. website) + } else if (k.channel() == cl.remote) { // from server client is connected to (e.g. website) cl.newRemoteData(); } } catch (IOException e) { // error occurred - remove client cl.client.close(); - if (cl.remote != null) + if (cl.remote != null) { cl.remote.close(); + } k.cancel(); clients.remove(cl); } @@ -75,10 +79,11 @@ else if (k.channel() == cl.remote) { // from server client is connected to (e.g // client timeout check for (int i = 0; i < clients.size(); i++) { SocksClient cl = clients.get(i); - if ((System.currentTimeMillis() - cl.lastData) > 30000L) { + if (System.currentTimeMillis() - cl.lastData > 30000L) { cl.client.close(); - if (cl.remote != null) + if (cl.remote != null) { cl.remote.close(); + } clients.remove(cl); } } @@ -115,8 +120,9 @@ class SocksClient { void newRemoteData() throws IOException { ByteBuffer buf = ByteBuffer.allocate(1024); - if (remote.read(buf) == -1) + if (remote.read(buf) == -1) { throw new IOException("disconnected"); + } lastData = System.currentTimeMillis(); buf.flip(); client.write(buf); @@ -125,8 +131,9 @@ void newRemoteData() throws IOException { void newClientData(Selector selector) throws IOException { if (!connected) { ByteBuffer inbuf = ByteBuffer.allocate(512); - if (client.read(inbuf) < 1) + if (client.read(inbuf) < 1) { return; + } inbuf.flip(); // read socks header @@ -143,13 +150,15 @@ void newClientData(Selector selector) throws IOException { final int port = inbuf.getShort() & 0xffff; - final byte ip[] = new byte[4]; + final byte[] ip = new byte[4]; // fetch IP inbuf.get(ip); InetAddress remoteAddr = InetAddress.getByAddress(ip); - while ((inbuf.get()) != 0) ; // username + while (inbuf.get() != 0) { + ; // username + } // hostname provided, not IP if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] != 0) { // host provided @@ -172,8 +181,9 @@ void newClientData(Selector selector) throws IOException { out.flip(); client.write(out); - if (!remote.isConnected()) + if (!remote.isConnected()) { throw new IOException("connect failed"); + } remote.configureBlocking(false); remote.register(selector, SelectionKey.OP_READ); @@ -181,8 +191,9 @@ void newClientData(Selector selector) throws IOException { connected = true; } else { ByteBuffer buf = ByteBuffer.allocate(1024); - if (client.read(buf) == -1) + if (client.read(buf) == -1) { throw new IOException("disconnected"); + } lastData = System.currentTimeMillis(); buf.flip(); remote.write(buf); diff --git a/client/src/test/java/org/asynchttpclient/uri/UriTest.java b/client/src/test/java/org/asynchttpclient/uri/UriTest.java index 143008e15..f766854e1 100644 --- a/client/src/test/java/org/asynchttpclient/uri/UriTest.java +++ b/client/src/test/java/org/asynchttpclient/uri/UriTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/client/src/test/java/org/asynchttpclient/ws/ProxyTunnellingTest.java b/client/src/test/java/org/asynchttpclient/ws/ProxyTunnellingTest.java index a2177ae96..ce9cda3dc 100644 --- a/client/src/test/java/org/asynchttpclient/ws/ProxyTunnellingTest.java +++ b/client/src/test/java/org/asynchttpclient/ws/ProxyTunnellingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023 AsyncHttpClient Project. All rights reserved. + * Copyright (c) 2014-2024 AsyncHttpClient Project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/docs/technical-overview.md b/docs/technical-overview.md deleted file mode 100644 index ad65ec1b9..000000000 --- a/docs/technical-overview.md +++ /dev/null @@ -1,368 +0,0 @@ -# [WIP] AsyncHttpClient Technical Overview - -#### Disclaimer - -This document is a work in progress. - -## Motivation - -While heavily used (~2.3M downloads across the project in December 2020 alone), AsyncHttpClient (or AHC) does not - at this point in time - have a single guiding document that -explains how it works internally. As a maintainer fresh on the scene it was unclear to me ([@TomGranot](https://github.com/TomGranot)) exactly how all the pieces fit together. - -As part of the attempt to educate myself, I figured it would be a good idea to write a technical overview of the project. This document provides an in-depth walkthtough of the -library, allowing new potential contributors to "hop on" the coding train as fast as possible. - -Note that this library *is not small*. I expect that in addition to offering a better understanding as to how each piece *works*, writing this document will also allow me to -understand which pieces *do not work* as well as expected, and direct me towards things that need a little bit of love. - -PRs are open for anyone who wants to help out. For now - let the fun begin. :) - -**Note: I wrote this guide while using AHC 2.12.2**. - -## The flow of a request - -### Introduction - -AHC is an *Asynchronous* HTTP Client. That means that it needs to have some underlying mechanism of dealing with response data that arrives **asynchronously**. To make that -part easier, the creator of the library ([@jfarcand](https://github.com/jfarcand)) built it on top of [Netty](https://netty.io/), which is ( -by [their own definition](https://netty.io/#content:~:text=Netty%20is%20a%20NIO%20client%20server,as%20TCP%20and%20UDP%20socket%20server.)) "a framework that enables quick and -easy development of network applications". - -This article is not a Netty user guide. If you're interested in all Netty has to offer, you should check out -the [official user guide](https://netty.io/wiki/user-guide-for-4.x.html). This article is, instead, more of a discussion of using Netty *in the wild* - an overview of what a -client library built on top of Netty actually looks like in practice. - -### The code in full - -The best way to explore what the client actually does is, of course, by following the path a request takes. - -Consider the following bit of -code, [taken verbatim from one of the simplest tests](https://github.com/AsyncHttpClient/async-http-client/blob/2b12d0ba819e05153fa265b4da7ca900651fd5b3/client/src/test/java/org/asynchttpclient/BasicHttpTest.java#L81-L91) -in the library: - -```java -@Test - public void getRootUrl() throws Throwable { - withClient().run(client -> - withServer(server).run(server -> { - String url = server.getHttpUrl(); - server.enqueueOk(); - - Response response = client.executeRequest(get(url), new AsyncCompletionHandlerAdapter()).get(TIMEOUT, SECONDS); - assertEquals(response.getUri().toUrl(), url); - })); - } -``` - -Let's take it bit by bit. - -First: - -```java -withClient().run(client -> - withServer(server).run(server -> { - String url = server.getHttpUrl(); - server.enqueueOk(); -``` - -These lines take care of spinning up a server to run the test against, and create an instance of `AsyncHttpClient` called `client`. If you were to drill deeper into the code, -you'd notice that the instantiation of `client` can be simplified to (converted from functional to procedural for the sake of the explanation): - -```java -DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder().build.()setMaxRedirects(0); -AsyncHttpClient client = new DefaultAsyncHttpClient(config); -``` - -Once the server and the client have been created, we can now run our test: - -```java -Response response = client.executeRequest(get(url), new AsyncCompletionHandlerAdapter()).get(TIMEOUT, SECONDS); -assertEquals(response.getUri().toUrl(), url); -``` - -The first line executes a `GET` request to the URL of the server that was previously spun up, while the second line is the assertion part of our test. Once the request is -completed, the final `Response` object is returned. - -The intersting bits, of course, happen between the lines - and the best way to start the discussion is by considering what happens under the hood when a new client is -instantiated. - -### Creating a new AsyncHTTPClient - Configuration - -AHC was designed to be *heavily configurable*. There are many, many different knobs you can turn and buttons you can press in order to get it to behave _just right_ -. [`DefaultAsyncHttpClientConfig`](https://github.com/AsyncHttpClient/async-http-client/blob/d4f1e5835b81a5e813033ba2a64a07b020c70007/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java) -is a utility class that pulls a set -of [hard-coded, sane defaults](https://github.com/AsyncHttpClient/async-http-client/blob/d4f1e5835b81a5e813033ba2a64a07b020c70007/client/src/main/resources/org/asynchttpclient/config/ahc-default.properties) -to prevent you from having to deal with these mundane configurations - please check it out if you have the time. - -A keen observer will note that the construction of a `DefaultAsyncHttpClient` is done using -the [Builder Pattern](https://dzone.com/articles/design-patterns-the-builder-pattern) - this is useful, since many times you want to change a few parameters in a -request (`followRedirect`, `requestTimeout`, etc... ) but still rely on the rest of the default configuration properties. The reason I'm mentioning this here is to prevent a -bit of busywork on your end the next time you want to create a client - it's much, much easier to work off of the default client and tweak properties than creating your own -set of configuration properties. - -The `setMaxRedicrects(0)` from the initialization code above is an example of doign this in practice. Having no redirects following the `GET` requeset is useful in the context -of the test, and so we turn a knob to ensure none do. - -### Creating a new AsyncHTTPClient - Client Instantiation - -Coming back to our example - once we've decided on a proper configuration, it's time to create a client. Let's look at the constructor of -the [`DefaultAsyncHttpClient`](https://github.com/AsyncHttpClient/async-http-client/blob/a44aac86616f4e8ffe6977dfef0f0aa460e79d07/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java): - -```java - public DefaultAsyncHttpClient(AsyncHttpClientConfig config) { - - this.config = config; - this.noRequestFilters = config.getRequestFilters().isEmpty(); - allowStopNettyTimer = config.getNettyTimer() == null; - nettyTimer = allowStopNettyTimer ? newNettyTimer(config) : config.getNettyTimer(); - - channelManager = new ChannelManager(config, nettyTimer); - requestSender = new NettyRequestSender(config, channelManager, nettyTimer, new AsyncHttpClientState(closed)); - channelManager.configureBootstraps(requestSender); - - CookieStore cookieStore = config.getCookieStore(); - if (cookieStore != null) { - int cookieStoreCount = config.getCookieStore().incrementAndGet(); - if ( - allowStopNettyTimer // timer is not shared - || cookieStoreCount == 1 // this is the first AHC instance for the shared (user-provided) timer - ) { - nettyTimer.newTimeout(new CookieEvictionTask(config.expiredCookieEvictionDelay(), cookieStore), - config.expiredCookieEvictionDelay(), TimeUnit.MILLISECONDS); - } - } - } -``` - -The constructor actually reveals a lot of the moving parts of AHC, and is worth a proper walkthrough: - -#### `RequestFilters` - -```java - this.noRequestFilters = config.getRequestFilters().isEmpty(); -``` - -`RequestFilters` are a way to perform some form of computation **before sending a request to a server**. You can read more about request filters [here](#request-filters), but -a simple example is -the [ThrottleRequestFilter](https://github.com/AsyncHttpClient/async-http-client/blob/758dcf214bf0ec08142ba234a3967d98a3dc60ef/client/src/main/java/org/asynchttpclient/filter/ThrottleRequestFilter.java) -that throttles requests by waiting for a response to arrive before executing the next request in line. - -Note that there is another set of filters, `ResponseFilters`, that can perform computations **before processing the first byte of the response**. You can read more about -them [here](#response-filters). - -#### `NettyTimer` - -```java -allowStopNettyTimer = config.getNettyTimer() == null; -nettyTimer = allowStopNettyTimer ? newNettyTimer(config) : config.getNettyTimer(); -``` - -`NettyTimer` is actually not a timer, but a *task executor* that waits an arbitrary amount of time before performing the next task. In the case of the code above, it is used -for evicting cookies after they expire - but it has many different use cases (request timeouts being a prime example). - -#### `ChannelManager` - -```java -channelManager = new ChannelManager(config, nettyTimer); -``` - -`ChannelManager` requires a [section of its own](#channelmanager), but the bottom line is that one has to do a lot of boilerplate work with Channels when building an HTTP -client using Netty. For any given request there's a variable number of channel operations you would have to take, and there's a lot of value in re-using existing channels in -clever ways instead of opening new ones. `ChannelManager` is AHC's way of encapsulating at least some of that functionality (for -example, [connection pooling](https://en.wikipedia.org/wiki/Connection_pool#:~:text=In%20software%20engineering%2C%20a%20connection,executing%20commands%20on%20a%20database.)) -into a single object, instead of having it spread out all over the place. - -There are two similiarly-named constructs in the project, so I'm mentioning them in this - -* `ChannelPool`, as it - is [implemented in AHC](https://github.com/AsyncHttpClient/async-http-client/blob/758dcf214bf0ec08142ba234a3967d98a3dc60ef/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java#L21) - , is an **AHC structure** designed to be a "container" of channels - a place you can add and remove channels from as the need arises. Note that the AHC implementation (that - might go as far back as 2012) *predates* the [Netty implementation](https://netty.io/news/2015/05/07/4-0-28-Final.html) introduced in 2015 (see - this [AHC user guide entry](https://asynchttpclient.github.io/async-http-client/configuring.html#contentBox:~:text=ConnectionsPoo,-%3C) from 2012 in which `ConnectionPool` - is referenced as proof). - - As - the [Netty release mentions](https://netty.io/news/2015/05/07/4-0-28-Final.html#main-content:~:text=Many%20of%20our%20users%20needed%20to,used%20Netty%20to%20writing%20a%20client.) - , connection pooling in the world of Netty-based clients is a valuable feature to have, one that [Jean-Francois](https://github.com/jfarcand) implemented himself instead of - waiting for Netty to do so. This might confuse anyone coming to the code a at a later point in time - like me - and I have yet to explore the tradeoffs of stripping away the - current implementation and in favor of the upstream one. See [this issue](https://github.com/AsyncHttpClient/async-http-client/issues/1766) for current progress. - -* [`ChannelGroup`](https://netty.io/4.0/api/io/netty/channel/group/ChannelGroup.html) (not to be confused with `ChannelPool`) is a **Netty structure** designed to work with - Netty `Channel`s *in bulk*, to reduce the need to perform the same operation on multiple channels sequnetially. - -#### `NettyRequestSender` - -```java -requestSender = new NettyRequestSender(config, channelManager, nettyTimer, new AsyncHttpClientState(closed)); -channelManager.configureBootstraps(requestSender); -``` - -`NettyRequestSender` does the all the heavy lifting required for sending the HTTP request - creating the required `Request` and `Response` objects, making sure `CONNECT` -requests are sent before the relevant requests, dealing with proxy servers (in the case -of [HTTPS connections](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT)), dispatching DNS hostname resolution requests and more. - -A few extra comments before we move on: - -* When finished with all the work, `NettyRequestSender` will send back - a [`ListenableFuture`](https://github.com/AsyncHttpClient/async-http-client/blob/d47c56e7ee80b76a4cffd4770237239cfea0ffd6/client/src/main/java/org/asynchttpclient/ListenableFuture.java#L40) - . AHC's `ListenableFuture` is an extension of a normal Java `Future` that allows for the addition of "Listeners" - pieces of code that get executed once the computation (the - one blocking the `Future` from completing) is finished. It is an example of a *very* common abstraction that exists in many different Java projects - - Google's [Guava](https://github.com/google/guava) [has one](https://github.com/google/guava/blob/master/futures/listenablefuture1/src/com/google/common/util/concurrent/ListenableFuture.java) - , for example, and so does [Spring](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/concurrent/ListenableFuture.html)). - -* Note the invocation of `configureBootstraps` in the second line here. `Bootstrap`s are a Netty concept that make it easy to set up `Channel`s - we'll talk about them a bit - later. - -#### `CookieStore` - -```java -CookieStore cookieStore = config.getCookieStore(); - if (cookieStore != null) { - int cookieStoreCount = config.getCookieStore().incrementAndGet(); - if ( - allowStopNettyTimer // timer is not shared - || cookieStoreCount == 1 // this is the first AHC instance for the shared (user-provided) timer - ) { - nettyTimer.newTimeout(new CookieEvictionTask(config.expiredCookieEvictionDelay(), cookieStore), - config.expiredCookieEvictionDelay(), TimeUnit.MILLISECONDS); - } - } -``` - -`CookieStore` is, well, a container for cookies. In this context, it is used to handle the task of cookie eviction (removing cookies whose expiry date has passed). This is, by -the way, an example of one of the *many, many features* AHC supports out of the box that might not be evident upon first observation. - -Once the client has been properly configured, it's time to actually execute the request. - -### Executing a request - Before execution - -Take a look at the execution line from the code above again: - -```java -Response response = client.executeRequest(get(url), new AsyncCompletionHandlerAdapter()).get(TIMEOUT, SECONDS); -``` - -Remember that what we have in front of us is an instance of `AsyncHttpClient` called `client` that is configured with an `AsyncHttpClientConfig`, and more specifically an -instance of `DefaultAsyncHttpClient` that is configured with `DefaultAsyncHttpClientConfig`. - -The `executeRequest` method is passed two arguments, and returns a `ListenableFuture`. The `Response` created by executing the `get` method on the `ListenableFuture` is the -end of the line in our case here, since this test is very simple - there's no response body to parse or any other computations to do in order to assert the test succeeded. The -only thing that is required for the correct operation of the code is for the `Response` to come back with the correct URL. - -Let's turn our eyes to the two arguments passed to `executeRequest`, then, since they are the key parts here: - -1. `get(url)` is the functional equivalent of `new RequestBuilder("GET").setUrl(url)`. `RequestBuilder` is in charge of scaffolding an instance - of [AHC's `Request` object](https://github.com/AsyncHttpClient/async-http-client/blob/c5eff423ebdd0cddd00bc6fcf17682651a151028/client/src/main/java/org/asynchttpclient/Request.java) - and providing it with sane defaults - mostly regarding HTTP headers (`RequestBuilder` does for `Request` what `DefaultAsyncHttpClientConfig.Builder()` does - for `DefaultAsyncHttpClient`). - 1. In our case, the `Request` contains no body (it's a simple `GET`). However, if that request was, for example, a `POST` - it could have a payload that would need to be - sent to the server via HTTP as well. We'll be talking about `Request` in more detail [here](#working-with-request-bodies), including how to work with request bodies. -2. To fully understand what `AsyncCompletionHandlerAdapter` is, and why it's such a core piece of everything that goes on in AHC, a bit of Netty background is required. Let's - take a sidestep for a moment: - -#### Netty `Channel`s and their associated entities ( `ChannelPipeline`s, `ChannelHandler`s and `ChannelAdapter`s) - -Recall that AHC is built on [Netty](https://netty.io/) and its networking abstractions. If you want to dive deeper into the framework you **should** -read [Netty in Action](https://www.manning.com/books/netty-in-action) (great book, Norman!), but for the sake of our discussion it's enough to settle on clarifying a few basic -terms: - -1. [`Channel`](https://netty.io/4.1/api/io/netty/channel/Channel.html) is Netty's version of a normal - Java [`Socket`](https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html), greatly simplified for easier usage. It - encapsulates [all that you can know and do](https://netty.io/4.1/api/io/netty/channel/Channel.html#allclasses_navbar_top:~:text=the%20current%20state%20of%20the%20channel,and%20requests%20associated%20with%20the%20channel.) - with a regular `Socket`: - - 1. **State** - Is the socket currently open? Is it currently closed? - 2. **I/O Options** - Can we read from it? Can we write to it? - 3. **Configuration** - What is the receive buffer size? What is the connect timeout? - 4. `ChannelPipeline` - A reference to this `Channel`'s `ChannelPipeline`. - -2. Note that operations on a channel, in and of themselves, are **blocking** - that is, any operation that is performed on a channel blocks any other operations from being - performed on the channel at any given point in time. This is contrary to the Asynchronous nature Netty purports to support. - - To solve the issue, Netty adds a `ChannelPipeline` to every new `Channel` that is initialised. A `ChannelPipeline` is nothing but a container for `ChannelHandlers`. -3. [`ChannelHandler`](https://netty.io/4.1/api/io/netty/channel/ChannelHandler.html)s encapsulate the application logic of a Netty application. To be more precise, a *chain* - of `ChannelHandler`s, each in charge of one or more small pieces of logic that - when taken together - describe the entire data processing that is supposed to take place - during the lifetime of the application. - -4. [`ChannelHandlerContext`](https://netty.io/4.0/api/io/netty/channel/ChannelHandlerContext.html) is also worth mentioning here - it's the actual mechanism a `ChannelHandler` - uses to talk to the `ChannelPipeline` that encapsulates it. - -5. `ChannelXHandlerAdapter`s are a set of *default* handler implementations - "sugar" that should make the development of application logic easier. `X` can - be `Inbound ` (`ChannelInboundHandlerAdapter`), `Oubound` (`ChannelOutboundHandlerAdapter`) or one of many other options Netty provides out of the box. - -#### `ChannelXHandlerAdapter` VS. `AsyncXHandlerAdapter` - -This where it's important to note the difference between `ChannelXHandlerAdapter` (i.e. `ChannelInboundHandlerAdapater`) - which is a **Netty construct** -and `AsyncXHandlerAdapter` (i.e. `AsyncCompletionHandlerAdapater`) - which is an **AHC construct**. - -Basically, `ChannelXHandlerAdapter` is a Netty construct that provides a default implementation of a `ChannelHandler`, while `AsyncXHandlerAdapter` is an AHC construct that -provides a default implementation of an `AsyncHandler`. - -A `ChannelXHandlerAdapter` has methods that are called when *handler-related* and *channel-related* events occur. When the events "fire", a piece of business logic is carried -out in the relevant method, and the operation is then **passed on to the** **next `ChannelHandler` in line.** *The methods return nothing.* - -An `AsyncXHandlerAdapter` works a bit differently. It has methods that are triggered when *some piece of data is available during an asynchronous response processing*. The -methods are invoked in a pre-determined order, based on the expected arrival of each piece of data (when the status code arrives, when the headers arrive, etc.). When these -pieces of information become availale, a piece of business logic is carried out in the relevant method, and * -a [`STATE`](https://github.com/AsyncHttpClient/async-http-client/blob/f61f88e694850818950195379c5ba7efd1cd82ee/client/src/main/java/org/asynchttpclient/AsyncHandler.java#L242-L253) -is returned*. This `STATE` enum instructs the current implementation of the `AsyncHandler` (in our case, `AsyncXHandlerAdapater`) whether it should `CONTINUE` or `ABORT` the -current processing. - -This is **the core of AHC**: an asynchronous mechanism that encodes - and allows a developer to "hook" into - the various stages of the asynchronous processing of an HTTP -response. - -### Executing a request - During execution - -TODO - -### Executing a request - After execution - -TODO - -## Working with Netty channels - -### ChannelManager - -TODO - -## Transforming requests and responses - -TODO - -### Working with Request Bodies - -TODO - -### Request Filters - -TODO - -### Working with Response Bodies - -TODO - -### Response Filters - -TODO - -### Handlers - -TODO - -## Resources - -### Netty - -* https://seeallhearall.blogspot.com/2012/05/netty-tutorial-part-1-introduction-to.html - -### AsyncHttpClient - -TODO - -### HTTP - -TODO - -## Footnotes - -[^1] Some Netty-related definitions borrow heavily from [here](https://seeallhearall.blogspot.com/2012/05/netty-tutorial-part-1-introduction-to.html). diff --git a/mvnw b/mvnw old mode 100644 new mode 100755 index b7f064624..5643201c7 --- a/mvnw +++ b/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.1.1 +# Maven Start Up Batch script # # Required ENV vars: # ------------------ @@ -27,6 +27,7 @@ # # Optional ENV vars # ----------------- +# M2_HOME - location of maven2's installed home dir # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @@ -61,9 +62,9 @@ case "`uname`" in # See https://developer.apple.com/library/mac/qa/qa1170/_index.html if [ -z "$JAVA_HOME" ]; then if [ -x "/usr/libexec/java_home" ]; then - JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME + export JAVA_HOME="`/usr/libexec/java_home`" else - JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + export JAVA_HOME="/Library/Java/Home" fi fi ;; @@ -75,8 +76,36 @@ if [ -z "$JAVA_HOME" ] ; then fi fi +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` [ -n "$CLASSPATH" ] && @@ -85,6 +114,8 @@ fi # For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" fi @@ -132,9 +163,12 @@ if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { + if [ -z "$1" ] then echo "Path not specified to find_maven_basedir" @@ -154,7 +188,7 @@ find_maven_basedir() { fi # end of workaround done - printf '%s' "$(cd "$basedir"; pwd)" + echo "${basedir}" } # concatenates all lines of a file @@ -164,16 +198,11 @@ concat_lines() { fi } -BASE_DIR=$(find_maven_basedir "$(dirname $0)") +BASE_DIR=`find_maven_basedir "$(pwd)"` if [ -z "$BASE_DIR" ]; then exit 1; fi -MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi - ########################################################################################## # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central # This allows using the maven wrapper in projects that prohibit checking in binary data. @@ -187,16 +216,16 @@ else echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." fi if [ -n "$MVNW_REPOURL" ]; then - wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" else - wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" fi while IFS="=" read key value; do - case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;; + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; esac done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $wrapperUrl" + echo "Downloading from: $jarUrl" fi wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" if $cygwin; then @@ -204,49 +233,42 @@ else fi if command -v wget > /dev/null; then - QUIET="--quiet" if [ "$MVNW_VERBOSE" = true ]; then echo "Found wget ... using wget" - QUIET="" fi if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" else - wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" fi - [ $? -eq 0 ] || rm -f "$wrapperJarPath" elif command -v curl > /dev/null; then - QUIET="--silent" if [ "$MVNW_VERBOSE" = true ]; then echo "Found curl ... using curl" - QUIET="" fi if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L + curl -o "$wrapperJarPath" "$jarUrl" -f else - curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f fi - [ $? -eq 0 ] || rm -f "$wrapperJarPath" + else if [ "$MVNW_VERBOSE" = true ]; then echo "Falling back to using Java to download" fi - javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" # For Cygwin, switch paths to Windows format before running javac if $cygwin; then - javaSource=`cygpath --path --windows "$javaSource"` javaClass=`cygpath --path --windows "$javaClass"` fi - if [ -e "$javaSource" ]; then - if [ ! -e "$javaClass" ]; then + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then if [ "$MVNW_VERBOSE" = true ]; then echo " - Compiling MavenWrapperDownloader.java ..." fi # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaSource") + ("$JAVA_HOME/bin/javac" "$javaClass") fi - if [ -e "$javaClass" ]; then + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then # Running the downloader if [ "$MVNW_VERBOSE" = true ]; then echo " - Running MavenWrapperDownloader.java ..." @@ -260,10 +282,16 @@ fi # End of extension ########################################################################################## +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` [ -n "$CLASSPATH" ] && @@ -283,5 +311,6 @@ exec "$JAVACMD" \ $MAVEN_OPTS \ $MAVEN_DEBUG_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 474c9d6b7..23b7079a3 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,187 +1,188 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.1.1 -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %WRAPPER_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause - -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% - -cmd /C exit /B %ERROR_CODE% +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index a6ec8ed66..ed8466618 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,6 @@