Skip to content

Commit

Permalink
Merge branch 'main' into stacktrace-response-complete-exception
Browse files Browse the repository at this point in the history
  • Loading branch information
ikhoon authored Nov 22, 2024
2 parents a233d61 + dc99553 commit 35322d8
Show file tree
Hide file tree
Showing 155 changed files with 8,754 additions and 1,080 deletions.
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "3.3.2"
version = "3.8.3"

style = default

Expand Down
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
8 changes: 8 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 Expand Up @@ -485,3 +489,7 @@ allprojects {
}
}
}

configure(projectsWithFlags('java', 'publish')) {
failOnVersionConflict(libs.protobuf.java)
}
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 35322d8

Please sign in to comment.