Skip to content

Commit

Permalink
Merge branch 'main' into deps/1.31.0
Browse files Browse the repository at this point in the history
  • Loading branch information
jrhee17 authored Nov 11, 2024
2 parents d67a851 + 0019bc7 commit feecb72
Show file tree
Hide file tree
Showing 132 changed files with 11,852 additions and 1,113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public class RoutersBenchmark {
FALLBACK_SERVICE = newServiceConfig(Route.ofCatchAll());
HOST = new VirtualHost(
"localhost", "localhost", 0, null,
null, SERVICES, FALLBACK_SERVICE, RejectedRouteHandler.DISABLED,
null, null, SERVICES, FALLBACK_SERVICE, RejectedRouteHandler.DISABLED,
unused -> NOPLogger.NOP_LOGGER, FALLBACK_SERVICE.defaultServiceNaming(),
FALLBACK_SERVICE.defaultLogName(), 0, 0, false,
AccessLogWriter.disabled(), CommonPools.blockingTaskExecutor(), 0, SuccessFunction.ofDefault(),
Expand Down
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ allprojects {
doFirst {
addTestOutputListener({ descriptor, event ->
if (event.message.contains('LEAK: ')) {
if (isCi) {
logger.warn("Leak is detected in ${descriptor.className}.${descriptor.displayName}\n" +
"${event.message}")
}
hasLeak.set(true)
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import java.util.function.Function;
import java.util.function.Supplier;

import com.google.common.collect.ImmutableList;

import com.linecorp.armeria.client.endpoint.EndpointGroup;
import com.linecorp.armeria.client.redirect.RedirectConfig;
import com.linecorp.armeria.common.HttpHeaderNames;
Expand Down Expand Up @@ -532,20 +534,20 @@ protected final ClientOptions buildOptions() {
*/
protected final ClientOptions buildOptions(@Nullable ClientOptions baseOptions) {
final Collection<ClientOptionValue<?>> optVals = options.values();
final int numOpts = optVals.size();
final int extra = contextCustomizer == null ? 3 : 4;
final ClientOptionValue<?>[] optValArray = optVals.toArray(new ClientOptionValue[numOpts + extra]);
optValArray[numOpts] = ClientOptions.DECORATION.newValue(decoration.build());
optValArray[numOpts + 1] = ClientOptions.HEADERS.newValue(headers.build());
optValArray[numOpts + 2] = ClientOptions.CONTEXT_HOOK.newValue(contextHook);
final ImmutableList.Builder<ClientOptionValue<?>> additionalValues =
ImmutableList.builder();
additionalValues.addAll(optVals);
additionalValues.add(ClientOptions.DECORATION.newValue(decoration.build()));
additionalValues.add(ClientOptions.HEADERS.newValue(headers.build()));
additionalValues.add(ClientOptions.CONTEXT_HOOK.newValue(contextHook));
if (contextCustomizer != null) {
optValArray[numOpts + 3] = ClientOptions.CONTEXT_CUSTOMIZER.newValue(contextCustomizer);
additionalValues.add(ClientOptions.CONTEXT_CUSTOMIZER.newValue(contextCustomizer));
}

if (baseOptions != null) {
return ClientOptions.of(baseOptions, optValArray);
return ClientOptions.of(baseOptions, additionalValues.build());
} else {
return ClientOptions.of(optValArray);
return ClientOptions.of(additionalValues.build());
}
}
}
174 changes: 123 additions & 51 deletions core/src/main/java/com/linecorp/armeria/client/Bootstraps.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import com.linecorp.armeria.common.SerializationFormat;
import com.linecorp.armeria.common.SessionProtocol;
import com.linecorp.armeria.common.annotation.Nullable;
import com.linecorp.armeria.internal.common.SslContextFactory;
import com.linecorp.armeria.internal.common.SslContextFactory.SslContextMode;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
Expand All @@ -36,65 +38,51 @@

final class Bootstraps {

private final Bootstrap[][] inetBootstraps;
private final Bootstrap @Nullable [][] unixBootstraps;
private final EventLoop eventLoop;
private final SslContext sslCtxHttp1Only;
private final SslContext sslCtxHttp1Or2;
@Nullable
private final SslContextFactory sslContextFactory;

private final HttpClientFactory clientFactory;
private final Bootstrap inetBaseBootstrap;
@Nullable
private final Bootstrap unixBaseBootstrap;
private final Bootstrap[][] inetBootstraps;
private final Bootstrap @Nullable [][] unixBootstraps;

Bootstraps(HttpClientFactory clientFactory, EventLoop eventLoop, SslContext sslCtxHttp1Or2,
SslContext sslCtxHttp1Only) {
Bootstraps(HttpClientFactory clientFactory, EventLoop eventLoop,
SslContext sslCtxHttp1Or2, SslContext sslCtxHttp1Only,
@Nullable SslContextFactory sslContextFactory) {
this.eventLoop = eventLoop;
this.sslCtxHttp1Or2 = sslCtxHttp1Or2;
this.sslCtxHttp1Only = sslCtxHttp1Only;
this.sslContextFactory = sslContextFactory;
this.clientFactory = clientFactory;

inetBaseBootstrap = clientFactory.newInetBootstrap();
inetBaseBootstrap.group(eventLoop);
inetBootstraps = staticBootstrapMap(inetBaseBootstrap);

final Bootstrap inetBaseBootstrap = clientFactory.newInetBootstrap();
final Bootstrap unixBaseBootstrap = clientFactory.newUnixBootstrap();
inetBootstraps = newBootstrapMap(inetBaseBootstrap, clientFactory, eventLoop);
unixBaseBootstrap = clientFactory.newUnixBootstrap();
if (unixBaseBootstrap != null) {
unixBootstraps = newBootstrapMap(unixBaseBootstrap, clientFactory, eventLoop);
unixBaseBootstrap.group(eventLoop);
unixBootstraps = staticBootstrapMap(unixBaseBootstrap);
} else {
unixBootstraps = null;
}
}

/**
* Returns a {@link Bootstrap} corresponding to the specified {@link SocketAddress}
* {@link SessionProtocol} and {@link SerializationFormat}.
*/
Bootstrap get(SocketAddress remoteAddress, SessionProtocol desiredProtocol,
SerializationFormat serializationFormat) {
if (!httpAndHttpsValues().contains(desiredProtocol)) {
throw new IllegalArgumentException("Unsupported session protocol: " + desiredProtocol);
}

if (remoteAddress instanceof InetSocketAddress) {
return select(inetBootstraps, desiredProtocol, serializationFormat);
}

assert remoteAddress instanceof DomainSocketAddress : remoteAddress;

if (unixBootstraps == null) {
throw new IllegalArgumentException("Domain sockets are not supported by " +
eventLoop.getClass().getName());
}

return select(unixBootstraps, desiredProtocol, serializationFormat);
}

private Bootstrap[][] newBootstrapMap(Bootstrap baseBootstrap,
HttpClientFactory clientFactory,
EventLoop eventLoop) {
baseBootstrap.group(eventLoop);
private Bootstrap[][] staticBootstrapMap(Bootstrap baseBootstrap) {
final Set<SessionProtocol> sessionProtocols = httpAndHttpsValues();
final Bootstrap[][] maps = (Bootstrap[][]) Array.newInstance(
Bootstrap.class, SessionProtocol.values().length, 2);
// Attempting to access the array with an unallowed protocol will trigger NPE,
// which will help us find a bug.
for (SessionProtocol p : sessionProtocols) {
final SslContext sslCtx = determineSslContext(p);
setBootstrap(baseBootstrap.clone(), clientFactory, maps, p, sslCtx, true);
setBootstrap(baseBootstrap.clone(), clientFactory, maps, p, sslCtx, false);
createAndSetBootstrap(baseBootstrap, maps, p, sslCtx, true);
createAndSetBootstrap(baseBootstrap, maps, p, sslCtx, false);
}
return maps;
}
Expand All @@ -106,22 +94,18 @@ SslContext determineSslContext(SessionProtocol desiredProtocol) {
return desiredProtocol.isExplicitHttp1() ? sslCtxHttp1Only : sslCtxHttp1Or2;
}

private static Bootstrap select(Bootstrap[][] bootstraps, SessionProtocol desiredProtocol,
SerializationFormat serializationFormat) {
private Bootstrap select(boolean isDomainSocket, SessionProtocol desiredProtocol,
SerializationFormat serializationFormat) {
final Bootstrap[][] bootstraps = isDomainSocket ? unixBootstraps : inetBootstraps;
assert bootstraps != null;
return bootstraps[desiredProtocol.ordinal()][toIndex(serializationFormat)];
}

private static void setBootstrap(Bootstrap bootstrap, HttpClientFactory clientFactory, Bootstrap[][] maps,
SessionProtocol p, SslContext sslCtx, boolean webSocket) {
bootstrap.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new HttpClientPipelineConfigurator(
clientFactory, webSocket, p, sslCtx));
}
}
);
maps[p.ordinal()][toIndex(webSocket)] = bootstrap;
private void createAndSetBootstrap(Bootstrap baseBootstrap, Bootstrap[][] maps,
SessionProtocol desiredProtocol, SslContext sslContext,
boolean webSocket) {
maps[desiredProtocol.ordinal()][toIndex(webSocket)] = newBootstrap(baseBootstrap, desiredProtocol,
sslContext, webSocket, false);
}

private static int toIndex(boolean webSocket) {
Expand All @@ -131,4 +115,92 @@ private static int toIndex(boolean webSocket) {
private static int toIndex(SerializationFormat serializationFormat) {
return toIndex(serializationFormat == SerializationFormat.WS);
}

/**
* Returns a {@link Bootstrap} corresponding to the specified {@link SocketAddress}
* {@link SessionProtocol} and {@link SerializationFormat}.
*/
Bootstrap getOrCreate(SocketAddress remoteAddress, SessionProtocol desiredProtocol,
SerializationFormat serializationFormat) {
if (!httpAndHttpsValues().contains(desiredProtocol)) {
throw new IllegalArgumentException("Unsupported session protocol: " + desiredProtocol);
}

final boolean isDomainSocket = remoteAddress instanceof DomainSocketAddress;
if (isDomainSocket && unixBaseBootstrap == null) {
throw new IllegalArgumentException("Domain sockets are not supported by " +
eventLoop.getClass().getName());
}

if (sslContextFactory == null || !desiredProtocol.isTls()) {
return select(isDomainSocket, desiredProtocol, serializationFormat);
}

final Bootstrap baseBootstrap = isDomainSocket ? unixBaseBootstrap : inetBaseBootstrap;
assert baseBootstrap != null;
return newBootstrap(baseBootstrap, remoteAddress, desiredProtocol, serializationFormat);
}

private Bootstrap newBootstrap(Bootstrap baseBootstrap, SocketAddress remoteAddress,
SessionProtocol desiredProtocol,
SerializationFormat serializationFormat) {
final boolean webSocket = serializationFormat == SerializationFormat.WS;
final SslContext sslContext = newSslContext(remoteAddress, desiredProtocol);
return newBootstrap(baseBootstrap, desiredProtocol, sslContext, webSocket, true);
}

private Bootstrap newBootstrap(Bootstrap baseBootstrap, SessionProtocol desiredProtocol,
SslContext sslContext, boolean webSocket, boolean closeSslContext) {
final Bootstrap bootstrap = baseBootstrap.clone();
bootstrap.handler(clientChannelInitializer(desiredProtocol, sslContext, webSocket, closeSslContext));
return bootstrap;
}

SslContext getOrCreateSslContext(SocketAddress remoteAddress, SessionProtocol desiredProtocol) {
if (sslContextFactory == null) {
return determineSslContext(desiredProtocol);
} else {
return newSslContext(remoteAddress, desiredProtocol);
}
}

private SslContext newSslContext(SocketAddress remoteAddress, SessionProtocol desiredProtocol) {
final String hostname;
if (remoteAddress instanceof InetSocketAddress) {
hostname = ((InetSocketAddress) remoteAddress).getHostString();
} else {
assert remoteAddress instanceof DomainSocketAddress;
hostname = "unix:" + ((DomainSocketAddress) remoteAddress).path();
}

final SslContextMode sslContextMode =
desiredProtocol.isExplicitHttp1() ? SslContextFactory.SslContextMode.CLIENT_HTTP1_ONLY
: SslContextFactory.SslContextMode.CLIENT;
assert sslContextFactory != null;
return sslContextFactory.getOrCreate(sslContextMode, hostname);
}

boolean shouldReleaseSslContext(SslContext sslContext) {
return sslContext != sslCtxHttp1Only && sslContext != sslCtxHttp1Or2;
}

void releaseSslContext(SslContext sslContext) {
if (sslContextFactory != null) {
sslContextFactory.release(sslContext);
}
}

private ChannelInitializer<Channel> clientChannelInitializer(SessionProtocol p, SslContext sslCtx,
boolean webSocket, boolean closeSslContext) {
return new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
if (closeSslContext) {
ch.closeFuture().addListener(unused -> releaseSslContext(sslCtx));
}
ch.pipeline().addLast(new HttpClientPipelineConfigurator(
clientFactory, webSocket, p, sslCtx));
}
};
}
}
Loading

0 comments on commit feecb72

Please sign in to comment.