Skip to content

Commit

Permalink
feat: Flag to disable insecure HTTP requests via ExtendedHttpClient.
Browse files Browse the repository at this point in the history
  • Loading branch information
nstdio committed Jun 8, 2022
1 parent e9ac5c9 commit bf90fb9
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 28 deletions.
28 changes: 23 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
# Changelog

All notable changes to this project will be documented in this file.

## [Unreleased](https://github.com/nstdio/http-client-ext/compare/v2.1.3...HEAD)

### ⭐ Features

- Flag to disable insecure HTTP requests via
ExtendedHttpClient. ([bfbdcac](https://github.com/nstdio/http-client-ext/commit/bfbdcacb07e49ab460cd27cef66b329cd19222a6))

### ♻️ Improvements

- Reformat code. ([e9ac5c9](https://github.com/nstdio/http-client-ext/commit/e9ac5c939c0c28d946af9b188c83578f190e5dac))

## [v2.1.3](https://github.com/nstdio/http-client-ext/compare/v2.1.2...v2.1.3)

### ⭐ Features
- Cache interface now extends Closeable ([2e9076a](https://github.com/nstdio/http-client-ext/commit/2e9076a7ae337c55c5817d15ddab9c8e9e3e5bb5))
### ⭐ Features

- Cache interface now extends
Closeable ([2e9076a](https://github.com/nstdio/http-client-ext/commit/2e9076a7ae337c55c5817d15ddab9c8e9e3e5bb5))

### ♻️ Improvements
- Improve cache write. ([b7f1289](https://github.com/nstdio/http-client-ext/commit/b7f128900372ee033aaef79b11ab09ed65f5c0ce))

- Improve cache
write. ([b7f1289](https://github.com/nstdio/http-client-ext/commit/b7f128900372ee033aaef79b11ab09ed65f5c0ce))

## [v2.1.2](https://github.com/nstdio/http-client-ext/compare/v2.1.1...v2.1.2)

### 🐞 Bug Fixes
- NPE when no `Content-Encoding` header present. ([b3afc61](https://github.com/nstdio/http-client-ext/commit/b3afc610c593024c013185ba93a3a3e7988bd9ca))
### 🐞 Bug Fixes

- NPE when no `Content-Encoding` header
present. ([b3afc61](https://github.com/nstdio/http-client-ext/commit/b3afc610c593024c013185ba93a3a3e7988bd9ca))
### ♻️ Improvements
- **perf** Tune decompression performance. ([1c3c581](https://github.com/nstdio/http-client-ext/commit/1c3c581f52db95cf4d88bc6dcea9e420b3015b5f))
## [v2.1.1](https://github.com/nstdio/http-client-ext/compare/v2.1.0...v2.1.1)
Expand Down
4 changes: 0 additions & 4 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,3 @@ dependencies {
}
implementation("com.github.spotbugs.snom:spotbugs-gradle-plugin:5.0.6")
}

kotlinDslPluginOptions {
jvmTarget.set(provider { java.targetCompatibility.toString() })
}
25 changes: 22 additions & 3 deletions src/main/java/io/github/nstdio/http/ext/ExtendedHttpClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,17 @@ public class ExtendedHttpClient extends HttpClient {
private final CachingInterceptor cachingInterceptor;

private final HttpClient delegate;
private final boolean allowInsecure;

ExtendedHttpClient(HttpClient delegate, Cache cache, Clock clock) {
this(delegate, cache, false, clock);
this(delegate, cache, false, true, clock);
}

ExtendedHttpClient(HttpClient delegate, Cache cache, boolean transparentEncoding, Clock clock) {
ExtendedHttpClient(HttpClient delegate, Cache cache, boolean transparentEncoding, boolean allowInsecure, Clock clock) {
this.delegate = delegate;
this.cachingInterceptor = cache instanceof NullCache ? null : new CachingInterceptor(cache, clock);
this.compressionInterceptor = transparentEncoding ? new CompressionInterceptor() : null;
this.allowInsecure = allowInsecure;
}

/**
Expand Down Expand Up @@ -160,6 +162,10 @@ public <T> CompletableFuture<HttpResponse<T>> sendAsync(HttpRequest request, Bod
}

private <T> CompletableFuture<HttpResponse<T>> send0(HttpRequest request, BodyHandler<T> bodyHandler, Sender<T> sender) {
if (!allowInsecure && "http".equalsIgnoreCase(request.uri().getScheme())) {
throw new IllegalArgumentException("Client does not allow insecure HTTP requests.");
}

Chain<T> chain = buildAndExecute(RequestContext.of(request, bodyHandler));
FutureHandler<T> handler = chain.futureHandler();

Expand Down Expand Up @@ -223,6 +229,7 @@ interface Sender<T> extends Function<RequestContext, CompletableFuture<HttpRespo
public static class Builder implements HttpClient.Builder {
private final HttpClient.Builder delegate;
private boolean transparentEncoding;
private boolean allowInsecure = true;
private Cache cache = Cache.noop();

Builder(HttpClient.Builder delegate) {
Expand Down Expand Up @@ -349,11 +356,23 @@ public Builder transparentEncoding(boolean transparentEncoding) {
return this;
}

/**
* Sets the flag whether client accept requests with {@code http} scheme. Default is {@code true}.
*
* @param allowInsecure Whether accept insecure {@code http} scheme or not.
*
* @return builder itself.
*/
public Builder allowInsecure(boolean allowInsecure) {
this.allowInsecure = allowInsecure;
return this;
}

@Override
public ExtendedHttpClient build() {
HttpClient client = delegate.build();

return new ExtendedHttpClient(client, cache, transparentEncoding, Clock.systemUTC());
return new ExtendedHttpClient(client, cache, transparentEncoding, allowInsecure, Clock.systemUTC());
}
}
}
50 changes: 34 additions & 16 deletions src/test/kotlin/io/github/nstdio/http/ext/ExtendedHttpClientTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,20 @@ import javax.net.ssl.SSLParameters

internal class ExtendedHttpClientTest {
private lateinit var client: ExtendedHttpClient
private lateinit var mockHttpClient: HttpClient
private lateinit var mockDelegate: HttpClient

@BeforeEach
fun setUp() {
mockHttpClient = mock(HttpClient::class.java)
client = ExtendedHttpClient(mockHttpClient, NullCache.INSTANCE, Clock.systemUTC())
mockDelegate = mock(HttpClient::class.java)
client = ExtendedHttpClient(mockDelegate, NullCache.INSTANCE, Clock.systemUTC())
}

@ParameterizedTest
@ValueSource(classes = [IOException::class, InterruptedException::class, SocketTimeoutException::class])
fun shouldPropagateExceptions(th: Class<Throwable>) {
//given
val request = HttpRequest.newBuilder().uri(URI.create("https://example.com")).build()
given(mockHttpClient.send(any(), any<BodyHandler<Any>>())).willThrow(th)
given(mockDelegate.send(any(), any<BodyHandler<Any>>())).willThrow(th)

//when + then
assertThatExceptionOfType(th)
Expand All @@ -75,7 +75,7 @@ internal class ExtendedHttpClientTest {
fun `Should throw CompletionException with cause`(th: Throwable) {
//given
val request = HttpRequest.newBuilder().uri(URI.create("https://example.com")).build()
given(mockHttpClient.send(any(), any<BodyHandler<Any>>())).willThrow(th)
given(mockDelegate.send(any(), any<BodyHandler<Any>>())).willThrow(th)


//when + then
Expand All @@ -98,17 +98,35 @@ internal class ExtendedHttpClientTest {
client.newWebSocketBuilder()

//then
val inOrder = inOrder(mockHttpClient)
inOrder.verify(mockHttpClient).cookieHandler()
inOrder.verify(mockHttpClient).connectTimeout()
inOrder.verify(mockHttpClient).followRedirects()
inOrder.verify(mockHttpClient).proxy()
inOrder.verify(mockHttpClient).sslContext()
inOrder.verify(mockHttpClient).sslParameters()
inOrder.verify(mockHttpClient).authenticator()
inOrder.verify(mockHttpClient).version()
inOrder.verify(mockHttpClient).executor()
inOrder.verify(mockHttpClient).newWebSocketBuilder()
val inOrder = inOrder(mockDelegate)
inOrder.verify(mockDelegate).cookieHandler()
inOrder.verify(mockDelegate).connectTimeout()
inOrder.verify(mockDelegate).followRedirects()
inOrder.verify(mockDelegate).proxy()
inOrder.verify(mockDelegate).sslContext()
inOrder.verify(mockDelegate).sslParameters()
inOrder.verify(mockDelegate).authenticator()
inOrder.verify(mockDelegate).version()
inOrder.verify(mockDelegate).executor()
inOrder.verify(mockDelegate).newWebSocketBuilder()
}

@Test
fun `Should not allow insecure requests`() {
//given
val mockBuilderDelegate = mock(HttpClient.Builder::class.java)
given(mockBuilderDelegate.build()).willReturn(mockDelegate)

client = Builder(mockBuilderDelegate)
.allowInsecure(false)
.build()

val request = HttpRequest.newBuilder("HTTP://abc.local".toUri()).build()

//when + then
shouldThrowExactly<IllegalArgumentException> {
client.send(request, ofString())
}
}

@Nested
Expand Down

0 comments on commit bf90fb9

Please sign in to comment.