From 957a6cb214233ec37974b10851a96719cf5e8850 Mon Sep 17 00:00:00 2001 From: Idel Pivnitskiy Date: Tue, 8 Mar 2022 22:26:58 -0800 Subject: [PATCH 1/6] Introduce `HttpProviders` and `GrpcProviders` Motivation: Allow users intercept static factories that create client/server builders to enhance builders behavior. Users can use these providers to override default values on the builders or to intercept builders for metrics purposes. Modifications: - Add `HttpProviders` and `GrpcProviders` that define provider interfaces for `ServiceLoader`; - Test how users can intercept builders; Result: Users can change the default builders using providers that are automatically loaded via `ServiceLoader`. --- .../grpc/api/DelegatingGrpcClientBuilder.java | 77 +++++ .../grpc/api/DelegatingGrpcServerBuilder.java | 86 +++++ .../servicetalk/grpc/api/GrpcProviders.java | 74 ++++ .../grpc/netty/DefaultGrpcClientBuilder.java | 1 + .../grpc/netty/DefaultGrpcServerBuilder.java | 1 + .../servicetalk/grpc/netty/GrpcClients.java | 44 ++- .../servicetalk/grpc/netty/GrpcServers.java | 37 +- .../grpc/netty/GrpcProvidersTest.java | 202 +++++++++++ ...pi.GrpcProviders$GrpcClientBuilderProvider | 1 + ...pi.GrpcProviders$GrpcServerBuilderProvider | 1 + ...pi.HttpProviders$HttpServerBuilderProvider | 1 + ...ers$SingleAddressHttpClientBuilderProvider | 1 + .../http/api/DelegatingHttpServerBuilder.java | 221 ++++++++++++ ...legatingMultiAddressHttpClientBuilder.java | 112 ++++++ ...elegatingPartitionedHttpClientBuilder.java | 137 ++++++++ ...egatingSingleAddressHttpClientBuilder.java | 236 +++++++++++++ .../servicetalk/http/api/HttpProviders.java | 104 ++++++ .../http/api/HttpServerBuilder.java | 12 +- .../http/netty/DefaultHttpServerBuilder.java | 1 + .../servicetalk/http/netty/HttpClients.java | 69 +++- .../servicetalk/http/netty/HttpServers.java | 36 +- .../http/netty/HttpProvidersTest.java | 325 ++++++++++++++++++ ...pi.HttpProviders$HttpServerBuilderProvider | 1 + ...ders$MultiAddressHttpClientBuilderProvider | 1 + ...iders$PartitionedHttpClientBuilderProvider | 1 + ...ers$SingleAddressHttpClientBuilderProvider | 1 + .../utils/internal/ServiceLoaderUtils.java | 53 +++ 27 files changed, 1799 insertions(+), 37 deletions(-) create mode 100644 servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcClientBuilder.java create mode 100644 servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcServerBuilder.java create mode 100644 servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcProviders.java create mode 100644 servicetalk-grpc-netty/src/test/java/io/servicetalk/grpc/netty/GrpcProvidersTest.java create mode 100644 servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.grpc.api.GrpcProviders$GrpcClientBuilderProvider create mode 100644 servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.grpc.api.GrpcProviders$GrpcServerBuilderProvider create mode 100644 servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$HttpServerBuilderProvider create mode 100644 servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$SingleAddressHttpClientBuilderProvider create mode 100644 servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingHttpServerBuilder.java create mode 100644 servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingMultiAddressHttpClientBuilder.java create mode 100644 servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingPartitionedHttpClientBuilder.java create mode 100644 servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingSingleAddressHttpClientBuilder.java create mode 100644 servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpProviders.java create mode 100644 servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java create mode 100644 servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$HttpServerBuilderProvider create mode 100644 servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$MultiAddressHttpClientBuilderProvider create mode 100644 servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$PartitionedHttpClientBuilderProvider create mode 100644 servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$SingleAddressHttpClientBuilderProvider create mode 100644 servicetalk-utils-internal/src/main/java/io/servicetalk/utils/internal/ServiceLoaderUtils.java diff --git a/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcClientBuilder.java b/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcClientBuilder.java new file mode 100644 index 0000000000..66f678f476 --- /dev/null +++ b/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcClientBuilder.java @@ -0,0 +1,77 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * 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 io.servicetalk.grpc.api; + +import java.time.Duration; + +import static java.util.Objects.requireNonNull; + +/** + * A {@link GrpcClientBuilder} that delegates all methods to another {@link GrpcClientBuilder}. + * + * @param the type of address before resolution (unresolved address) + * @param the type of address after resolution (resolved address) + */ +public class DelegatingGrpcClientBuilder implements GrpcClientBuilder { + + private final GrpcClientBuilder delegate; + + public DelegatingGrpcClientBuilder(final GrpcClientBuilder delegate) { + this.delegate = requireNonNull(delegate); + } + + /** + * Returns the {@link GrpcClientBuilder} delegate. + * + * @return Delegate {@link GrpcClientBuilder}. + */ + protected final GrpcClientBuilder delegate() { + return delegate; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{delegate=" + delegate() + '}'; + } + + @Override + public GrpcClientBuilder initializeHttp(final HttpInitializer initializer) { + delegate.initializeHttp(initializer); + return this; + } + + @Override + public GrpcClientBuilder defaultTimeout(final Duration defaultTimeout) { + delegate.defaultTimeout(defaultTimeout); + return this; + } + + @Override + public > Client build(final GrpcClientFactory clientFactory) { + return delegate.build(clientFactory); + } + + @Override + public > BlockingClient buildBlocking( + final GrpcClientFactory clientFactory) { + return delegate.buildBlocking(clientFactory); + } + + @Override + public MultiClientBuilder buildMulti() { + return delegate.buildMulti(); + } +} diff --git a/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcServerBuilder.java b/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcServerBuilder.java new file mode 100644 index 0000000000..cc6c7e7fb8 --- /dev/null +++ b/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcServerBuilder.java @@ -0,0 +1,86 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * 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 io.servicetalk.grpc.api; + +import io.servicetalk.concurrent.api.Single; + +import java.time.Duration; + +import static java.util.Objects.requireNonNull; + +/** + * A {@link GrpcServerBuilder} that delegates all methods to another {@link GrpcServerBuilder}. + */ +public class DelegatingGrpcServerBuilder implements GrpcServerBuilder { + + private final GrpcServerBuilder delegate; + + public DelegatingGrpcServerBuilder(final GrpcServerBuilder delegate) { + this.delegate = requireNonNull(delegate); + } + + /** + * Returns the {@link GrpcServerBuilder} delegate. + * + * @return Delegate {@link GrpcServerBuilder}. + */ + protected final GrpcServerBuilder delegate() { + return delegate; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{delegate=" + delegate() + '}'; + } + + @Override + public GrpcServerBuilder initializeHttp(final HttpInitializer initializer) { + delegate.initializeHttp(initializer); + return this; + } + + @Override + public GrpcServerBuilder defaultTimeout(final Duration defaultTimeout) { + delegate.defaultTimeout(defaultTimeout); + return this; + } + + @Override + public GrpcServerBuilder lifecycleObserver(final GrpcLifecycleObserver lifecycleObserver) { + delegate.lifecycleObserver(lifecycleObserver); + return this; + } + + @Override + public Single listen(final GrpcBindableService... services) { + return delegate.listen(services); + } + + @Override + public Single listen(final GrpcServiceFactory... serviceFactories) { + return delegate.listen(serviceFactories); + } + + @Override + public GrpcServerContext listenAndAwait(final GrpcServiceFactory... serviceFactories) throws Exception { + return delegate.listenAndAwait(serviceFactories); + } + + @Override + public GrpcServerContext listenAndAwait(final GrpcBindableService... services) throws Exception { + return delegate.listenAndAwait(services); + } +} diff --git a/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcProviders.java b/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcProviders.java new file mode 100644 index 0000000000..3c7d97c3a2 --- /dev/null +++ b/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcProviders.java @@ -0,0 +1,74 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * 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 io.servicetalk.grpc.api; + +import io.servicetalk.http.api.HttpProviders; +import io.servicetalk.http.api.HttpProviders.HttpServerBuilderProvider; +import io.servicetalk.http.api.HttpProviders.SingleAddressHttpClientBuilderProvider; + +import java.net.SocketAddress; +import java.util.ServiceLoader; + +/** + * A holder for all gRPC-specific providers that can be registered using {@link ServiceLoader}. + * + * @see HttpProviders + */ +public final class GrpcProviders { + + private GrpcProviders() { + // No instances. + } + + /** + * Provider for {@link GrpcClientBuilder}. + *

+ * An HTTP layer should use {@link SingleAddressHttpClientBuilderProvider}. + */ + @FunctionalInterface + public interface GrpcClientBuilderProvider { + + /** + * Returns a {@link GrpcClientBuilder} based on the address and pre-initialized {@link GrpcClientBuilder}. + * + * @param address a remote address used to create a {@link GrpcClientBuilder}, it can be resolved or unresolved + * based on the factory used + * @param builder pre-initialized {@link GrpcClientBuilder} + * @param the type of address before resolution (unresolved address) + * @param the type of address after resolution (resolved address) + * @return a {@link GrpcClientBuilder} based on the address and pre-initialized {@link GrpcClientBuilder}. + */ + GrpcClientBuilder newBuilder(U address, GrpcClientBuilder builder); + } + + /** + * Provider for {@link GrpcServerBuilder}. + *

+ * An HTTP layer should use {@link HttpServerBuilderProvider}. + */ + @FunctionalInterface + public interface GrpcServerBuilderProvider { + + /** + * Returns a {@link GrpcServerBuilder} based on the address and pre-initialized {@link GrpcServerBuilder}. + * + * @param address a server address used to create a {@link GrpcServerBuilder} + * @param builder pre-initialized {@link GrpcServerBuilder} + * @return a {@link GrpcServerBuilder} based on the address and pre-initialized{@link GrpcServerBuilder}. + */ + GrpcServerBuilder newBuilder(SocketAddress address, GrpcServerBuilder builder); + } +} diff --git a/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/DefaultGrpcClientBuilder.java b/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/DefaultGrpcClientBuilder.java index 7e806ac123..4141942f4f 100644 --- a/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/DefaultGrpcClientBuilder.java +++ b/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/DefaultGrpcClientBuilder.java @@ -67,6 +67,7 @@ final class DefaultGrpcClientBuilder implements GrpcClientBuilder { private final Supplier> httpClientBuilderSupplier; + // Do not use this ctor directly, GrpcClients is the entry point for creating a new builder. DefaultGrpcClientBuilder(final Supplier> httpClientBuilderSupplier) { this.httpClientBuilderSupplier = httpClientBuilderSupplier; } diff --git a/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/DefaultGrpcServerBuilder.java b/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/DefaultGrpcServerBuilder.java index 2835aa89c6..9c05026032 100644 --- a/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/DefaultGrpcServerBuilder.java +++ b/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/DefaultGrpcServerBuilder.java @@ -91,6 +91,7 @@ final class DefaultGrpcServerBuilder implements GrpcServerBuilder, ServerBinder @Nullable private Duration defaultTimeout; + // Do not use this ctor directly, GrpcServers is the entry point for creating a new builder. DefaultGrpcServerBuilder(final Supplier httpServerBuilderSupplier) { this.httpServerBuilderSupplier = () -> httpServerBuilderSupplier.get() .protocols(h2Default()).allowDropRequestTrailers(true); diff --git a/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcClients.java b/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcClients.java index c4ff1fbb45..19cdba2014 100644 --- a/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcClients.java +++ b/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcClients.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Apple Inc. and the ServiceTalk project authors + * Copyright © 2019, 2022 Apple Inc. and the ServiceTalk project authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,23 +19,47 @@ import io.servicetalk.client.api.ServiceDiscoverer; import io.servicetalk.client.api.ServiceDiscovererEvent; import io.servicetalk.grpc.api.GrpcClientBuilder; +import io.servicetalk.grpc.api.GrpcProviders.GrpcClientBuilderProvider; import io.servicetalk.http.api.HttpHeaderNames; import io.servicetalk.http.netty.HttpClients; import io.servicetalk.transport.api.HostAndPort; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.util.List; import java.util.function.Function; +import static io.servicetalk.utils.internal.ServiceLoaderUtils.loadProviders; + /** * A factory to create gRPC clients. */ public final class GrpcClients { + private static final Logger LOGGER = LoggerFactory.getLogger(GrpcClients.class); + + private static final List PROVIDERS; + private GrpcClients() { // No instances } + static { + final ClassLoader classLoader = GrpcClients.class.getClassLoader(); + PROVIDERS = loadProviders(GrpcClientBuilderProvider.class, classLoader, LOGGER); + } + + private static GrpcClientBuilder applyProviders( + final U address, GrpcClientBuilder builder) { + for (GrpcClientBuilderProvider provider : PROVIDERS) { + builder = provider.newBuilder(address, builder); + } + return builder; + } + /** * Creates a {@link GrpcClientBuilder} for an address with default {@link LoadBalancer} and DNS * {@link ServiceDiscoverer}. @@ -45,7 +69,7 @@ private GrpcClients() { * @return new builder for the address */ public static GrpcClientBuilder forAddress(final String host, final int port) { - return new DefaultGrpcClientBuilder<>(() -> HttpClients.forSingleAddress(host, port)); + return forAddress(HostAndPort.of(host, port)); } /** @@ -57,7 +81,7 @@ public static GrpcClientBuilder forAddress(final * @return new builder for the address */ public static GrpcClientBuilder forAddress(final HostAndPort address) { - return new DefaultGrpcClientBuilder<>(() -> HttpClients.forSingleAddress(address)); + return applyProviders(address, new DefaultGrpcClientBuilder<>(() -> HttpClients.forSingleAddress(address))); } /** @@ -68,7 +92,8 @@ public static GrpcClientBuilder forAddress(final * @return new builder for the address */ public static GrpcClientBuilder forServiceAddress(final String serviceName) { - return new DefaultGrpcClientBuilder<>(() -> HttpClients.forServiceAddress(serviceName)); + return applyProviders(serviceName, + new DefaultGrpcClientBuilder<>(() -> HttpClients.forServiceAddress(serviceName))); } /** @@ -80,7 +105,7 @@ public static GrpcClientBuilder forServiceAddress(fin */ public static GrpcClientBuilder forResolvedAddress(final String host, final int port) { - return new DefaultGrpcClientBuilder<>(() -> HttpClients.forResolvedAddress(host, port)); + return forResolvedAddress(HostAndPort.of(host, port)); } /** @@ -90,7 +115,7 @@ public static GrpcClientBuilder forResolvedAddre * @return new builder for the address */ public static GrpcClientBuilder forResolvedAddress(final HostAndPort address) { - return new DefaultGrpcClientBuilder<>(() -> HttpClients.forResolvedAddress(address)); + return applyProviders(address, new DefaultGrpcClientBuilder<>(() -> HttpClients.forResolvedAddress(address))); } /** @@ -101,7 +126,7 @@ public static GrpcClientBuilder forResolvedAddre */ public static GrpcClientBuilder forResolvedAddress( final InetSocketAddress address) { - return new DefaultGrpcClientBuilder<>(() -> HttpClients.forResolvedAddress(address)); + return applyProviders(address, new DefaultGrpcClientBuilder<>(() -> HttpClients.forResolvedAddress(address))); } /** @@ -118,7 +143,7 @@ public static GrpcClientBuilder forResolve * @return new builder for the address */ public static GrpcClientBuilder forResolvedAddress(final T address) { - return new DefaultGrpcClientBuilder<>(() -> HttpClients.forResolvedAddress(address)); + return applyProviders(address, new DefaultGrpcClientBuilder<>(() -> HttpClients.forResolvedAddress(address))); } /** @@ -135,6 +160,7 @@ public static GrpcClientBuilder forResolvedAddre public static GrpcClientBuilder forAddress(final ServiceDiscoverer> serviceDiscoverer, final U address) { - return new DefaultGrpcClientBuilder<>(() -> HttpClients.forSingleAddress(serviceDiscoverer, address)); + return applyProviders(address, + new DefaultGrpcClientBuilder<>(() -> HttpClients.forSingleAddress(serviceDiscoverer, address))); } } diff --git a/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcServers.java b/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcServers.java index 09e487483e..3f687d7c20 100644 --- a/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcServers.java +++ b/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcServers.java @@ -1,5 +1,5 @@ /* - * Copyright © 2019 Apple Inc. and the ServiceTalk project authors + * Copyright © 2019, 2022 Apple Inc. and the ServiceTalk project authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,37 +15,62 @@ */ package io.servicetalk.grpc.netty; +import io.servicetalk.grpc.api.GrpcProviders.GrpcServerBuilderProvider; import io.servicetalk.grpc.api.GrpcServerBuilder; import io.servicetalk.http.netty.HttpServers; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.util.List; + +import static io.servicetalk.utils.internal.ServiceLoaderUtils.loadProviders; /** * A factory to create gRPC servers. */ public final class GrpcServers { + private static final Logger LOGGER = LoggerFactory.getLogger(GrpcServers.class); + + private static final List PROVIDERS; + + static { + final ClassLoader classLoader = GrpcServers.class.getClassLoader(); + PROVIDERS = loadProviders(GrpcServerBuilderProvider.class, classLoader, LOGGER); + } + private GrpcServers() { // No instances } + private static GrpcServerBuilder applyProviders(final SocketAddress address, GrpcServerBuilder builder) { + for (GrpcServerBuilderProvider provider : PROVIDERS) { + builder = provider.newBuilder(address, builder); + } + return builder; + } + /** * New {@link GrpcServerBuilder} instance. * * @param port the listen port for the server. * @return a new builder. */ - public static GrpcServerBuilder forPort(int port) { - return new DefaultGrpcServerBuilder(() -> HttpServers.forPort(port)); + public static GrpcServerBuilder forPort(final int port) { + final InetSocketAddress address = new InetSocketAddress(port); + return forAddress(address); } /** * New {@link GrpcServerBuilder} instance. * - * @param socketAddress the listen address for the server. + * @param address the listen {@link SocketAddress} for the server. * @return a new builder. */ - public static GrpcServerBuilder forAddress(SocketAddress socketAddress) { - return new DefaultGrpcServerBuilder(() -> HttpServers.forAddress(socketAddress)); + public static GrpcServerBuilder forAddress(final SocketAddress address) { + return applyProviders(address, new DefaultGrpcServerBuilder(() -> HttpServers.forAddress(address))); } } diff --git a/servicetalk-grpc-netty/src/test/java/io/servicetalk/grpc/netty/GrpcProvidersTest.java b/servicetalk-grpc-netty/src/test/java/io/servicetalk/grpc/netty/GrpcProvidersTest.java new file mode 100644 index 0000000000..5cb645aede --- /dev/null +++ b/servicetalk-grpc-netty/src/test/java/io/servicetalk/grpc/netty/GrpcProvidersTest.java @@ -0,0 +1,202 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * 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 io.servicetalk.grpc.netty; + +import io.servicetalk.client.api.TransportObserverConnectionFactoryFilter; +import io.servicetalk.grpc.api.BlockingGrpcClient; +import io.servicetalk.grpc.api.DelegatingGrpcClientBuilder; +import io.servicetalk.grpc.api.DelegatingGrpcServerBuilder; +import io.servicetalk.grpc.api.GrpcBindableService; +import io.servicetalk.grpc.api.GrpcClientBuilder; +import io.servicetalk.grpc.api.GrpcClientFactory; +import io.servicetalk.grpc.api.GrpcProviders.GrpcClientBuilderProvider; +import io.servicetalk.grpc.api.GrpcProviders.GrpcServerBuilderProvider; +import io.servicetalk.grpc.api.GrpcServerBuilder; +import io.servicetalk.grpc.api.GrpcServerContext; +import io.servicetalk.grpc.api.GrpcServiceContext; +import io.servicetalk.http.api.HttpProviders.HttpServerBuilderProvider; +import io.servicetalk.http.api.HttpProviders.SingleAddressHttpClientBuilderProvider; +import io.servicetalk.http.api.HttpServerBuilder; +import io.servicetalk.http.api.SingleAddressHttpClientBuilder; +import io.servicetalk.transport.api.HostAndPort; +import io.servicetalk.transport.api.ServerContext; +import io.servicetalk.transport.api.TransportObserver; +import io.servicetalk.transport.netty.internal.NoopTransportObserver; + +import io.grpc.examples.helloworld.Greeter; +import io.grpc.examples.helloworld.Greeter.BlockingGreeterService; +import io.grpc.examples.helloworld.Greeter.ClientFactory; +import io.grpc.examples.helloworld.HelloReply; +import io.grpc.examples.helloworld.HelloRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static io.servicetalk.transport.netty.internal.AddressUtils.localAddress; +import static io.servicetalk.transport.netty.internal.AddressUtils.serverHostAndPort; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +class GrpcProvidersTest { + + @BeforeEach + void reset() { + TestGrpcServerBuilderProvider.reset(); + TestGrpcClientBuilderProvider.reset(); + } + + @Test + void testNoProvidersForAddress() throws Exception { + try (ServerContext serverContext = GrpcServers.forAddress(localAddress(0)) + .listenAndAwait(BlockingGreeterServiceImpl.INSTANCE); + Greeter.BlockingGreeterClient client = GrpcClients.forAddress(serverHostAndPort(serverContext)) + .buildBlocking(new ClientFactory())) { + HelloReply reply = client.sayHello(HelloRequest.newBuilder().setName("foo").build()); + assertThat(reply.getMessage(), is(equalTo("reply to foo"))); + } + } + + @Test + void testGrpcServerBuilderProvider() throws Exception { + final InetSocketAddress serverAddress = localAddress(0); + TestGrpcServerBuilderProvider.MODIFY_FOR_ADDRESS.set(serverAddress); + try (ServerContext serverContext = GrpcServers.forAddress(localAddress(0)) + .listenAndAwait(BlockingGreeterServiceImpl.INSTANCE)) { + assertThat(TestGrpcServerBuilderProvider.BUILD_COUNTER.get(), is(1)); + try (Greeter.BlockingGreeterClient client = GrpcClients.forAddress(serverHostAndPort(serverContext)) + .buildBlocking(new ClientFactory())) { + HelloReply reply = client.sayHello(HelloRequest.newBuilder().setName("foo").build()); + assertThat(reply.getMessage(), is(equalTo("reply to foo"))); + assertThat(TestGrpcServerBuilderProvider.CONNECTION_COUNTER.get(), is(1)); + } + } + } + + @Test + void testGrpcClientBuilderProvider() throws Exception { + try (ServerContext serverContext = GrpcServers.forAddress(localAddress(0)) + .listenAndAwait(BlockingGreeterServiceImpl.INSTANCE)) { + HostAndPort serverAddress = serverHostAndPort(serverContext); + TestGrpcClientBuilderProvider.MODIFY_FOR_ADDRESS.set(serverAddress); + try (Greeter.BlockingGreeterClient client = GrpcClients.forAddress(serverAddress) + .buildBlocking(new ClientFactory())) { + assertThat(TestGrpcClientBuilderProvider.BUILD_COUNTER.get(), is(1)); + HelloReply reply = client.sayHello(HelloRequest.newBuilder().setName("foo").build()); + assertThat(reply.getMessage(), is(equalTo("reply to foo"))); + assertThat(TestGrpcClientBuilderProvider.CONNECTION_COUNTER.get(), is(1)); + } + } + } + + private static final class BlockingGreeterServiceImpl implements BlockingGreeterService { + + static final BlockingGreeterService INSTANCE = new BlockingGreeterServiceImpl(); + + @Override + public HelloReply sayHello(GrpcServiceContext ctx, HelloRequest request) { + return HelloReply.newBuilder().setMessage("reply to " + request.getName()).build(); + } + } + + public static final class TestGrpcServerBuilderProvider implements GrpcServerBuilderProvider, + HttpServerBuilderProvider { + + static final AtomicReference MODIFY_FOR_ADDRESS = new AtomicReference<>(); + static final AtomicInteger BUILD_COUNTER = new AtomicInteger(); + static final AtomicInteger CONNECTION_COUNTER = new AtomicInteger(); + + static void reset() { + MODIFY_FOR_ADDRESS.set(null); + BUILD_COUNTER.set(0); + CONNECTION_COUNTER.set(0); + } + + @Override + public GrpcServerBuilder newBuilder(SocketAddress address, GrpcServerBuilder builder) { + if (address.equals(MODIFY_FOR_ADDRESS.get())) { + return new DelegatingGrpcServerBuilder(builder) { + + @Override + public GrpcServerContext listenAndAwait(GrpcBindableService... services) throws Exception { + BUILD_COUNTER.incrementAndGet(); + return delegate().listenAndAwait(services); + } + }; + } + return builder; + } + + @Override + public HttpServerBuilder newBuilder(final SocketAddress address, final HttpServerBuilder builder) { + if (address.equals(MODIFY_FOR_ADDRESS.get())) { + return builder.transportObserver(transportObserver(CONNECTION_COUNTER)); + } + return builder; + } + } + + public static final class TestGrpcClientBuilderProvider implements GrpcClientBuilderProvider, + SingleAddressHttpClientBuilderProvider { + + static final AtomicReference MODIFY_FOR_ADDRESS = new AtomicReference<>(); + static final AtomicInteger BUILD_COUNTER = new AtomicInteger(); + static final AtomicInteger CONNECTION_COUNTER = new AtomicInteger(); + + static void reset() { + MODIFY_FOR_ADDRESS.set(null); + BUILD_COUNTER.set(0); + CONNECTION_COUNTER.set(0); + } + + @Override + public GrpcClientBuilder newBuilder(U address, GrpcClientBuilder builder) { + if (address.equals(MODIFY_FOR_ADDRESS.get())) { + return new DelegatingGrpcClientBuilder(builder) { + + @Override + public > BlockingClient buildBlocking( + GrpcClientFactory clientFactory) { + BUILD_COUNTER.incrementAndGet(); + return delegate().buildBlocking(clientFactory); + } + }; + } + return builder; + } + + @Override + public SingleAddressHttpClientBuilder newBuilder(U address, + SingleAddressHttpClientBuilder builder) { + if (address.equals(MODIFY_FOR_ADDRESS.get())) { + return builder.appendConnectionFactoryFilter(new TransportObserverConnectionFactoryFilter<>( + transportObserver(CONNECTION_COUNTER))); + } + return builder; + } + } + + private static TransportObserver transportObserver(AtomicInteger counter) { + return (localAddress, remoteAddress) -> { + counter.incrementAndGet(); + return NoopTransportObserver.NoopConnectionObserver.INSTANCE; + }; + } +} diff --git a/servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.grpc.api.GrpcProviders$GrpcClientBuilderProvider b/servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.grpc.api.GrpcProviders$GrpcClientBuilderProvider new file mode 100644 index 0000000000..895924e0ca --- /dev/null +++ b/servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.grpc.api.GrpcProviders$GrpcClientBuilderProvider @@ -0,0 +1 @@ +io.servicetalk.grpc.netty.GrpcProvidersTest$TestGrpcClientBuilderProvider diff --git a/servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.grpc.api.GrpcProviders$GrpcServerBuilderProvider b/servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.grpc.api.GrpcProviders$GrpcServerBuilderProvider new file mode 100644 index 0000000000..d2e90d5925 --- /dev/null +++ b/servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.grpc.api.GrpcProviders$GrpcServerBuilderProvider @@ -0,0 +1 @@ +io.servicetalk.grpc.netty.GrpcProvidersTest$TestGrpcServerBuilderProvider diff --git a/servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$HttpServerBuilderProvider b/servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$HttpServerBuilderProvider new file mode 100644 index 0000000000..d2e90d5925 --- /dev/null +++ b/servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$HttpServerBuilderProvider @@ -0,0 +1 @@ +io.servicetalk.grpc.netty.GrpcProvidersTest$TestGrpcServerBuilderProvider diff --git a/servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$SingleAddressHttpClientBuilderProvider b/servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$SingleAddressHttpClientBuilderProvider new file mode 100644 index 0000000000..895924e0ca --- /dev/null +++ b/servicetalk-grpc-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$SingleAddressHttpClientBuilderProvider @@ -0,0 +1 @@ +io.servicetalk.grpc.netty.GrpcProvidersTest$TestGrpcClientBuilderProvider diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingHttpServerBuilder.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingHttpServerBuilder.java new file mode 100644 index 0000000000..1484bf47de --- /dev/null +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingHttpServerBuilder.java @@ -0,0 +1,221 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * 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 io.servicetalk.http.api; + +import io.servicetalk.buffer.api.BufferAllocator; +import io.servicetalk.concurrent.api.Executor; +import io.servicetalk.concurrent.api.Single; +import io.servicetalk.logging.api.LogLevel; +import io.servicetalk.transport.api.ConnectionAcceptorFactory; +import io.servicetalk.transport.api.IoExecutor; +import io.servicetalk.transport.api.ServerSslConfig; +import io.servicetalk.transport.api.TransportObserver; + +import java.net.SocketOption; +import java.util.Map; +import java.util.function.BooleanSupplier; +import java.util.function.Predicate; + +import static java.util.Objects.requireNonNull; + +/** + * A {@link HttpServerBuilder} that delegates all methods to another {@link HttpServerBuilder}. + */ +public class DelegatingHttpServerBuilder implements HttpServerBuilder { + + private final HttpServerBuilder delegate; + + /** + * Create a new instance. + * + * @param delegate {@link HttpServerBuilder} to which all methods are delegated. + */ + public DelegatingHttpServerBuilder(final HttpServerBuilder delegate) { + this.delegate = requireNonNull(delegate); + } + + /** + * Returns the {@link HttpServerBuilder} delegate. + * + * @return Delegate {@link HttpServerBuilder}. + */ + protected final HttpServerBuilder delegate() { + return delegate; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{delegate=" + delegate() + '}'; + } + + @Override + public HttpServerBuilder protocols(final HttpProtocolConfig... protocols) { + delegate.protocols(protocols); + return this; + } + + @Override + public HttpServerBuilder sslConfig(final ServerSslConfig config) { + delegate.sslConfig(config); + return this; + } + + @Override + public HttpServerBuilder sslConfig(final ServerSslConfig defaultConfig, final Map sniMap) { + delegate.sslConfig(defaultConfig, sniMap); + return this; + } + + @Override + public HttpServerBuilder socketOption(final SocketOption option, final T value) { + delegate.socketOption(option, value); + return this; + } + + @Override + public HttpServerBuilder listenSocketOption(final SocketOption option, final T value) { + delegate.listenSocketOption(option, value); + return this; + } + + @Override + public HttpServerBuilder enableWireLogging(final String loggerName, final LogLevel logLevel, + final BooleanSupplier logUserData) { + delegate.enableWireLogging(loggerName, logLevel, logUserData); + return this; + } + + @Override + public HttpServerBuilder transportObserver(final TransportObserver transportObserver) { + delegate.transportObserver(transportObserver); + return this; + } + + @Override + public HttpServerBuilder lifecycleObserver(final HttpLifecycleObserver lifecycleObserver) { + delegate.lifecycleObserver(lifecycleObserver); + return this; + } + + @Override + public HttpServerBuilder drainRequestPayloadBody(final boolean enable) { + delegate.drainRequestPayloadBody(enable); + return this; + } + + @Override + public HttpServerBuilder allowDropRequestTrailers(final boolean allowDrop) { + delegate.allowDropRequestTrailers(allowDrop); + return this; + } + + @Override + public HttpServerBuilder appendConnectionAcceptorFilter(final ConnectionAcceptorFactory factory) { + delegate.appendConnectionAcceptorFilter(factory); + return this; + } + + @Override + public HttpServerBuilder appendNonOffloadingServiceFilter(final StreamingHttpServiceFilterFactory factory) { + delegate.appendNonOffloadingServiceFilter(factory); + return this; + } + + @Override + public HttpServerBuilder appendNonOffloadingServiceFilter(final Predicate predicate, + final StreamingHttpServiceFilterFactory factory) { + delegate.appendNonOffloadingServiceFilter(predicate, factory); + return this; + } + + @Override + public HttpServerBuilder appendServiceFilter(final StreamingHttpServiceFilterFactory factory) { + delegate.appendServiceFilter(factory); + return this; + } + + @Override + public HttpServerBuilder appendServiceFilter(final Predicate predicate, + final StreamingHttpServiceFilterFactory factory) { + delegate.appendServiceFilter(predicate, factory); + return this; + } + + @Override + public HttpServerBuilder ioExecutor(final IoExecutor ioExecutor) { + delegate.ioExecutor(ioExecutor); + return this; + } + + @Override + public HttpServerBuilder executor(final Executor executor) { + delegate.executor(executor); + return this; + } + + @Override + public HttpServerBuilder bufferAllocator(final BufferAllocator allocator) { + delegate.bufferAllocator(allocator); + return this; + } + + @Override + public HttpServerBuilder executionStrategy(final HttpExecutionStrategy strategy) { + delegate.executionStrategy(strategy); + return this; + } + + @Override + public Single listen(final HttpService service) { + return delegate.listen(service); + } + + @Override + public Single listenStreaming(final StreamingHttpService service) { + return delegate.listenStreaming(service); + } + + @Override + public Single listenBlocking(final BlockingHttpService service) { + return delegate.listenBlocking(service); + } + + @Override + public Single listenBlockingStreaming(final BlockingStreamingHttpService service) { + return delegate.listenBlockingStreaming(service); + } + + @Override + public HttpServerContext listenAndAwait(final HttpService service) throws Exception { + return delegate.listenAndAwait(service); + } + + @Override + public HttpServerContext listenStreamingAndAwait(final StreamingHttpService service) throws Exception { + return delegate.listenStreamingAndAwait(service); + } + + @Override + public HttpServerContext listenBlockingAndAwait(final BlockingHttpService service) throws Exception { + return delegate.listenBlockingAndAwait(service); + } + + @Override + public HttpServerContext listenBlockingStreamingAndAwait(final BlockingStreamingHttpService service) + throws Exception { + return delegate.listenBlockingStreamingAndAwait(service); + } +} diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingMultiAddressHttpClientBuilder.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingMultiAddressHttpClientBuilder.java new file mode 100644 index 0000000000..0ead9b8ff8 --- /dev/null +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingMultiAddressHttpClientBuilder.java @@ -0,0 +1,112 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * 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 io.servicetalk.http.api; + +import io.servicetalk.buffer.api.BufferAllocator; +import io.servicetalk.concurrent.api.Executor; +import io.servicetalk.transport.api.IoExecutor; + +import static java.util.Objects.requireNonNull; + +/** + * A {@link MultiAddressHttpClientBuilder} that delegates all methods to another {@link MultiAddressHttpClientBuilder}. + * + * @param the type of address before resolution (unresolved address) + * @param the type of address after resolution (resolved address) + */ +public class DelegatingMultiAddressHttpClientBuilder implements MultiAddressHttpClientBuilder { + + private final MultiAddressHttpClientBuilder delegate; + + /** + * Create a new instance. + * + * @param delegate {@link MultiAddressHttpClientBuilder} to which all methods are delegated. + */ + public DelegatingMultiAddressHttpClientBuilder(final MultiAddressHttpClientBuilder delegate) { + this.delegate = requireNonNull(delegate); + } + + /** + * Returns the {@link MultiAddressHttpClientBuilder} delegate. + * + * @return Delegate {@link MultiAddressHttpClientBuilder}. + */ + protected final MultiAddressHttpClientBuilder delegate() { + return delegate; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{delegate=" + delegate() + '}'; + } + + @Override + public MultiAddressHttpClientBuilder ioExecutor(final IoExecutor ioExecutor) { + delegate.ioExecutor(ioExecutor); + return this; + } + + @Override + public MultiAddressHttpClientBuilder executor(final Executor executor) { + delegate.executor(executor); + return this; + } + + @Override + public MultiAddressHttpClientBuilder executionStrategy(final HttpExecutionStrategy strategy) { + delegate.executionStrategy(strategy); + return this; + } + + @Override + public MultiAddressHttpClientBuilder bufferAllocator(final BufferAllocator allocator) { + delegate.bufferAllocator(allocator); + return this; + } + + @Override + public MultiAddressHttpClientBuilder initializer(final SingleAddressInitializer initializer) { + delegate.initializer(initializer); + return this; + } + + @Override + public MultiAddressHttpClientBuilder followRedirects(final RedirectConfig config) { + delegate.followRedirects(config); + return this; + } + + @Override + public HttpClient build() { + return delegate.build(); + } + + @Override + public StreamingHttpClient buildStreaming() { + return delegate.buildStreaming(); + } + + @Override + public BlockingHttpClient buildBlocking() { + return delegate.buildBlocking(); + } + + @Override + public BlockingStreamingHttpClient buildBlockingStreaming() { + return delegate.buildBlockingStreaming(); + } +} diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingPartitionedHttpClientBuilder.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingPartitionedHttpClientBuilder.java new file mode 100644 index 0000000000..a785905b47 --- /dev/null +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingPartitionedHttpClientBuilder.java @@ -0,0 +1,137 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * 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 io.servicetalk.http.api; + +import io.servicetalk.buffer.api.BufferAllocator; +import io.servicetalk.client.api.ServiceDiscoverer; +import io.servicetalk.client.api.partition.PartitionMapFactory; +import io.servicetalk.client.api.partition.PartitionedServiceDiscovererEvent; +import io.servicetalk.concurrent.api.BiIntFunction; +import io.servicetalk.concurrent.api.Completable; +import io.servicetalk.concurrent.api.Executor; +import io.servicetalk.transport.api.IoExecutor; + +import static java.util.Objects.requireNonNull; + +/** + * A {@link PartitionedHttpClientBuilder} that delegates all methods to another {@link PartitionedHttpClientBuilder}. + * + * @param the type of address before resolution (unresolved address) + * @param the type of address after resolution (resolved address) + */ +public class DelegatingPartitionedHttpClientBuilder implements PartitionedHttpClientBuilder { + + private final PartitionedHttpClientBuilder delegate; + + /** + * Create a new instance. + * + * @param delegate {@link PartitionedHttpClientBuilder} to which all methods are delegated. + */ + public DelegatingPartitionedHttpClientBuilder(final PartitionedHttpClientBuilder delegate) { + this.delegate = requireNonNull(delegate); + } + + /** + * Returns the {@link PartitionedHttpClientBuilder} delegate. + * + * @return Delegate {@link PartitionedHttpClientBuilder}. + */ + protected final PartitionedHttpClientBuilder delegate() { + return delegate; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{delegate=" + delegate() + '}'; + } + + @Override + public PartitionedHttpClientBuilder ioExecutor(final IoExecutor ioExecutor) { + delegate.ioExecutor(ioExecutor); + return this; + } + + @Override + public PartitionedHttpClientBuilder executor(final Executor executor) { + delegate.executor(executor); + return this; + } + + @Override + public PartitionedHttpClientBuilder executionStrategy(final HttpExecutionStrategy strategy) { + delegate.executionStrategy(strategy); + return this; + } + + @Override + public PartitionedHttpClientBuilder bufferAllocator(final BufferAllocator allocator) { + delegate.bufferAllocator(allocator); + return this; + } + + @Override + public PartitionedHttpClientBuilder serviceDiscoverer( + final ServiceDiscoverer> serviceDiscoverer) { + delegate.serviceDiscoverer(serviceDiscoverer); + return this; + } + + @Override + public PartitionedHttpClientBuilder retryServiceDiscoveryErrors( + final BiIntFunction retryStrategy) { + delegate.retryServiceDiscoveryErrors(retryStrategy); + return this; + } + + @Override + public PartitionedHttpClientBuilder serviceDiscoveryMaxQueueSize(final int serviceDiscoveryMaxQueueSize) { + delegate.serviceDiscoveryMaxQueueSize(serviceDiscoveryMaxQueueSize); + return this; + } + + @Override + public PartitionedHttpClientBuilder partitionMapFactory(final PartitionMapFactory partitionMapFactory) { + delegate.partitionMapFactory(partitionMapFactory); + return this; + } + + @Override + public PartitionedHttpClientBuilder initializer(final SingleAddressInitializer initializer) { + delegate.initializer(initializer); + return this; + } + + @Override + public HttpClient build() { + return delegate.build(); + } + + @Override + public StreamingHttpClient buildStreaming() { + return delegate.buildStreaming(); + } + + @Override + public BlockingHttpClient buildBlocking() { + return delegate.buildBlocking(); + } + + @Override + public BlockingStreamingHttpClient buildBlockingStreaming() { + return delegate.buildBlockingStreaming(); + } +} diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingSingleAddressHttpClientBuilder.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingSingleAddressHttpClientBuilder.java new file mode 100644 index 0000000000..9cc98e7b42 --- /dev/null +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingSingleAddressHttpClientBuilder.java @@ -0,0 +1,236 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * 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 io.servicetalk.http.api; + +import io.servicetalk.buffer.api.BufferAllocator; +import io.servicetalk.client.api.ConnectionFactoryFilter; +import io.servicetalk.client.api.ServiceDiscoverer; +import io.servicetalk.client.api.ServiceDiscovererEvent; +import io.servicetalk.concurrent.api.BiIntFunction; +import io.servicetalk.concurrent.api.Completable; +import io.servicetalk.concurrent.api.Executor; +import io.servicetalk.logging.api.LogLevel; +import io.servicetalk.transport.api.ClientSslConfig; +import io.servicetalk.transport.api.IoExecutor; + +import java.net.SocketOption; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.Predicate; + +import static java.util.Objects.requireNonNull; + +/** + * A {@link SingleAddressHttpClientBuilder} that delegates all methods to another + * {@link SingleAddressHttpClientBuilder}. + * + * @param the type of address before resolution (unresolved address) + * @param the type of address after resolution (resolved address) + */ +public class DelegatingSingleAddressHttpClientBuilder implements SingleAddressHttpClientBuilder { + + private final SingleAddressHttpClientBuilder delegate; + + /** + * Create a new instance. + * + * @param delegate {@link SingleAddressHttpClientBuilder} to which all methods are delegated. + */ + public DelegatingSingleAddressHttpClientBuilder(final SingleAddressHttpClientBuilder delegate) { + this.delegate = requireNonNull(delegate); + } + + /** + * Returns the {@link SingleAddressHttpClientBuilder} delegate. + * + * @return Delegate {@link SingleAddressHttpClientBuilder}. + */ + protected final SingleAddressHttpClientBuilder delegate() { + return delegate; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{delegate=" + delegate() + '}'; + } + + @Override + public SingleAddressHttpClientBuilder proxyAddress(final U proxyAddress) { + delegate.proxyAddress(proxyAddress); + return this; + } + + @Override + public SingleAddressHttpClientBuilder socketOption(final SocketOption option, final T value) { + delegate.socketOption(option, value); + return this; + } + + @Override + public SingleAddressHttpClientBuilder enableWireLogging(final String loggerName, final LogLevel logLevel, + final BooleanSupplier logUserData) { + delegate.enableWireLogging(loggerName, logLevel, logUserData); + return this; + } + + @Override + public SingleAddressHttpClientBuilder protocols(final HttpProtocolConfig... protocols) { + delegate.protocols(protocols); + return this; + } + + @Override + public SingleAddressHttpClientBuilder hostHeaderFallback(final boolean enable) { + delegate.hostHeaderFallback(enable); + return this; + } + + @Override + public SingleAddressHttpClientBuilder allowDropResponseTrailers(final boolean allowDrop) { + delegate.allowDropResponseTrailers(allowDrop); + return this; + } + + @Override + public SingleAddressHttpClientBuilder appendConnectionFilter( + final StreamingHttpConnectionFilterFactory factory) { + delegate.appendConnectionFilter(factory); + return this; + } + + @Override + public SingleAddressHttpClientBuilder appendConnectionFilter( + final Predicate predicate, final StreamingHttpConnectionFilterFactory factory) { + delegate.appendConnectionFilter(predicate, factory); + return this; + } + + @Override + public SingleAddressHttpClientBuilder ioExecutor(final IoExecutor ioExecutor) { + delegate.ioExecutor(ioExecutor); + return this; + } + + @Override + public SingleAddressHttpClientBuilder executor(final Executor executor) { + delegate.executor(executor); + return this; + } + + @Override + public SingleAddressHttpClientBuilder executionStrategy(final HttpExecutionStrategy strategy) { + delegate.executionStrategy(strategy); + return this; + } + + @Override + public SingleAddressHttpClientBuilder bufferAllocator(final BufferAllocator allocator) { + delegate.bufferAllocator(allocator); + return this; + } + + @Override + public SingleAddressHttpClientBuilder appendConnectionFactoryFilter( + final ConnectionFactoryFilter factory) { + delegate.appendConnectionFactoryFilter(factory); + return this; + } + + @Override + public SingleAddressHttpClientBuilder appendClientFilter(final StreamingHttpClientFilterFactory factory) { + delegate.appendClientFilter(factory); + return this; + } + + @Override + public SingleAddressHttpClientBuilder appendClientFilter(final Predicate predicate, + final StreamingHttpClientFilterFactory factory) { + delegate.appendClientFilter(predicate, factory); + return this; + } + + @Override + public SingleAddressHttpClientBuilder unresolvedAddressToHost( + final Function unresolvedAddressToHostFunction) { + delegate.unresolvedAddressToHost(unresolvedAddressToHostFunction); + return this; + } + + @Override + public SingleAddressHttpClientBuilder serviceDiscoverer( + final ServiceDiscoverer> serviceDiscoverer) { + delegate.serviceDiscoverer(serviceDiscoverer); + return this; + } + + @Override + public SingleAddressHttpClientBuilder retryServiceDiscoveryErrors( + final BiIntFunction retryStrategy) { + delegate.retryServiceDiscoveryErrors(retryStrategy); + return this; + } + + @Override + public SingleAddressHttpClientBuilder loadBalancerFactory( + final HttpLoadBalancerFactory loadBalancerFactory) { + delegate.loadBalancerFactory(loadBalancerFactory); + return this; + } + + @Override + public SingleAddressHttpClientBuilder sslConfig(final ClientSslConfig sslConfig) { + delegate.sslConfig(sslConfig); + return this; + } + + @Override + public SingleAddressHttpClientBuilder inferPeerHost(final boolean shouldInfer) { + delegate.inferPeerHost(shouldInfer); + return this; + } + + @Override + public SingleAddressHttpClientBuilder inferPeerPort(final boolean shouldInfer) { + delegate.inferPeerPort(shouldInfer); + return this; + } + + @Override + public SingleAddressHttpClientBuilder inferSniHostname(final boolean shouldInfer) { + delegate.inferSniHostname(shouldInfer); + return this; + } + + @Override + public HttpClient build() { + return delegate.build(); + } + + @Override + public StreamingHttpClient buildStreaming() { + return delegate.buildStreaming(); + } + + @Override + public BlockingHttpClient buildBlocking() { + return delegate.buildBlocking(); + } + + @Override + public BlockingStreamingHttpClient buildBlockingStreaming() { + return delegate.buildBlockingStreaming(); + } +} diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpProviders.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpProviders.java new file mode 100644 index 0000000000..1acd59a4f3 --- /dev/null +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpProviders.java @@ -0,0 +1,104 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * 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 io.servicetalk.http.api; + +import java.net.SocketAddress; +import java.util.ServiceLoader; + +/** + * A holder for all HTTP-specific providers that can be registered using {@link ServiceLoader}. + */ +public final class HttpProviders { + + private HttpProviders() { + // No instances. + } + + /** + * Provider for {@link SingleAddressHttpClientBuilder}. + */ + @FunctionalInterface + public interface SingleAddressHttpClientBuilderProvider { + + /** + * Returns a {@link SingleAddressHttpClientBuilder} based on the address and pre-initialized + * {@link SingleAddressHttpClientBuilder}. + * + * @param address a remote address used to create a {@link SingleAddressHttpClientBuilder}, it can be resolved + * or unresolved based on the factory used + * @param builder pre-initialized {@link SingleAddressHttpClientBuilder} + * @param the type of address before resolution (unresolved address) + * @param the type of address after resolution (resolved address) + * @return a {@link SingleAddressHttpClientBuilder} based on the address and pre-initialized + * {@link SingleAddressHttpClientBuilder}. + */ + SingleAddressHttpClientBuilder newBuilder(U address, SingleAddressHttpClientBuilder builder); + } + + /** + * Provider for {@link MultiAddressHttpClientBuilder}. + */ + @FunctionalInterface + public interface MultiAddressHttpClientBuilderProvider { + + /** + * Returns a {@link MultiAddressHttpClientBuilder} based on the pre-initialized + * {@link MultiAddressHttpClientBuilder}. + * + * @param builder pre-initialized {@link MultiAddressHttpClientBuilder} + * @param the type of address before resolution (unresolved address) + * @param the type of address after resolution (resolved address) + * @return a {@link MultiAddressHttpClientBuilder} based on the pre-initialized + * {@link MultiAddressHttpClientBuilder}. + */ + MultiAddressHttpClientBuilder newBuilder(MultiAddressHttpClientBuilder builder); + } + + /** + * Provider for {@link PartitionedHttpClientBuilder}. + */ + @FunctionalInterface + public interface PartitionedHttpClientBuilderProvider { + + /** + * Returns a {@link PartitionedHttpClientBuilder} based on the pre-initialized + * {@link PartitionedHttpClientBuilder}. + * + * @param builder pre-initialized {@link PartitionedHttpClientBuilder} + * @param the type of address before resolution (unresolved address) + * @param the type of address after resolution (resolved address) + * @return a {@link PartitionedHttpClientBuilder} based on the pre-initialized + * {@link PartitionedHttpClientBuilder}. + */ + PartitionedHttpClientBuilder newBuilder(PartitionedHttpClientBuilder builder); + } + + /** + * Provider for {@link HttpServerBuilder}. + */ + @FunctionalInterface + public interface HttpServerBuilderProvider { + + /** + * Returns a {@link HttpServerBuilder} based on the address and pre-initialized {@link HttpServerBuilder}. + * + * @param address a server address used to create a {@link HttpServerBuilder} + * @param builder pre-initialized {@link HttpServerBuilder} + * @return a {@link HttpServerBuilder} based on the address and pre-initialized{@link HttpServerBuilder}. + */ + HttpServerBuilder newBuilder(SocketAddress address, HttpServerBuilder builder); + } +} diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpServerBuilder.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpServerBuilder.java index cf956c0747..5848a140cc 100644 --- a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpServerBuilder.java +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpServerBuilder.java @@ -334,14 +334,14 @@ default HttpServerContext listenAndAwait(HttpService service) throws Exception { *

* If the underlying protocol (e.g. TCP) supports it this will result in a socket bind/listen on {@code address}. * - * @param handler Service invoked for every request received by this server. The returned {@link HttpServerContext} + * @param service Service invoked for every request received by this server. The returned {@link HttpServerContext} * manages the lifecycle of the {@code service}, ensuring it is closed when the {@link HttpServerContext} is closed. * @return A {@link HttpServerContext} by blocking the calling thread until the server is successfully started or * throws an {@link Exception} if the server could not be started. * @throws Exception if the server could not be started. */ - default HttpServerContext listenStreamingAndAwait(StreamingHttpService handler) throws Exception { - return blockingInvocation(listenStreaming(handler)); + default HttpServerContext listenStreamingAndAwait(StreamingHttpService service) throws Exception { + return blockingInvocation(listenStreaming(service)); } /** @@ -364,14 +364,14 @@ default HttpServerContext listenBlockingAndAwait(BlockingHttpService service) th *

* If the underlying protocol (e.g. TCP) supports it this will result in a socket bind/listen on {@code address}. * - * @param handler Service invoked for every request received by this server. The returned {@link HttpServerContext} + * @param service Service invoked for every request received by this server. The returned {@link HttpServerContext} * manages the lifecycle of the {@code service}, ensuring it is closed when the {@link HttpServerContext} is closed. * @return A {@link HttpServerContext} by blocking the calling thread until the server is successfully started or * throws an {@link Exception} if the server could not be started. * @throws Exception if the server could not be started. */ - default HttpServerContext listenBlockingStreamingAndAwait(BlockingStreamingHttpService handler) throws Exception { - return blockingInvocation(listenBlockingStreaming(handler)); + default HttpServerContext listenBlockingStreamingAndAwait(BlockingStreamingHttpService service) throws Exception { + return blockingInvocation(listenBlockingStreaming(service)); } /** diff --git a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/DefaultHttpServerBuilder.java b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/DefaultHttpServerBuilder.java index 38f01697be..b48af98221 100644 --- a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/DefaultHttpServerBuilder.java +++ b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/DefaultHttpServerBuilder.java @@ -84,6 +84,7 @@ final class DefaultHttpServerBuilder implements HttpServerBuilder { private final HttpExecutionContextBuilder executionContextBuilder = new HttpExecutionContextBuilder(); private final SocketAddress address; + // Do not use this ctor directly, HttpServers is the entry point for creating a new builder. DefaultHttpServerBuilder(SocketAddress address) { appendNonOffloadingServiceFilter(ClearAsyncContextHttpServiceFilter.CLEAR_ASYNC_CONTEXT_HTTP_SERVICE_FILTER); this.address = address; diff --git a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java index f33ebe2f5e..23dc561fef 100644 --- a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java +++ b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java @@ -27,6 +27,9 @@ import io.servicetalk.concurrent.api.Publisher; import io.servicetalk.http.api.HttpClient; import io.servicetalk.http.api.HttpHeaderNames; +import io.servicetalk.http.api.HttpProviders.MultiAddressHttpClientBuilderProvider; +import io.servicetalk.http.api.HttpProviders.PartitionedHttpClientBuilderProvider; +import io.servicetalk.http.api.HttpProviders.SingleAddressHttpClientBuilderProvider; import io.servicetalk.http.api.HttpRequestMetaData; import io.servicetalk.http.api.MultiAddressHttpClientBuilder; import io.servicetalk.http.api.MultiAddressHttpClientBuilder.SingleAddressInitializer; @@ -36,9 +39,13 @@ import io.servicetalk.transport.api.HostAndPort; import io.servicetalk.transport.netty.internal.BuilderUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Collection; +import java.util.List; import java.util.function.Function; import static io.servicetalk.concurrent.api.AsyncCloseables.emptyAsyncCloseable; @@ -46,6 +53,7 @@ import static io.servicetalk.http.netty.GlobalDnsServiceDiscoverer.globalDnsServiceDiscoverer; import static io.servicetalk.http.netty.GlobalDnsServiceDiscoverer.globalSrvDnsServiceDiscoverer; import static io.servicetalk.http.netty.GlobalDnsServiceDiscoverer.mappingServiceDiscoverer; +import static io.servicetalk.utils.internal.ServiceLoaderUtils.loadProviders; import static java.util.function.Function.identity; /** @@ -53,10 +61,47 @@ */ public final class HttpClients { + private static final Logger LOGGER = LoggerFactory.getLogger(HttpClients.class); + + private static final List SINGLE_ADDRESS_PROVIDERS; + private static final List MULTI_ADDRESS_PROVIDERS; + private static final List PARTITIONED_PROVIDERS; + + static { + final ClassLoader classLoader = HttpClients.class.getClassLoader(); + SINGLE_ADDRESS_PROVIDERS = loadProviders(SingleAddressHttpClientBuilderProvider.class, classLoader, LOGGER); + MULTI_ADDRESS_PROVIDERS = loadProviders(MultiAddressHttpClientBuilderProvider.class, classLoader, LOGGER); + PARTITIONED_PROVIDERS = loadProviders(PartitionedHttpClientBuilderProvider.class, classLoader, LOGGER); + } + private HttpClients() { // No instances } + private static SingleAddressHttpClientBuilder applyProviders( + final U address, SingleAddressHttpClientBuilder builder) { + for (SingleAddressHttpClientBuilderProvider provider : SINGLE_ADDRESS_PROVIDERS) { + builder = provider.newBuilder(address, builder); + } + return builder; + } + + private static MultiAddressHttpClientBuilder applyProviders( + MultiAddressHttpClientBuilder builder) { + for (MultiAddressHttpClientBuilderProvider provider : MULTI_ADDRESS_PROVIDERS) { + builder = provider.newBuilder(builder); + } + return builder; + } + + private static PartitionedHttpClientBuilder applyProviders( + PartitionedHttpClientBuilder builder) { + for (PartitionedHttpClientBuilderProvider provider : PARTITIONED_PROVIDERS) { + builder = provider.newBuilder(builder); + } + return builder; + } + /** * Creates a {@link MultiAddressHttpClientBuilder} for clients capable of parsing an absolute-form URL, connecting to multiple addresses @@ -69,7 +114,7 @@ private HttpClients() { * @return new builder with default configuration */ public static MultiAddressHttpClientBuilder forMultiAddressUrl() { - return new DefaultMultiAddressUrlHttpClientBuilder(HttpClients::forSingleAddress); + return applyProviders(new DefaultMultiAddressUrlHttpClientBuilder(HttpClients::forSingleAddress)); } /** @@ -93,7 +138,8 @@ public static MultiAddressHttpClientBuilder forM public static MultiAddressHttpClientBuilder forMultiAddressUrl( final ServiceDiscoverer> serviceDiscoverer) { - return new DefaultMultiAddressUrlHttpClientBuilder(address -> forSingleAddress(serviceDiscoverer, address)); + return applyProviders( + new DefaultMultiAddressUrlHttpClientBuilder(address -> forSingleAddress(serviceDiscoverer, address))); } /** @@ -124,7 +170,8 @@ public static SingleAddressHttpClientBuilder for */ public static SingleAddressHttpClientBuilder forSingleAddress( final HostAndPort address) { - return new DefaultSingleAddressHttpClientBuilder<>(address, globalDnsServiceDiscoverer()); + return applyProviders(address, + new DefaultSingleAddressHttpClientBuilder<>(address, globalDnsServiceDiscoverer())); } /** @@ -137,7 +184,8 @@ public static SingleAddressHttpClientBuilder for */ public static SingleAddressHttpClientBuilder forServiceAddress( final String serviceName) { - return new DefaultSingleAddressHttpClientBuilder<>(serviceName, globalSrvDnsServiceDiscoverer()); + return applyProviders(serviceName, + new DefaultSingleAddressHttpClientBuilder<>(serviceName, globalSrvDnsServiceDiscoverer())); } /** @@ -174,8 +222,8 @@ public static SingleAddressHttpClientBuilder for */ public static SingleAddressHttpClientBuilder forResolvedAddress( final HostAndPort address) { - return new DefaultSingleAddressHttpClientBuilder<>(address, - mappingServiceDiscoverer(BuilderUtils::toResolvedInetSocketAddress)); + return applyProviders(address, new DefaultSingleAddressHttpClientBuilder<>(address, + mappingServiceDiscoverer(BuilderUtils::toResolvedInetSocketAddress))); } /** @@ -189,7 +237,8 @@ public static SingleAddressHttpClientBuilder for * @return new builder for the address */ public static SingleAddressHttpClientBuilder forResolvedAddress(final R address) { - return new DefaultSingleAddressHttpClientBuilder<>(address, mappingServiceDiscoverer(identity())); + return applyProviders(address, + new DefaultSingleAddressHttpClientBuilder<>(address, mappingServiceDiscoverer(identity()))); } /** @@ -209,7 +258,7 @@ public static SingleAddressHttpClientBuilder for public static SingleAddressHttpClientBuilder forSingleAddress( final ServiceDiscoverer> serviceDiscoverer, final U address) { - return new DefaultSingleAddressHttpClientBuilder<>(address, serviceDiscoverer); + return applyProviders(address, new DefaultSingleAddressHttpClientBuilder<>(address, serviceDiscoverer)); } /** @@ -237,7 +286,7 @@ public static PartitionedHttpClientBuilder forPartitionedAddress( final ServiceDiscoverer> serviceDiscoverer, final U address, final Function partitionAttributesBuilderFactory) { - return new DefaultPartitionedHttpClientBuilder<>(address, + return applyProviders(new DefaultPartitionedHttpClientBuilder<>(address, () -> forSingleAddress( new ServiceDiscoverer>() { private final ListenableAsyncCloseable closeable = emptyAsyncCloseable(); @@ -261,6 +310,6 @@ public Completable closeAsync() { public Completable closeAsyncGracefully() { return closeable.closeAsyncGracefully(); } - }, address), serviceDiscoverer, partitionAttributesBuilderFactory); + }, address), serviceDiscoverer, partitionAttributesBuilderFactory)); } } diff --git a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpServers.java b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpServers.java index f9afd85386..ccea34aa90 100644 --- a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpServers.java +++ b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpServers.java @@ -1,5 +1,5 @@ /* - * Copyright © 2018 Apple Inc. and the ServiceTalk project authors + * Copyright © 2018, 2022 Apple Inc. and the ServiceTalk project authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,38 +15,62 @@ */ package io.servicetalk.http.netty; +import io.servicetalk.http.api.HttpProviders.HttpServerBuilderProvider; import io.servicetalk.http.api.HttpServerBuilder; import io.servicetalk.transport.api.ServerContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.util.List; + +import static io.servicetalk.utils.internal.ServiceLoaderUtils.loadProviders; /** * Factory methods for building HTTP Servers backed by {@link ServerContext}. */ public final class HttpServers { + private static final Logger LOGGER = LoggerFactory.getLogger(HttpServers.class); + + private static final List PROVIDERS; + + static { + final ClassLoader classLoader = HttpServers.class.getClassLoader(); + PROVIDERS = loadProviders(HttpServerBuilderProvider.class, classLoader, LOGGER); + } + private HttpServers() { // No instances } + private static HttpServerBuilder applyProviders(final SocketAddress address, HttpServerBuilder builder) { + for (HttpServerBuilderProvider provider : PROVIDERS) { + builder = provider.newBuilder(address, builder); + } + return builder; + } + /** * New {@link HttpServerBuilder} instance. * * @param port The listen port for the server. * @return a new builder. */ - public static HttpServerBuilder forPort(int port) { - return new DefaultHttpServerBuilder(new InetSocketAddress(port)); + public static HttpServerBuilder forPort(final int port) { + final InetSocketAddress address = new InetSocketAddress(port); + return applyProviders(address, new DefaultHttpServerBuilder(address)); } /** * New {@link HttpServerBuilder} instance. * - * @param socketAddress The listen address for the server. + * @param address The listen {@link SocketAddress} for the server. * @return a new builder. */ - public static HttpServerBuilder forAddress(SocketAddress socketAddress) { - return new DefaultHttpServerBuilder(socketAddress); + public static HttpServerBuilder forAddress(final SocketAddress address) { + return applyProviders(address, new DefaultHttpServerBuilder(address)); } } diff --git a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java new file mode 100644 index 0000000000..621dc854c7 --- /dev/null +++ b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java @@ -0,0 +1,325 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * 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 io.servicetalk.http.netty; + +import io.servicetalk.client.api.ServiceDiscoverer; +import io.servicetalk.client.api.TransportObserverConnectionFactoryFilter; +import io.servicetalk.client.api.internal.partition.DefaultPartitionAttributesBuilder; +import io.servicetalk.client.api.partition.PartitionAttributes; +import io.servicetalk.client.api.partition.PartitionAttributesBuilder; +import io.servicetalk.client.api.partition.PartitionedServiceDiscovererEvent; +import io.servicetalk.concurrent.api.Publisher; +import io.servicetalk.http.api.BlockingHttpClient; +import io.servicetalk.http.api.DelegatingHttpServerBuilder; +import io.servicetalk.http.api.DelegatingMultiAddressHttpClientBuilder; +import io.servicetalk.http.api.DelegatingPartitionedHttpClientBuilder; +import io.servicetalk.http.api.DelegatingSingleAddressHttpClientBuilder; +import io.servicetalk.http.api.HttpProviders.HttpServerBuilderProvider; +import io.servicetalk.http.api.HttpProviders.MultiAddressHttpClientBuilderProvider; +import io.servicetalk.http.api.HttpProviders.PartitionedHttpClientBuilderProvider; +import io.servicetalk.http.api.HttpProviders.SingleAddressHttpClientBuilderProvider; +import io.servicetalk.http.api.HttpRequestMetaData; +import io.servicetalk.http.api.HttpResponse; +import io.servicetalk.http.api.HttpServerBuilder; +import io.servicetalk.http.api.HttpServerContext; +import io.servicetalk.http.api.MultiAddressHttpClientBuilder; +import io.servicetalk.http.api.PartitionedHttpClientBuilder; +import io.servicetalk.http.api.SingleAddressHttpClientBuilder; +import io.servicetalk.http.api.StreamingHttpClient; +import io.servicetalk.http.api.StreamingHttpService; +import io.servicetalk.transport.api.HostAndPort; +import io.servicetalk.transport.api.ServerContext; +import io.servicetalk.transport.api.TransportObserver; +import io.servicetalk.transport.netty.internal.NoopTransportObserver.NoopConnectionObserver; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.Collection; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; + +import static io.servicetalk.client.api.ServiceDiscovererEvent.Status.AVAILABLE; +import static io.servicetalk.client.api.partition.PartitionAttributes.Key.newKey; +import static io.servicetalk.concurrent.api.Publisher.never; +import static io.servicetalk.http.api.HttpResponseStatus.OK; +import static io.servicetalk.http.netty.TestServiceStreaming.SVC_ECHO; +import static io.servicetalk.transport.netty.internal.AddressUtils.localAddress; +import static io.servicetalk.transport.netty.internal.AddressUtils.serverHostAndPort; +import static java.util.Collections.singletonList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class HttpProvidersTest { + + private static final PartitionAttributes.Key PARTITION_ATTR_KEY = newKey("PARTITION_ATTR_KEY"); + private static final String PARTITION_ATTR_VALUE = "PARTITION_ATTR_VALUE"; + private static final PartitionAttributes PARTITION_ATTRIBUTES = new DefaultPartitionAttributesBuilder(1) + .add(PARTITION_ATTR_KEY, PARTITION_ATTR_VALUE).build(); + + @BeforeEach + void reset() { + TestHttpServerBuilderProvider.reset(); + TestSingleAddressHttpClientBuilderProvider.reset(); + TestMultiAddressHttpClientBuilderProvider.reset(); + TestPartitionedHttpClientBuilderProvider.reset(); + } + + @Test + void testNoProvidersForAddress() throws Exception { + try (ServerContext serverContext = HttpServers.forAddress(localAddress(0)) + .listenStreamingAndAwait(new TestServiceStreaming()); + BlockingHttpClient client = HttpClients.forSingleAddress(serverHostAndPort(serverContext)) + .buildBlocking()) { + HttpResponse response = client.request(client.get(SVC_ECHO)); + assertThat(response.status(), is(OK)); + } + assertThat(TestHttpServerBuilderProvider.BUILD_COUNTER.get(), is(0)); + assertThat(TestHttpServerBuilderProvider.CONNECTION_COUNTER.get(), is(0)); + assertThat(TestSingleAddressHttpClientBuilderProvider.BUILD_COUNTER.get(), is(0)); + assertThat(TestSingleAddressHttpClientBuilderProvider.CONNECTION_COUNTER.get(), is(0)); + } + + @Test + void testHttpServerBuilderProvider() throws Exception { + final InetSocketAddress serverAddress = localAddress(0); + TestHttpServerBuilderProvider.MODIFY_FOR_ADDRESS.set(serverAddress); + try (ServerContext serverContext = HttpServers.forAddress(serverAddress) + .listenStreamingAndAwait(new TestServiceStreaming())) { + assertThat(TestHttpServerBuilderProvider.BUILD_COUNTER.get(), is(1)); + try (BlockingHttpClient client = HttpClients.forSingleAddress(serverHostAndPort(serverContext)) + .buildBlocking()) { + HttpResponse response = client.request(client.get(SVC_ECHO)); + assertThat(response.status(), is(OK)); + assertThat(TestHttpServerBuilderProvider.CONNECTION_COUNTER.get(), is(1)); + } + } + } + + @Test + void testSingleAddressHttpClientBuilderProviderWithHostAndPort() throws Exception { + try (ServerContext serverContext = HttpServers.forAddress(localAddress(0)) + .listenStreamingAndAwait(new TestServiceStreaming())) { + HostAndPort serverAddress = serverHostAndPort(serverContext); + TestSingleAddressHttpClientBuilderProvider.MODIFY_FOR_ADDRESS.set(serverAddress); + try (BlockingHttpClient client = HttpClients.forSingleAddress(serverAddress).buildBlocking()) { + assertThat(TestSingleAddressHttpClientBuilderProvider.BUILD_COUNTER.get(), is(1)); + HttpResponse response = client.request(client.get(SVC_ECHO)); + assertThat(response.status(), is(OK)); + assertThat(TestSingleAddressHttpClientBuilderProvider.CONNECTION_COUNTER.get(), is(1)); + } + } + } + + @Test + void testSingleAddressHttpClientBuilderProviderForResolvedAddress() throws Exception { + try (ServerContext serverContext = HttpServers.forAddress(localAddress(0)) + .listenStreamingAndAwait(new TestServiceStreaming())) { + SocketAddress serverAddress = serverContext.listenAddress(); + TestSingleAddressHttpClientBuilderProvider.MODIFY_FOR_ADDRESS.set(serverAddress); + try (BlockingHttpClient client = HttpClients.forResolvedAddress(serverAddress).buildBlocking()) { + assertThat(TestSingleAddressHttpClientBuilderProvider.BUILD_COUNTER.get(), is(1)); + HttpResponse response = client.request(client.get(SVC_ECHO)); + assertThat(response.status(), is(OK)); + assertThat(TestSingleAddressHttpClientBuilderProvider.CONNECTION_COUNTER.get(), is(1)); + } + } + } + + @Test + void testMultiAddressHttpClientBuilderProvider() throws Exception { + try (ServerContext serverContext = HttpServers.forAddress(localAddress(0)) + .listenStreamingAndAwait(new TestServiceStreaming())) { + HostAndPort serverAddress = serverHostAndPort(serverContext); + TestSingleAddressHttpClientBuilderProvider.MODIFY_FOR_ADDRESS.set(serverAddress); + try (BlockingHttpClient client = HttpClients.forMultiAddressUrl().buildBlocking()) { + assertThat(TestMultiAddressHttpClientBuilderProvider.BUILD_COUNTER.get(), is(1)); + HttpResponse response = client.request(client.get("http://" + serverAddress + SVC_ECHO)); + assertThat(response.status(), is(OK)); + assertThat(TestSingleAddressHttpClientBuilderProvider.BUILD_COUNTER.get(), is(1)); + assertThat(TestSingleAddressHttpClientBuilderProvider.CONNECTION_COUNTER.get(), is(1)); + } + } + } + + @Test + void testPartitionedHttpClientBuilderProvider() throws Exception { + try (ServerContext serverContext = HttpServers.forAddress(localAddress(0)) + .listenStreamingAndAwait(new TestServiceStreaming())) { + PartitionedServiceDiscovererEvent event = + new PartitionedServiceDiscovererEvent() { + @Override + public PartitionAttributes partitionAddress() { + return PARTITION_ATTRIBUTES; + } + + @Override + public InetSocketAddress address() { + return (InetSocketAddress) serverContext.listenAddress(); + } + + @Override + public Status status() { + return AVAILABLE; + } + }; + ServiceDiscoverer> psd = + mock(ServiceDiscoverer.class); + Publisher>> eventsStream = + Publisher.>>from( + singletonList(event)).concat(never()); + when(psd.discover("test-server")).thenReturn(eventsStream); + Function selector = + req -> new DefaultPartitionAttributesBuilder(1) + .add(PARTITION_ATTR_KEY, PARTITION_ATTR_VALUE); + String serverAddress = "test-server"; + TestSingleAddressHttpClientBuilderProvider.MODIFY_FOR_ADDRESS.set(serverAddress); + try (BlockingHttpClient client = HttpClients.forPartitionedAddress(psd, serverAddress, selector) + .buildBlocking()) { + assertThat(TestPartitionedHttpClientBuilderProvider.BUILD_COUNTER.get(), is(1)); + HttpResponse response = client.request(client.get(SVC_ECHO)); + assertThat(response.status(), is(OK)); + assertThat(TestSingleAddressHttpClientBuilderProvider.BUILD_COUNTER.get(), is(1)); + assertThat(TestSingleAddressHttpClientBuilderProvider.CONNECTION_COUNTER.get(), is(1)); + } + } + } + + public static final class TestHttpServerBuilderProvider implements HttpServerBuilderProvider { + + static final AtomicReference MODIFY_FOR_ADDRESS = new AtomicReference<>(); + static final AtomicInteger BUILD_COUNTER = new AtomicInteger(); + static final AtomicInteger CONNECTION_COUNTER = new AtomicInteger(); + + static void reset() { + MODIFY_FOR_ADDRESS.set(null); + BUILD_COUNTER.set(0); + CONNECTION_COUNTER.set(0); + } + + @Override + public HttpServerBuilder newBuilder(SocketAddress address, HttpServerBuilder builder) { + if (address.equals(MODIFY_FOR_ADDRESS.get())) { + return new DelegatingHttpServerBuilder( + builder.transportObserver(transportObserver(CONNECTION_COUNTER))) { + + @Override + public HttpServerContext listenStreamingAndAwait(StreamingHttpService service) + throws Exception { + BUILD_COUNTER.incrementAndGet(); + return delegate().listenStreamingAndAwait(service); + } + }; + } + return builder; + } + } + + public static final class TestSingleAddressHttpClientBuilderProvider + implements SingleAddressHttpClientBuilderProvider { + + static final AtomicReference MODIFY_FOR_ADDRESS = new AtomicReference<>(); + static final AtomicInteger BUILD_COUNTER = new AtomicInteger(); + static final AtomicInteger CONNECTION_COUNTER = new AtomicInteger(); + + static void reset() { + MODIFY_FOR_ADDRESS.set(null); + BUILD_COUNTER.set(0); + CONNECTION_COUNTER.set(0); + } + + @Override + public SingleAddressHttpClientBuilder newBuilder(U address, + SingleAddressHttpClientBuilder builder) { + if (address.equals(MODIFY_FOR_ADDRESS.get())) { + // Test that users can either modify the existing filter or wrap it for additional logic: + return new DelegatingSingleAddressHttpClientBuilder(builder.appendConnectionFactoryFilter( + new TransportObserverConnectionFactoryFilter<>(transportObserver(CONNECTION_COUNTER)))) { + + @Override + public BlockingHttpClient buildBlocking() { + BUILD_COUNTER.incrementAndGet(); + return super.buildBlocking(); + } + + // multi-address and partitioned client builders uses this method: + @Override + public StreamingHttpClient buildStreaming() { + BUILD_COUNTER.incrementAndGet(); + return super.buildStreaming(); + } + }; + } + return builder; + } + } + + public static final class TestMultiAddressHttpClientBuilderProvider + implements MultiAddressHttpClientBuilderProvider { + + static final AtomicInteger BUILD_COUNTER = new AtomicInteger(); + + static void reset() { + BUILD_COUNTER.set(0); + } + + @Override + public MultiAddressHttpClientBuilder newBuilder(MultiAddressHttpClientBuilder builder) { + return new DelegatingMultiAddressHttpClientBuilder(builder) { + + @Override + public BlockingHttpClient buildBlocking() { + BUILD_COUNTER.incrementAndGet(); + return delegate().buildBlocking(); + } + }; + } + } + + public static final class TestPartitionedHttpClientBuilderProvider + implements PartitionedHttpClientBuilderProvider { + + static final AtomicInteger BUILD_COUNTER = new AtomicInteger(); + + static void reset() { + BUILD_COUNTER.set(0); + } + + @Override + public PartitionedHttpClientBuilder newBuilder(PartitionedHttpClientBuilder builder) { + return new DelegatingPartitionedHttpClientBuilder(builder) { + + @Override + public BlockingHttpClient buildBlocking() { + BUILD_COUNTER.incrementAndGet(); + return delegate().buildBlocking(); + } + }; + } + } + + private static TransportObserver transportObserver(AtomicInteger counter) { + return (localAddress, remoteAddress) -> { + counter.incrementAndGet(); + return NoopConnectionObserver.INSTANCE; + }; + } +} diff --git a/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$HttpServerBuilderProvider b/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$HttpServerBuilderProvider new file mode 100644 index 0000000000..08413dc8aa --- /dev/null +++ b/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$HttpServerBuilderProvider @@ -0,0 +1 @@ +io.servicetalk.http.netty.HttpProvidersTest$TestHttpServerBuilderProvider diff --git a/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$MultiAddressHttpClientBuilderProvider b/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$MultiAddressHttpClientBuilderProvider new file mode 100644 index 0000000000..236eecd767 --- /dev/null +++ b/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$MultiAddressHttpClientBuilderProvider @@ -0,0 +1 @@ +io.servicetalk.http.netty.HttpProvidersTest$TestMultiAddressHttpClientBuilderProvider diff --git a/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$PartitionedHttpClientBuilderProvider b/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$PartitionedHttpClientBuilderProvider new file mode 100644 index 0000000000..51b5a19c97 --- /dev/null +++ b/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$PartitionedHttpClientBuilderProvider @@ -0,0 +1 @@ +io.servicetalk.http.netty.HttpProvidersTest$TestPartitionedHttpClientBuilderProvider diff --git a/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$SingleAddressHttpClientBuilderProvider b/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$SingleAddressHttpClientBuilderProvider new file mode 100644 index 0000000000..64224476c8 --- /dev/null +++ b/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$SingleAddressHttpClientBuilderProvider @@ -0,0 +1 @@ +io.servicetalk.http.netty.HttpProvidersTest$TestSingleAddressHttpClientBuilderProvider diff --git a/servicetalk-utils-internal/src/main/java/io/servicetalk/utils/internal/ServiceLoaderUtils.java b/servicetalk-utils-internal/src/main/java/io/servicetalk/utils/internal/ServiceLoaderUtils.java new file mode 100644 index 0000000000..626ca75cd3 --- /dev/null +++ b/servicetalk-utils-internal/src/main/java/io/servicetalk/utils/internal/ServiceLoaderUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright © 2022 Apple Inc. and the ServiceTalk project authors + * + * 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 io.servicetalk.utils.internal; + +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; + +/** + * {@link ServiceLoader} utilities. + */ +public final class ServiceLoaderUtils { + + private ServiceLoaderUtils() { + // No instances. + } + + /** + * Loads provider classes via {@link ServiceLoader}. + * + * @param clazz interface of abstract class which implementations should be loaded + * @param classLoader {@link ClassLoader} to use + * @param logger {@link Logger} to use + * @param type of the provider + * @return a list of loaded providers for the specified class + */ + public static List loadProviders(final Class clazz, final ClassLoader classLoader, final Logger logger) { + final List list = new ArrayList<>(0); + for (T provider : ServiceLoader.load(clazz, classLoader)) { + list.add(provider); + } + logger.debug("Registered {} {}(s): {}", list.size(), clazz.getSimpleName(), list); + return list.isEmpty() ? emptyList() : unmodifiableList(list); + } +} From e1082b309a0e8d7e875bbea3d3ab998068585f8c Mon Sep 17 00:00:00 2001 From: Idel Pivnitskiy Date: Tue, 8 Mar 2022 22:28:59 -0800 Subject: [PATCH 2/6] Remove `PartitionedHttpClientBuilderProvider` --- .../servicetalk/http/api/HttpProviders.java | 19 ---- .../servicetalk/http/netty/HttpClients.java | 15 +--- .../http/netty/HttpProvidersTest.java | 89 ------------------- ...iders$PartitionedHttpClientBuilderProvider | 1 - 4 files changed, 2 insertions(+), 122 deletions(-) delete mode 100644 servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$PartitionedHttpClientBuilderProvider diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpProviders.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpProviders.java index 1acd59a4f3..5136290574 100644 --- a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpProviders.java +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpProviders.java @@ -67,25 +67,6 @@ public interface MultiAddressHttpClientBuilderProvider { MultiAddressHttpClientBuilder newBuilder(MultiAddressHttpClientBuilder builder); } - /** - * Provider for {@link PartitionedHttpClientBuilder}. - */ - @FunctionalInterface - public interface PartitionedHttpClientBuilderProvider { - - /** - * Returns a {@link PartitionedHttpClientBuilder} based on the pre-initialized - * {@link PartitionedHttpClientBuilder}. - * - * @param builder pre-initialized {@link PartitionedHttpClientBuilder} - * @param the type of address before resolution (unresolved address) - * @param the type of address after resolution (resolved address) - * @return a {@link PartitionedHttpClientBuilder} based on the pre-initialized - * {@link PartitionedHttpClientBuilder}. - */ - PartitionedHttpClientBuilder newBuilder(PartitionedHttpClientBuilder builder); - } - /** * Provider for {@link HttpServerBuilder}. */ diff --git a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java index 23dc561fef..70b4eac9a4 100644 --- a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java +++ b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java @@ -28,7 +28,6 @@ import io.servicetalk.http.api.HttpClient; import io.servicetalk.http.api.HttpHeaderNames; import io.servicetalk.http.api.HttpProviders.MultiAddressHttpClientBuilderProvider; -import io.servicetalk.http.api.HttpProviders.PartitionedHttpClientBuilderProvider; import io.servicetalk.http.api.HttpProviders.SingleAddressHttpClientBuilderProvider; import io.servicetalk.http.api.HttpRequestMetaData; import io.servicetalk.http.api.MultiAddressHttpClientBuilder; @@ -65,13 +64,11 @@ public final class HttpClients { private static final List SINGLE_ADDRESS_PROVIDERS; private static final List MULTI_ADDRESS_PROVIDERS; - private static final List PARTITIONED_PROVIDERS; static { final ClassLoader classLoader = HttpClients.class.getClassLoader(); SINGLE_ADDRESS_PROVIDERS = loadProviders(SingleAddressHttpClientBuilderProvider.class, classLoader, LOGGER); MULTI_ADDRESS_PROVIDERS = loadProviders(MultiAddressHttpClientBuilderProvider.class, classLoader, LOGGER); - PARTITIONED_PROVIDERS = loadProviders(PartitionedHttpClientBuilderProvider.class, classLoader, LOGGER); } private HttpClients() { @@ -94,14 +91,6 @@ private static MultiAddressHttpClientBuilder applyProviders( return builder; } - private static PartitionedHttpClientBuilder applyProviders( - PartitionedHttpClientBuilder builder) { - for (PartitionedHttpClientBuilderProvider provider : PARTITIONED_PROVIDERS) { - builder = provider.newBuilder(builder); - } - return builder; - } - /** * Creates a {@link MultiAddressHttpClientBuilder} for clients capable of parsing an absolute-form URL, connecting to multiple addresses @@ -286,7 +275,7 @@ public static PartitionedHttpClientBuilder forPartitionedAddress( final ServiceDiscoverer> serviceDiscoverer, final U address, final Function partitionAttributesBuilderFactory) { - return applyProviders(new DefaultPartitionedHttpClientBuilder<>(address, + return new DefaultPartitionedHttpClientBuilder<>(address, () -> forSingleAddress( new ServiceDiscoverer>() { private final ListenableAsyncCloseable closeable = emptyAsyncCloseable(); @@ -310,6 +299,6 @@ public Completable closeAsync() { public Completable closeAsyncGracefully() { return closeable.closeAsyncGracefully(); } - }, address), serviceDiscoverer, partitionAttributesBuilderFactory)); + }, address), serviceDiscoverer, partitionAttributesBuilderFactory); } } diff --git a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java index 621dc854c7..a04b8d8253 100644 --- a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java +++ b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java @@ -15,28 +15,18 @@ */ package io.servicetalk.http.netty; -import io.servicetalk.client.api.ServiceDiscoverer; import io.servicetalk.client.api.TransportObserverConnectionFactoryFilter; -import io.servicetalk.client.api.internal.partition.DefaultPartitionAttributesBuilder; -import io.servicetalk.client.api.partition.PartitionAttributes; -import io.servicetalk.client.api.partition.PartitionAttributesBuilder; -import io.servicetalk.client.api.partition.PartitionedServiceDiscovererEvent; -import io.servicetalk.concurrent.api.Publisher; import io.servicetalk.http.api.BlockingHttpClient; import io.servicetalk.http.api.DelegatingHttpServerBuilder; import io.servicetalk.http.api.DelegatingMultiAddressHttpClientBuilder; -import io.servicetalk.http.api.DelegatingPartitionedHttpClientBuilder; import io.servicetalk.http.api.DelegatingSingleAddressHttpClientBuilder; import io.servicetalk.http.api.HttpProviders.HttpServerBuilderProvider; import io.servicetalk.http.api.HttpProviders.MultiAddressHttpClientBuilderProvider; -import io.servicetalk.http.api.HttpProviders.PartitionedHttpClientBuilderProvider; import io.servicetalk.http.api.HttpProviders.SingleAddressHttpClientBuilderProvider; -import io.servicetalk.http.api.HttpRequestMetaData; import io.servicetalk.http.api.HttpResponse; import io.servicetalk.http.api.HttpServerBuilder; import io.servicetalk.http.api.HttpServerContext; import io.servicetalk.http.api.MultiAddressHttpClientBuilder; -import io.servicetalk.http.api.PartitionedHttpClientBuilder; import io.servicetalk.http.api.SingleAddressHttpClientBuilder; import io.servicetalk.http.api.StreamingHttpClient; import io.servicetalk.http.api.StreamingHttpService; @@ -50,37 +40,23 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.util.Collection; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Function; -import static io.servicetalk.client.api.ServiceDiscovererEvent.Status.AVAILABLE; -import static io.servicetalk.client.api.partition.PartitionAttributes.Key.newKey; -import static io.servicetalk.concurrent.api.Publisher.never; import static io.servicetalk.http.api.HttpResponseStatus.OK; import static io.servicetalk.http.netty.TestServiceStreaming.SVC_ECHO; import static io.servicetalk.transport.netty.internal.AddressUtils.localAddress; import static io.servicetalk.transport.netty.internal.AddressUtils.serverHostAndPort; -import static java.util.Collections.singletonList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; class HttpProvidersTest { - private static final PartitionAttributes.Key PARTITION_ATTR_KEY = newKey("PARTITION_ATTR_KEY"); - private static final String PARTITION_ATTR_VALUE = "PARTITION_ATTR_VALUE"; - private static final PartitionAttributes PARTITION_ATTRIBUTES = new DefaultPartitionAttributesBuilder(1) - .add(PARTITION_ATTR_KEY, PARTITION_ATTR_VALUE).build(); - @BeforeEach void reset() { TestHttpServerBuilderProvider.reset(); TestSingleAddressHttpClientBuilderProvider.reset(); TestMultiAddressHttpClientBuilderProvider.reset(); - TestPartitionedHttpClientBuilderProvider.reset(); } @Test @@ -160,49 +136,6 @@ void testMultiAddressHttpClientBuilderProvider() throws Exception { } } - @Test - void testPartitionedHttpClientBuilderProvider() throws Exception { - try (ServerContext serverContext = HttpServers.forAddress(localAddress(0)) - .listenStreamingAndAwait(new TestServiceStreaming())) { - PartitionedServiceDiscovererEvent event = - new PartitionedServiceDiscovererEvent() { - @Override - public PartitionAttributes partitionAddress() { - return PARTITION_ATTRIBUTES; - } - - @Override - public InetSocketAddress address() { - return (InetSocketAddress) serverContext.listenAddress(); - } - - @Override - public Status status() { - return AVAILABLE; - } - }; - ServiceDiscoverer> psd = - mock(ServiceDiscoverer.class); - Publisher>> eventsStream = - Publisher.>>from( - singletonList(event)).concat(never()); - when(psd.discover("test-server")).thenReturn(eventsStream); - Function selector = - req -> new DefaultPartitionAttributesBuilder(1) - .add(PARTITION_ATTR_KEY, PARTITION_ATTR_VALUE); - String serverAddress = "test-server"; - TestSingleAddressHttpClientBuilderProvider.MODIFY_FOR_ADDRESS.set(serverAddress); - try (BlockingHttpClient client = HttpClients.forPartitionedAddress(psd, serverAddress, selector) - .buildBlocking()) { - assertThat(TestPartitionedHttpClientBuilderProvider.BUILD_COUNTER.get(), is(1)); - HttpResponse response = client.request(client.get(SVC_ECHO)); - assertThat(response.status(), is(OK)); - assertThat(TestSingleAddressHttpClientBuilderProvider.BUILD_COUNTER.get(), is(1)); - assertThat(TestSingleAddressHttpClientBuilderProvider.CONNECTION_COUNTER.get(), is(1)); - } - } - } - public static final class TestHttpServerBuilderProvider implements HttpServerBuilderProvider { static final AtomicReference MODIFY_FOR_ADDRESS = new AtomicReference<>(); @@ -294,28 +227,6 @@ public BlockingHttpClient buildBlocking() { } } - public static final class TestPartitionedHttpClientBuilderProvider - implements PartitionedHttpClientBuilderProvider { - - static final AtomicInteger BUILD_COUNTER = new AtomicInteger(); - - static void reset() { - BUILD_COUNTER.set(0); - } - - @Override - public PartitionedHttpClientBuilder newBuilder(PartitionedHttpClientBuilder builder) { - return new DelegatingPartitionedHttpClientBuilder(builder) { - - @Override - public BlockingHttpClient buildBlocking() { - BUILD_COUNTER.incrementAndGet(); - return delegate().buildBlocking(); - } - }; - } - } - private static TransportObserver transportObserver(AtomicInteger counter) { return (localAddress, remoteAddress) -> { counter.incrementAndGet(); diff --git a/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$PartitionedHttpClientBuilderProvider b/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$PartitionedHttpClientBuilderProvider deleted file mode 100644 index 51b5a19c97..0000000000 --- a/servicetalk-http-netty/src/test/resources/META-INF/services/io.servicetalk.http.api.HttpProviders$PartitionedHttpClientBuilderProvider +++ /dev/null @@ -1 +0,0 @@ -io.servicetalk.http.netty.HttpProvidersTest$TestPartitionedHttpClientBuilderProvider From ac1530d266cdd6ea3494a48df5caa2984a091bdc Mon Sep 17 00:00:00 2001 From: Idel Pivnitskiy Date: Wed, 9 Mar 2022 14:03:25 -0800 Subject: [PATCH 3/6] Override `delegate` inside delegating* impls --- .../grpc/api/DelegatingGrpcClientBuilder.java | 6 +-- .../grpc/api/DelegatingGrpcServerBuilder.java | 8 ++-- .../http/api/DelegatingHttpServerBuilder.java | 40 ++++++++-------- ...legatingMultiAddressHttpClientBuilder.java | 14 +++--- ...elegatingPartitionedHttpClientBuilder.java | 20 ++++---- ...egatingSingleAddressHttpClientBuilder.java | 48 +++++++++---------- 6 files changed, 68 insertions(+), 68 deletions(-) diff --git a/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcClientBuilder.java b/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcClientBuilder.java index 66f678f476..427973372c 100644 --- a/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcClientBuilder.java +++ b/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcClientBuilder.java @@ -27,7 +27,7 @@ */ public class DelegatingGrpcClientBuilder implements GrpcClientBuilder { - private final GrpcClientBuilder delegate; + private GrpcClientBuilder delegate; public DelegatingGrpcClientBuilder(final GrpcClientBuilder delegate) { this.delegate = requireNonNull(delegate); @@ -49,13 +49,13 @@ public String toString() { @Override public GrpcClientBuilder initializeHttp(final HttpInitializer initializer) { - delegate.initializeHttp(initializer); + delegate = delegate.initializeHttp(initializer); return this; } @Override public GrpcClientBuilder defaultTimeout(final Duration defaultTimeout) { - delegate.defaultTimeout(defaultTimeout); + delegate = delegate.defaultTimeout(defaultTimeout); return this; } diff --git a/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcServerBuilder.java b/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcServerBuilder.java index cc6c7e7fb8..14d12e231e 100644 --- a/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcServerBuilder.java +++ b/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/DelegatingGrpcServerBuilder.java @@ -26,7 +26,7 @@ */ public class DelegatingGrpcServerBuilder implements GrpcServerBuilder { - private final GrpcServerBuilder delegate; + private GrpcServerBuilder delegate; public DelegatingGrpcServerBuilder(final GrpcServerBuilder delegate) { this.delegate = requireNonNull(delegate); @@ -48,19 +48,19 @@ public String toString() { @Override public GrpcServerBuilder initializeHttp(final HttpInitializer initializer) { - delegate.initializeHttp(initializer); + delegate = delegate.initializeHttp(initializer); return this; } @Override public GrpcServerBuilder defaultTimeout(final Duration defaultTimeout) { - delegate.defaultTimeout(defaultTimeout); + delegate = delegate.defaultTimeout(defaultTimeout); return this; } @Override public GrpcServerBuilder lifecycleObserver(final GrpcLifecycleObserver lifecycleObserver) { - delegate.lifecycleObserver(lifecycleObserver); + delegate = delegate.lifecycleObserver(lifecycleObserver); return this; } diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingHttpServerBuilder.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingHttpServerBuilder.java index 1484bf47de..27f5f91738 100644 --- a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingHttpServerBuilder.java +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingHttpServerBuilder.java @@ -36,7 +36,7 @@ */ public class DelegatingHttpServerBuilder implements HttpServerBuilder { - private final HttpServerBuilder delegate; + private HttpServerBuilder delegate; /** * Create a new instance. @@ -63,118 +63,118 @@ public String toString() { @Override public HttpServerBuilder protocols(final HttpProtocolConfig... protocols) { - delegate.protocols(protocols); + delegate = delegate.protocols(protocols); return this; } @Override public HttpServerBuilder sslConfig(final ServerSslConfig config) { - delegate.sslConfig(config); + delegate = delegate.sslConfig(config); return this; } @Override public HttpServerBuilder sslConfig(final ServerSslConfig defaultConfig, final Map sniMap) { - delegate.sslConfig(defaultConfig, sniMap); + delegate = delegate.sslConfig(defaultConfig, sniMap); return this; } @Override public HttpServerBuilder socketOption(final SocketOption option, final T value) { - delegate.socketOption(option, value); + delegate = delegate.socketOption(option, value); return this; } @Override public HttpServerBuilder listenSocketOption(final SocketOption option, final T value) { - delegate.listenSocketOption(option, value); + delegate = delegate.listenSocketOption(option, value); return this; } @Override public HttpServerBuilder enableWireLogging(final String loggerName, final LogLevel logLevel, final BooleanSupplier logUserData) { - delegate.enableWireLogging(loggerName, logLevel, logUserData); + delegate = delegate.enableWireLogging(loggerName, logLevel, logUserData); return this; } @Override public HttpServerBuilder transportObserver(final TransportObserver transportObserver) { - delegate.transportObserver(transportObserver); + delegate = delegate.transportObserver(transportObserver); return this; } @Override public HttpServerBuilder lifecycleObserver(final HttpLifecycleObserver lifecycleObserver) { - delegate.lifecycleObserver(lifecycleObserver); + delegate = delegate.lifecycleObserver(lifecycleObserver); return this; } @Override public HttpServerBuilder drainRequestPayloadBody(final boolean enable) { - delegate.drainRequestPayloadBody(enable); + delegate = delegate.drainRequestPayloadBody(enable); return this; } @Override public HttpServerBuilder allowDropRequestTrailers(final boolean allowDrop) { - delegate.allowDropRequestTrailers(allowDrop); + delegate = delegate.allowDropRequestTrailers(allowDrop); return this; } @Override public HttpServerBuilder appendConnectionAcceptorFilter(final ConnectionAcceptorFactory factory) { - delegate.appendConnectionAcceptorFilter(factory); + delegate = delegate.appendConnectionAcceptorFilter(factory); return this; } @Override public HttpServerBuilder appendNonOffloadingServiceFilter(final StreamingHttpServiceFilterFactory factory) { - delegate.appendNonOffloadingServiceFilter(factory); + delegate = delegate.appendNonOffloadingServiceFilter(factory); return this; } @Override public HttpServerBuilder appendNonOffloadingServiceFilter(final Predicate predicate, final StreamingHttpServiceFilterFactory factory) { - delegate.appendNonOffloadingServiceFilter(predicate, factory); + delegate = delegate.appendNonOffloadingServiceFilter(predicate, factory); return this; } @Override public HttpServerBuilder appendServiceFilter(final StreamingHttpServiceFilterFactory factory) { - delegate.appendServiceFilter(factory); + delegate = delegate.appendServiceFilter(factory); return this; } @Override public HttpServerBuilder appendServiceFilter(final Predicate predicate, final StreamingHttpServiceFilterFactory factory) { - delegate.appendServiceFilter(predicate, factory); + delegate = delegate.appendServiceFilter(predicate, factory); return this; } @Override public HttpServerBuilder ioExecutor(final IoExecutor ioExecutor) { - delegate.ioExecutor(ioExecutor); + delegate = delegate.ioExecutor(ioExecutor); return this; } @Override public HttpServerBuilder executor(final Executor executor) { - delegate.executor(executor); + delegate = delegate.executor(executor); return this; } @Override public HttpServerBuilder bufferAllocator(final BufferAllocator allocator) { - delegate.bufferAllocator(allocator); + delegate = delegate.bufferAllocator(allocator); return this; } @Override public HttpServerBuilder executionStrategy(final HttpExecutionStrategy strategy) { - delegate.executionStrategy(strategy); + delegate = delegate.executionStrategy(strategy); return this; } diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingMultiAddressHttpClientBuilder.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingMultiAddressHttpClientBuilder.java index 0ead9b8ff8..e6e6d9de1e 100644 --- a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingMultiAddressHttpClientBuilder.java +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingMultiAddressHttpClientBuilder.java @@ -29,7 +29,7 @@ */ public class DelegatingMultiAddressHttpClientBuilder implements MultiAddressHttpClientBuilder { - private final MultiAddressHttpClientBuilder delegate; + private MultiAddressHttpClientBuilder delegate; /** * Create a new instance. @@ -56,37 +56,37 @@ public String toString() { @Override public MultiAddressHttpClientBuilder ioExecutor(final IoExecutor ioExecutor) { - delegate.ioExecutor(ioExecutor); + delegate = delegate.ioExecutor(ioExecutor); return this; } @Override public MultiAddressHttpClientBuilder executor(final Executor executor) { - delegate.executor(executor); + delegate = delegate.executor(executor); return this; } @Override public MultiAddressHttpClientBuilder executionStrategy(final HttpExecutionStrategy strategy) { - delegate.executionStrategy(strategy); + delegate = delegate.executionStrategy(strategy); return this; } @Override public MultiAddressHttpClientBuilder bufferAllocator(final BufferAllocator allocator) { - delegate.bufferAllocator(allocator); + delegate = delegate.bufferAllocator(allocator); return this; } @Override public MultiAddressHttpClientBuilder initializer(final SingleAddressInitializer initializer) { - delegate.initializer(initializer); + delegate = delegate.initializer(initializer); return this; } @Override public MultiAddressHttpClientBuilder followRedirects(final RedirectConfig config) { - delegate.followRedirects(config); + delegate = delegate.followRedirects(config); return this; } diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingPartitionedHttpClientBuilder.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingPartitionedHttpClientBuilder.java index a785905b47..bb7a85a420 100644 --- a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingPartitionedHttpClientBuilder.java +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingPartitionedHttpClientBuilder.java @@ -34,7 +34,7 @@ */ public class DelegatingPartitionedHttpClientBuilder implements PartitionedHttpClientBuilder { - private final PartitionedHttpClientBuilder delegate; + private PartitionedHttpClientBuilder delegate; /** * Create a new instance. @@ -61,57 +61,57 @@ public String toString() { @Override public PartitionedHttpClientBuilder ioExecutor(final IoExecutor ioExecutor) { - delegate.ioExecutor(ioExecutor); + delegate = delegate.ioExecutor(ioExecutor); return this; } @Override public PartitionedHttpClientBuilder executor(final Executor executor) { - delegate.executor(executor); + delegate = delegate.executor(executor); return this; } @Override public PartitionedHttpClientBuilder executionStrategy(final HttpExecutionStrategy strategy) { - delegate.executionStrategy(strategy); + delegate = delegate.executionStrategy(strategy); return this; } @Override public PartitionedHttpClientBuilder bufferAllocator(final BufferAllocator allocator) { - delegate.bufferAllocator(allocator); + delegate = delegate.bufferAllocator(allocator); return this; } @Override public PartitionedHttpClientBuilder serviceDiscoverer( final ServiceDiscoverer> serviceDiscoverer) { - delegate.serviceDiscoverer(serviceDiscoverer); + delegate = delegate.serviceDiscoverer(serviceDiscoverer); return this; } @Override public PartitionedHttpClientBuilder retryServiceDiscoveryErrors( final BiIntFunction retryStrategy) { - delegate.retryServiceDiscoveryErrors(retryStrategy); + delegate = delegate.retryServiceDiscoveryErrors(retryStrategy); return this; } @Override public PartitionedHttpClientBuilder serviceDiscoveryMaxQueueSize(final int serviceDiscoveryMaxQueueSize) { - delegate.serviceDiscoveryMaxQueueSize(serviceDiscoveryMaxQueueSize); + delegate = delegate.serviceDiscoveryMaxQueueSize(serviceDiscoveryMaxQueueSize); return this; } @Override public PartitionedHttpClientBuilder partitionMapFactory(final PartitionMapFactory partitionMapFactory) { - delegate.partitionMapFactory(partitionMapFactory); + delegate = delegate.partitionMapFactory(partitionMapFactory); return this; } @Override public PartitionedHttpClientBuilder initializer(final SingleAddressInitializer initializer) { - delegate.initializer(initializer); + delegate = delegate.initializer(initializer); return this; } diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingSingleAddressHttpClientBuilder.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingSingleAddressHttpClientBuilder.java index 9cc98e7b42..87e00b374f 100644 --- a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingSingleAddressHttpClientBuilder.java +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingSingleAddressHttpClientBuilder.java @@ -42,7 +42,7 @@ */ public class DelegatingSingleAddressHttpClientBuilder implements SingleAddressHttpClientBuilder { - private final SingleAddressHttpClientBuilder delegate; + private SingleAddressHttpClientBuilder delegate; /** * Create a new instance. @@ -69,148 +69,148 @@ public String toString() { @Override public SingleAddressHttpClientBuilder proxyAddress(final U proxyAddress) { - delegate.proxyAddress(proxyAddress); + delegate = delegate.proxyAddress(proxyAddress); return this; } @Override public SingleAddressHttpClientBuilder socketOption(final SocketOption option, final T value) { - delegate.socketOption(option, value); + delegate = delegate.socketOption(option, value); return this; } @Override public SingleAddressHttpClientBuilder enableWireLogging(final String loggerName, final LogLevel logLevel, final BooleanSupplier logUserData) { - delegate.enableWireLogging(loggerName, logLevel, logUserData); + delegate = delegate.enableWireLogging(loggerName, logLevel, logUserData); return this; } @Override public SingleAddressHttpClientBuilder protocols(final HttpProtocolConfig... protocols) { - delegate.protocols(protocols); + delegate = delegate.protocols(protocols); return this; } @Override public SingleAddressHttpClientBuilder hostHeaderFallback(final boolean enable) { - delegate.hostHeaderFallback(enable); + delegate = delegate.hostHeaderFallback(enable); return this; } @Override public SingleAddressHttpClientBuilder allowDropResponseTrailers(final boolean allowDrop) { - delegate.allowDropResponseTrailers(allowDrop); + delegate = delegate.allowDropResponseTrailers(allowDrop); return this; } @Override public SingleAddressHttpClientBuilder appendConnectionFilter( final StreamingHttpConnectionFilterFactory factory) { - delegate.appendConnectionFilter(factory); + delegate = delegate.appendConnectionFilter(factory); return this; } @Override public SingleAddressHttpClientBuilder appendConnectionFilter( final Predicate predicate, final StreamingHttpConnectionFilterFactory factory) { - delegate.appendConnectionFilter(predicate, factory); + delegate = delegate.appendConnectionFilter(predicate, factory); return this; } @Override public SingleAddressHttpClientBuilder ioExecutor(final IoExecutor ioExecutor) { - delegate.ioExecutor(ioExecutor); + delegate = delegate.ioExecutor(ioExecutor); return this; } @Override public SingleAddressHttpClientBuilder executor(final Executor executor) { - delegate.executor(executor); + delegate = delegate.executor(executor); return this; } @Override public SingleAddressHttpClientBuilder executionStrategy(final HttpExecutionStrategy strategy) { - delegate.executionStrategy(strategy); + delegate = delegate.executionStrategy(strategy); return this; } @Override public SingleAddressHttpClientBuilder bufferAllocator(final BufferAllocator allocator) { - delegate.bufferAllocator(allocator); + delegate = delegate.bufferAllocator(allocator); return this; } @Override public SingleAddressHttpClientBuilder appendConnectionFactoryFilter( final ConnectionFactoryFilter factory) { - delegate.appendConnectionFactoryFilter(factory); + delegate = delegate.appendConnectionFactoryFilter(factory); return this; } @Override public SingleAddressHttpClientBuilder appendClientFilter(final StreamingHttpClientFilterFactory factory) { - delegate.appendClientFilter(factory); + delegate = delegate.appendClientFilter(factory); return this; } @Override public SingleAddressHttpClientBuilder appendClientFilter(final Predicate predicate, final StreamingHttpClientFilterFactory factory) { - delegate.appendClientFilter(predicate, factory); + delegate = delegate.appendClientFilter(predicate, factory); return this; } @Override public SingleAddressHttpClientBuilder unresolvedAddressToHost( final Function unresolvedAddressToHostFunction) { - delegate.unresolvedAddressToHost(unresolvedAddressToHostFunction); + delegate = delegate.unresolvedAddressToHost(unresolvedAddressToHostFunction); return this; } @Override public SingleAddressHttpClientBuilder serviceDiscoverer( final ServiceDiscoverer> serviceDiscoverer) { - delegate.serviceDiscoverer(serviceDiscoverer); + delegate = delegate.serviceDiscoverer(serviceDiscoverer); return this; } @Override public SingleAddressHttpClientBuilder retryServiceDiscoveryErrors( final BiIntFunction retryStrategy) { - delegate.retryServiceDiscoveryErrors(retryStrategy); + delegate = delegate.retryServiceDiscoveryErrors(retryStrategy); return this; } @Override public SingleAddressHttpClientBuilder loadBalancerFactory( final HttpLoadBalancerFactory loadBalancerFactory) { - delegate.loadBalancerFactory(loadBalancerFactory); + delegate = delegate.loadBalancerFactory(loadBalancerFactory); return this; } @Override public SingleAddressHttpClientBuilder sslConfig(final ClientSslConfig sslConfig) { - delegate.sslConfig(sslConfig); + delegate = delegate.sslConfig(sslConfig); return this; } @Override public SingleAddressHttpClientBuilder inferPeerHost(final boolean shouldInfer) { - delegate.inferPeerHost(shouldInfer); + delegate = delegate.inferPeerHost(shouldInfer); return this; } @Override public SingleAddressHttpClientBuilder inferPeerPort(final boolean shouldInfer) { - delegate.inferPeerPort(shouldInfer); + delegate = delegate.inferPeerPort(shouldInfer); return this; } @Override public SingleAddressHttpClientBuilder inferSniHostname(final boolean shouldInfer) { - delegate.inferSniHostname(shouldInfer); + delegate = delegate.inferSniHostname(shouldInfer); return this; } From f2c11b4a1680c5067f56bb61300cf4548f88a60e Mon Sep 17 00:00:00 2001 From: Idel Pivnitskiy Date: Wed, 9 Mar 2022 15:10:24 -0800 Subject: [PATCH 4/6] Implement missed `headersFactory` method --- .../DelegatingMultiAddressHttpClientBuilder.java | 6 ++++++ .../DelegatingPartitionedHttpClientBuilder.java | 6 ++++++ .../servicetalk/http/netty/HttpProvidersTest.java | 14 ++++++++++++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingMultiAddressHttpClientBuilder.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingMultiAddressHttpClientBuilder.java index e6e6d9de1e..5cfabe0528 100644 --- a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingMultiAddressHttpClientBuilder.java +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingMultiAddressHttpClientBuilder.java @@ -78,6 +78,12 @@ public MultiAddressHttpClientBuilder bufferAllocator(final BufferAllocator return this; } + @Override + public MultiAddressHttpClientBuilder headersFactory(final HttpHeadersFactory headersFactory) { + delegate = delegate.headersFactory(headersFactory); + return this; + } + @Override public MultiAddressHttpClientBuilder initializer(final SingleAddressInitializer initializer) { delegate = delegate.initializer(initializer); diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingPartitionedHttpClientBuilder.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingPartitionedHttpClientBuilder.java index bb7a85a420..6072e62825 100644 --- a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingPartitionedHttpClientBuilder.java +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/DelegatingPartitionedHttpClientBuilder.java @@ -83,6 +83,12 @@ public PartitionedHttpClientBuilder bufferAllocator(final BufferAllocator return this; } + @Override + public PartitionedHttpClientBuilder headersFactory(final HttpHeadersFactory headersFactory) { + delegate = delegate.headersFactory(headersFactory); + return this; + } + @Override public PartitionedHttpClientBuilder serviceDiscoverer( final ServiceDiscoverer> serviceDiscoverer) { diff --git a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java index a04b8d8253..69f9dc4e7d 100644 --- a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java +++ b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java @@ -35,11 +35,13 @@ import io.servicetalk.transport.api.TransportObserver; import io.servicetalk.transport.netty.internal.NoopTransportObserver.NoopConnectionObserver; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -59,6 +61,11 @@ void reset() { TestMultiAddressHttpClientBuilderProvider.reset(); } + @AfterEach + void deactivate() { + TestMultiAddressHttpClientBuilderProvider.ACTIVATED.set(false); + } + @Test void testNoProvidersForAddress() throws Exception { try (ServerContext serverContext = HttpServers.forAddress(localAddress(0)) @@ -208,22 +215,25 @@ public StreamingHttpClient buildStreaming() { public static final class TestMultiAddressHttpClientBuilderProvider implements MultiAddressHttpClientBuilderProvider { + // Used to prevent applying this provider for other test classes: + static final AtomicBoolean ACTIVATED = new AtomicBoolean(); static final AtomicInteger BUILD_COUNTER = new AtomicInteger(); static void reset() { + ACTIVATED.set(true); BUILD_COUNTER.set(0); } @Override public MultiAddressHttpClientBuilder newBuilder(MultiAddressHttpClientBuilder builder) { - return new DelegatingMultiAddressHttpClientBuilder(builder) { + return ACTIVATED.get() ? new DelegatingMultiAddressHttpClientBuilder(builder) { @Override public BlockingHttpClient buildBlocking() { BUILD_COUNTER.incrementAndGet(); return delegate().buildBlocking(); } - }; + } : builder; } } From 74c9a8172fdf7f39ef4842241e08cd788eda98f0 Mon Sep 17 00:00:00 2001 From: Idel Pivnitskiy Date: Wed, 23 Mar 2022 10:55:19 -0700 Subject: [PATCH 5/6] Address comments 1 --- .../servicetalk/grpc/api/GrpcProviders.java | 8 +++++ .../servicetalk/grpc/netty/GrpcClients.java | 24 +++++++++++++++ .../servicetalk/grpc/netty/GrpcServers.java | 14 ++++++--- .../grpc/netty/GrpcProvidersTest.java | 3 ++ .../servicetalk/http/api/HttpProviders.java | 12 ++++++++ .../servicetalk/http/netty/HttpClients.java | 29 +++++++++++++++++++ .../servicetalk/http/netty/HttpServers.java | 18 ++++++++---- .../http/netty/HttpProvidersTest.java | 3 ++ .../utils/internal/ServiceLoaderUtils.java | 2 +- 9 files changed, 102 insertions(+), 11 deletions(-) diff --git a/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcProviders.java b/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcProviders.java index 3c7d97c3a2..8b319c5646 100644 --- a/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcProviders.java +++ b/servicetalk-grpc-api/src/main/java/io/servicetalk/grpc/api/GrpcProviders.java @@ -43,6 +43,9 @@ public interface GrpcClientBuilderProvider { /** * Returns a {@link GrpcClientBuilder} based on the address and pre-initialized {@link GrpcClientBuilder}. + *

+ * This method may return the pre-initialized {@code builder} as-is, or apply custom builder settings before + * returning it, or wrap it ({@link DelegatingGrpcClientBuilder} may be helpful). * * @param address a remote address used to create a {@link GrpcClientBuilder}, it can be resolved or unresolved * based on the factory used @@ -50,6 +53,7 @@ public interface GrpcClientBuilderProvider { * @param the type of address before resolution (unresolved address) * @param the type of address after resolution (resolved address) * @return a {@link GrpcClientBuilder} based on the address and pre-initialized {@link GrpcClientBuilder}. + * @see DelegatingGrpcClientBuilder */ GrpcClientBuilder newBuilder(U address, GrpcClientBuilder builder); } @@ -64,10 +68,14 @@ public interface GrpcServerBuilderProvider { /** * Returns a {@link GrpcServerBuilder} based on the address and pre-initialized {@link GrpcServerBuilder}. + *

+ * This method may return the pre-initialized {@code builder} as-is, or apply custom builder settings before + * returning it, or wrap it ({@link DelegatingGrpcServerBuilder} may be helpful). * * @param address a server address used to create a {@link GrpcServerBuilder} * @param builder pre-initialized {@link GrpcServerBuilder} * @return a {@link GrpcServerBuilder} based on the address and pre-initialized{@link GrpcServerBuilder}. + * @see DelegatingGrpcServerBuilder */ GrpcServerBuilder newBuilder(SocketAddress address, GrpcServerBuilder builder); } diff --git a/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcClients.java b/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcClients.java index 19cdba2014..dac9615984 100644 --- a/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcClients.java +++ b/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcClients.java @@ -63,10 +63,13 @@ private static GrpcClientBuilder applyProviders( /** * Creates a {@link GrpcClientBuilder} for an address with default {@link LoadBalancer} and DNS * {@link ServiceDiscoverer}. + *

+ * The returned builder can be customized using {@link GrpcClientBuilderProvider}. * * @param host host to connect to, resolved by default using a DNS {@link ServiceDiscoverer}. * @param port port to connect to * @return new builder for the address + * @see GrpcClientBuilderProvider */ public static GrpcClientBuilder forAddress(final String host, final int port) { return forAddress(HostAndPort.of(host, port)); @@ -75,10 +78,13 @@ public static GrpcClientBuilder forAddress(final /** * Creates a {@link GrpcClientBuilder} for an address with default {@link LoadBalancer} and DNS * {@link ServiceDiscoverer}. + *

+ * The returned builder can be customized using {@link GrpcClientBuilderProvider}. * * @param address the {@code UnresolvedAddress} to connect to, resolved by default using a DNS * {@link ServiceDiscoverer}. * @return new builder for the address + * @see GrpcClientBuilderProvider */ public static GrpcClientBuilder forAddress(final HostAndPort address) { return applyProviders(address, new DefaultGrpcClientBuilder<>(() -> HttpClients.forSingleAddress(address))); @@ -87,9 +93,12 @@ public static GrpcClientBuilder forAddress(final /** * Creates a {@link GrpcClientBuilder} for the passed {@code serviceName} with default {@link LoadBalancer} and a * DNS {@link ServiceDiscoverer} using SRV record lookups. + *

+ * The returned builder can be customized using {@link GrpcClientBuilderProvider}. * * @param serviceName the service name to query via SRV DNS. * @return new builder for the address + * @see GrpcClientBuilderProvider */ public static GrpcClientBuilder forServiceAddress(final String serviceName) { return applyProviders(serviceName, @@ -98,10 +107,13 @@ public static GrpcClientBuilder forServiceAddress(fin /** * Creates a {@link GrpcClientBuilder} for a resolved address with default {@link LoadBalancer}. + *

+ * The returned builder can be customized using {@link GrpcClientBuilderProvider}. * * @param host resolved host address to connect to. * @param port port to connect to * @return new builder for the address + * @see GrpcClientBuilderProvider */ public static GrpcClientBuilder forResolvedAddress(final String host, final int port) { @@ -110,9 +122,12 @@ public static GrpcClientBuilder forResolvedAddre /** * Creates a {@link GrpcClientBuilder} for an address with default {@link LoadBalancer}. + *

+ * The returned builder can be customized using {@link GrpcClientBuilderProvider}. * * @param address the {@code ResolvedAddress} to connect to. * @return new builder for the address + * @see GrpcClientBuilderProvider */ public static GrpcClientBuilder forResolvedAddress(final HostAndPort address) { return applyProviders(address, new DefaultGrpcClientBuilder<>(() -> HttpClients.forResolvedAddress(address))); @@ -120,9 +135,12 @@ public static GrpcClientBuilder forResolvedAddre /** * Creates a {@link GrpcClientBuilder} for an address with default {@link LoadBalancer}. + *

+ * The returned builder can be customized using {@link GrpcClientBuilderProvider}. * * @param address the {@code InetSocketAddress} to connect to. * @return new builder for the address + * @see GrpcClientBuilderProvider */ public static GrpcClientBuilder forResolvedAddress( final InetSocketAddress address) { @@ -131,6 +149,8 @@ public static GrpcClientBuilder forResolve /** * Creates a {@link GrpcClientBuilder} for an address with default {@link LoadBalancer}. + *

+ * The returned builder can be customized using {@link GrpcClientBuilderProvider}. * * @param address the {@code ResolvedAddress} to connect. This address will also be used for the * {@link HttpHeaderNames#HOST}. @@ -141,6 +161,7 @@ public static GrpcClientBuilder forResolve * if you want to disable this behavior. * @param The type of {@link SocketAddress}. * @return new builder for the address + * @see GrpcClientBuilderProvider */ public static GrpcClientBuilder forResolvedAddress(final T address) { return applyProviders(address, new DefaultGrpcClientBuilder<>(() -> HttpClients.forResolvedAddress(address))); @@ -149,6 +170,8 @@ public static GrpcClientBuilder forResolvedAddre /** * Creates a {@link GrpcClientBuilder} for a custom address type with default {@link LoadBalancer} and user * provided {@link ServiceDiscoverer}. + *

+ * The returned builder can be customized using {@link GrpcClientBuilderProvider}. * * @param serviceDiscoverer The {@link ServiceDiscoverer} to resolve addresses of remote servers to connect to. * The lifecycle of the provided {@link ServiceDiscoverer} should be managed by the caller. @@ -156,6 +179,7 @@ public static GrpcClientBuilder forResolvedAddre * @param the type of address before resolution (unresolved address) * @param the type of address after resolution (resolved address) * @return new builder with provided configuration + * @see GrpcClientBuilderProvider */ public static GrpcClientBuilder forAddress(final ServiceDiscoverer> serviceDiscoverer, diff --git a/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcServers.java b/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcServers.java index 3f687d7c20..c66826ee56 100644 --- a/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcServers.java +++ b/servicetalk-grpc-netty/src/main/java/io/servicetalk/grpc/netty/GrpcServers.java @@ -55,9 +55,12 @@ private static GrpcServerBuilder applyProviders(final SocketAddress address, Grp /** * New {@link GrpcServerBuilder} instance. + *

+ * The returned builder can be customized using {@link GrpcServerBuilderProvider}. * - * @param port the listen port for the server. - * @return a new builder. + * @param port the listen port for the server + * @return a new builder + * @see GrpcServerBuilderProvider */ public static GrpcServerBuilder forPort(final int port) { final InetSocketAddress address = new InetSocketAddress(port); @@ -66,9 +69,12 @@ public static GrpcServerBuilder forPort(final int port) { /** * New {@link GrpcServerBuilder} instance. + *

+ * The returned builder can be customized using {@link GrpcServerBuilderProvider}. * - * @param address the listen {@link SocketAddress} for the server. - * @return a new builder. + * @param address the listen {@link SocketAddress} for the server + * @return a new builder + * @see GrpcServerBuilderProvider */ public static GrpcServerBuilder forAddress(final SocketAddress address) { return applyProviders(address, new DefaultGrpcServerBuilder(() -> HttpServers.forAddress(address))); diff --git a/servicetalk-grpc-netty/src/test/java/io/servicetalk/grpc/netty/GrpcProvidersTest.java b/servicetalk-grpc-netty/src/test/java/io/servicetalk/grpc/netty/GrpcProvidersTest.java index 5cb645aede..1d95bd685b 100644 --- a/servicetalk-grpc-netty/src/test/java/io/servicetalk/grpc/netty/GrpcProvidersTest.java +++ b/servicetalk-grpc-netty/src/test/java/io/servicetalk/grpc/netty/GrpcProvidersTest.java @@ -43,6 +43,7 @@ import io.grpc.examples.helloworld.HelloRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -54,7 +55,9 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; +@Execution(SAME_THREAD) class GrpcProvidersTest { @BeforeEach diff --git a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpProviders.java b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpProviders.java index 5136290574..361eb77f9b 100644 --- a/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpProviders.java +++ b/servicetalk-http-api/src/main/java/io/servicetalk/http/api/HttpProviders.java @@ -36,6 +36,9 @@ public interface SingleAddressHttpClientBuilderProvider { /** * Returns a {@link SingleAddressHttpClientBuilder} based on the address and pre-initialized * {@link SingleAddressHttpClientBuilder}. + *

+ * This method may return the pre-initialized {@code builder} as-is, or apply custom builder settings before + * returning it, or wrap it ({@link DelegatingSingleAddressHttpClientBuilder} may be helpful). * * @param address a remote address used to create a {@link SingleAddressHttpClientBuilder}, it can be resolved * or unresolved based on the factory used @@ -44,6 +47,7 @@ public interface SingleAddressHttpClientBuilderProvider { * @param the type of address after resolution (resolved address) * @return a {@link SingleAddressHttpClientBuilder} based on the address and pre-initialized * {@link SingleAddressHttpClientBuilder}. + * @see DelegatingSingleAddressHttpClientBuilder */ SingleAddressHttpClientBuilder newBuilder(U address, SingleAddressHttpClientBuilder builder); } @@ -57,12 +61,16 @@ public interface MultiAddressHttpClientBuilderProvider { /** * Returns a {@link MultiAddressHttpClientBuilder} based on the pre-initialized * {@link MultiAddressHttpClientBuilder}. + *

+ * This method may return the pre-initialized {@code builder} as-is, or apply custom builder settings before + * returning it, or wrap it ({@link DelegatingMultiAddressHttpClientBuilder} may be helpful). * * @param builder pre-initialized {@link MultiAddressHttpClientBuilder} * @param the type of address before resolution (unresolved address) * @param the type of address after resolution (resolved address) * @return a {@link MultiAddressHttpClientBuilder} based on the pre-initialized * {@link MultiAddressHttpClientBuilder}. + * @see DelegatingMultiAddressHttpClientBuilder */ MultiAddressHttpClientBuilder newBuilder(MultiAddressHttpClientBuilder builder); } @@ -75,10 +83,14 @@ public interface HttpServerBuilderProvider { /** * Returns a {@link HttpServerBuilder} based on the address and pre-initialized {@link HttpServerBuilder}. + *

+ * This method may return the pre-initialized {@code builder} as-is, or apply custom builder settings before + * returning it, or wrap it ({@link DelegatingHttpServerBuilder} may be helpful). * * @param address a server address used to create a {@link HttpServerBuilder} * @param builder pre-initialized {@link HttpServerBuilder} * @return a {@link HttpServerBuilder} based on the address and pre-initialized{@link HttpServerBuilder}. + * @see DelegatingHttpServerBuilder */ HttpServerBuilder newBuilder(SocketAddress address, HttpServerBuilder builder); } diff --git a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java index 70b4eac9a4..abe5afaa9d 100644 --- a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java +++ b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java @@ -99,8 +99,11 @@ private static MultiAddressHttpClientBuilder applyProviders( * When a relative URL is passed in the {@link * StreamingHttpRequest#requestTarget(String)} this client requires a {@link HttpHeaderNames#HOST} present in * order to infer the remote address. + *

+ * The returned builder can be customized using {@link MultiAddressHttpClientBuilderProvider}. * * @return new builder with default configuration + * @see MultiAddressHttpClientBuilderProvider */ public static MultiAddressHttpClientBuilder forMultiAddressUrl() { return applyProviders(new DefaultMultiAddressUrlHttpClientBuilder(HttpClients::forSingleAddress)); @@ -114,6 +117,8 @@ public static MultiAddressHttpClientBuilder forM * When a relative URL is passed in the {@link * StreamingHttpRequest#requestTarget(String)} this client requires a {@link HttpHeaderNames#HOST} present in * order to infer the remote address. + *

+ * The returned builder can be customized using {@link MultiAddressHttpClientBuilderProvider}. * * @param serviceDiscoverer The {@link ServiceDiscoverer} to resolve addresses of remote servers to connect to. * The lifecycle of the provided {@link ServiceDiscoverer} should be managed by the caller. @@ -122,6 +127,7 @@ public static MultiAddressHttpClientBuilder forM * {@link MultiAddressHttpClientBuilder#initializer(SingleAddressInitializer)} to override {@link ServiceDiscoverer} * using {@link SingleAddressHttpClientBuilder#serviceDiscoverer(ServiceDiscoverer)} for all or some of the internal * clients. + * @see MultiAddressHttpClientBuilderProvider */ @Deprecated // FIXME: 0.43 - remove deprecated method public static MultiAddressHttpClientBuilder forMultiAddressUrl( @@ -134,6 +140,8 @@ public static MultiAddressHttpClientBuilder forM /** * Creates a {@link SingleAddressHttpClientBuilder} for an address with default {@link LoadBalancer} and DNS {@link * ServiceDiscoverer}. + *

+ * The returned builder can be customized using {@link SingleAddressHttpClientBuilderProvider}. * * @param host host to connect to, resolved by default using a DNS {@link ServiceDiscoverer}. This will also be * used for the {@link HttpHeaderNames#HOST} together with the {@code port}. Use @@ -141,6 +149,7 @@ public static MultiAddressHttpClientBuilder forM * or {@link SingleAddressHttpClientBuilder#hostHeaderFallback(boolean)} if you want to disable this behavior. * @param port port to connect to * @return new builder for the address + * @see SingleAddressHttpClientBuilderProvider */ public static SingleAddressHttpClientBuilder forSingleAddress( final String host, final int port) { @@ -150,12 +159,15 @@ public static SingleAddressHttpClientBuilder for /** * Creates a {@link SingleAddressHttpClientBuilder} for an address with default {@link LoadBalancer} and DNS {@link * ServiceDiscoverer}. + *

+ * The returned builder can be customized using {@link SingleAddressHttpClientBuilderProvider}. * * @param address the {@code UnresolvedAddress} to connect to, resolved by default using a DNS {@link * ServiceDiscoverer}. This address will also be used for the {@link HttpHeaderNames#HOST}. * Use {@link SingleAddressHttpClientBuilder#unresolvedAddressToHost(Function)} if you want to override that * value or {@link SingleAddressHttpClientBuilder#hostHeaderFallback(boolean)} if you want to disable this behavior. * @return new builder for the address + * @see SingleAddressHttpClientBuilderProvider */ public static SingleAddressHttpClientBuilder forSingleAddress( final HostAndPort address) { @@ -167,9 +179,12 @@ public static SingleAddressHttpClientBuilder for * Creates a {@link SingleAddressHttpClientBuilder} for the passed {@code serviceName} with default * {@link LoadBalancer} and a DNS {@link ServiceDiscoverer} using * SRV record lookups. + *

+ * The returned builder can be customized using {@link SingleAddressHttpClientBuilderProvider}. * * @param serviceName The service name to resolve with SRV DNS. * @return new builder for the address + * @see SingleAddressHttpClientBuilderProvider */ public static SingleAddressHttpClientBuilder forServiceAddress( final String serviceName) { @@ -188,8 +203,12 @@ public static SingleAddressHttpClientBuilder forServi * Note, if {@link SingleAddressHttpClientBuilder#proxyAddress(Object) a proxy} is configured for this client, * the proxy address also needs to be already resolved. Otherwise, runtime exceptions will be thrown when * the client is built. + *

+ * The returned builder can be customized using {@link SingleAddressHttpClientBuilderProvider}. + * * @param port port to connect to * @return new builder for the address + * @see SingleAddressHttpClientBuilderProvider */ public static SingleAddressHttpClientBuilder forResolvedAddress( final String host, final int port) { @@ -207,7 +226,11 @@ public static SingleAddressHttpClientBuilder for * Note, if {@link SingleAddressHttpClientBuilder#proxyAddress(Object) a proxy} is configured for this client, * the proxy address also needs to be already resolved. Otherwise, runtime exceptions will be thrown when * the client is built. + *

+ * The returned builder can be customized using {@link SingleAddressHttpClientBuilderProvider}. + * * @return new builder for the address + * @see SingleAddressHttpClientBuilderProvider */ public static SingleAddressHttpClientBuilder forResolvedAddress( final HostAndPort address) { @@ -217,6 +240,8 @@ public static SingleAddressHttpClientBuilder for /** * Creates a {@link SingleAddressHttpClientBuilder} for an address with default {@link LoadBalancer}. + *

+ * The returned builder can be customized using {@link SingleAddressHttpClientBuilderProvider}. * * @param address the {@code ResolvedAddress} to connect. This address will also be used for the * {@link HttpHeaderNames#HOST}. Use {@link SingleAddressHttpClientBuilder#unresolvedAddressToHost(Function)} @@ -224,6 +249,7 @@ public static SingleAddressHttpClientBuilder for * want to disable this behavior. * @param The type of resolved {@link SocketAddress}. * @return new builder for the address + * @see SingleAddressHttpClientBuilderProvider */ public static SingleAddressHttpClientBuilder forResolvedAddress(final R address) { return applyProviders(address, @@ -233,6 +259,8 @@ public static SingleAddressHttpClientBuilder for /** * Creates a {@link SingleAddressHttpClientBuilder} for a custom address type with default {@link LoadBalancer} and * user provided {@link ServiceDiscoverer}. + *

+ * The returned builder can be customized using {@link SingleAddressHttpClientBuilderProvider}. * * @param serviceDiscoverer The {@link ServiceDiscoverer} to resolve addresses of remote servers to connect to. * The lifecycle of the provided {@link ServiceDiscoverer} should be managed by the caller. @@ -243,6 +271,7 @@ public static SingleAddressHttpClientBuilder for * @param the type of address before resolution (unresolved address) * @param the type of address after resolution (resolved address) * @return new builder with provided configuration + * @see SingleAddressHttpClientBuilderProvider */ public static SingleAddressHttpClientBuilder forSingleAddress( final ServiceDiscoverer> serviceDiscoverer, diff --git a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpServers.java b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpServers.java index ccea34aa90..9f49ae5a81 100644 --- a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpServers.java +++ b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpServers.java @@ -17,7 +17,7 @@ import io.servicetalk.http.api.HttpProviders.HttpServerBuilderProvider; import io.servicetalk.http.api.HttpServerBuilder; -import io.servicetalk.transport.api.ServerContext; +import io.servicetalk.http.api.HttpServerContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +29,7 @@ import static io.servicetalk.utils.internal.ServiceLoaderUtils.loadProviders; /** - * Factory methods for building HTTP Servers backed by {@link ServerContext}. + * A factory to create {@link HttpServerContext HTTP servers}. */ public final class HttpServers { @@ -55,9 +55,12 @@ private static HttpServerBuilder applyProviders(final SocketAddress address, Htt /** * New {@link HttpServerBuilder} instance. + *

+ * The returned builder can be customized using {@link HttpServerBuilderProvider}. * - * @param port The listen port for the server. - * @return a new builder. + * @param port The listen port for the server + * @return a new builder + * @see HttpServerBuilderProvider */ public static HttpServerBuilder forPort(final int port) { final InetSocketAddress address = new InetSocketAddress(port); @@ -66,9 +69,12 @@ public static HttpServerBuilder forPort(final int port) { /** * New {@link HttpServerBuilder} instance. + *

+ * The returned builder can be customized using {@link HttpServerBuilderProvider}. * - * @param address The listen {@link SocketAddress} for the server. - * @return a new builder. + * @param address The listen {@link SocketAddress} for the server + * @return a new builder + * @see HttpServerBuilderProvider */ public static HttpServerBuilder forAddress(final SocketAddress address) { return applyProviders(address, new DefaultHttpServerBuilder(address)); diff --git a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java index 69f9dc4e7d..7cfffaa57b 100644 --- a/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java +++ b/servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProvidersTest.java @@ -38,6 +38,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -51,7 +52,9 @@ import static io.servicetalk.transport.netty.internal.AddressUtils.serverHostAndPort; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; +@Execution(SAME_THREAD) class HttpProvidersTest { @BeforeEach diff --git a/servicetalk-utils-internal/src/main/java/io/servicetalk/utils/internal/ServiceLoaderUtils.java b/servicetalk-utils-internal/src/main/java/io/servicetalk/utils/internal/ServiceLoaderUtils.java index 626ca75cd3..2c25e8ae8a 100644 --- a/servicetalk-utils-internal/src/main/java/io/servicetalk/utils/internal/ServiceLoaderUtils.java +++ b/servicetalk-utils-internal/src/main/java/io/servicetalk/utils/internal/ServiceLoaderUtils.java @@ -37,7 +37,7 @@ private ServiceLoaderUtils() { * Loads provider classes via {@link ServiceLoader}. * * @param clazz interface of abstract class which implementations should be loaded - * @param classLoader {@link ClassLoader} to use + * @param classLoader {@link ClassLoader} to be searched for provider instances * @param logger {@link Logger} to use * @param type of the provider * @return a list of loaded providers for the specified class From e24d5839c38cd28eee39fd14147c0b4785d5236e Mon Sep 17 00:00:00 2001 From: Idel Pivnitskiy Date: Wed, 23 Mar 2022 11:34:19 -0700 Subject: [PATCH 6/6] fix checkstyle --- .../src/main/java/io/servicetalk/http/netty/HttpClients.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java index abe5afaa9d..d270a42e86 100644 --- a/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java +++ b/servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/HttpClients.java @@ -123,11 +123,11 @@ public static MultiAddressHttpClientBuilder forM * @param serviceDiscoverer The {@link ServiceDiscoverer} to resolve addresses of remote servers to connect to. * The lifecycle of the provided {@link ServiceDiscoverer} should be managed by the caller. * @return new builder with default configuration + * @see MultiAddressHttpClientBuilderProvider * @deprecated Use {@link #forMultiAddressUrl()} to create {@link MultiAddressHttpClientBuilder}, then use * {@link MultiAddressHttpClientBuilder#initializer(SingleAddressInitializer)} to override {@link ServiceDiscoverer} * using {@link SingleAddressHttpClientBuilder#serviceDiscoverer(ServiceDiscoverer)} for all or some of the internal * clients. - * @see MultiAddressHttpClientBuilderProvider */ @Deprecated // FIXME: 0.43 - remove deprecated method public static MultiAddressHttpClientBuilder forMultiAddressUrl(