Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Preprocessor based clients #6060

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
Expand All @@ -46,10 +47,15 @@ abstract class BraveClientIntegrationTest extends ITHttpAsyncClient<WebClient> {
/**
* OkHttp's MockWebServer does not support H2C with HTTP/1 upgrade request.
*/
private static final ClientFactory clientFactoryWithoutUpgradeRequest =
ClientFactory.builder().useHttp2Preface(true).build();
private static ClientFactory clientFactoryWithoutUpgradeRequest;

@AfterAll static void closeClientFactory() {
@BeforeAll
static void createClientFactory() {
clientFactoryWithoutUpgradeRequest = ClientFactory.builder().useHttp2Preface(true).build();
}

@AfterAll
static void closeClientFactory() {
clientFactoryWithoutUpgradeRequest.closeAsync();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static com.google.common.base.Preconditions.checkArgument;
import static com.linecorp.armeria.common.SessionProtocol.httpAndHttpsValues;
import static com.linecorp.armeria.internal.client.ClientBuilderParamsUtil.preprocessorToUri;
import static com.linecorp.armeria.internal.client.ClientUtil.UNDEFINED_URI;
import static java.util.Objects.requireNonNull;

Expand Down Expand Up @@ -72,6 +73,14 @@ protected AbstractWebClientBuilder(SessionProtocol sessionProtocol, EndpointGrou
requireNonNull(endpointGroup, "endpointGroup"), path);
}

/**
* Creates a new instance.
*/
protected AbstractWebClientBuilder(HttpPreprocessor httpPreprocessor, @Nullable String path) {
this(preprocessorToUri(httpPreprocessor, path), null, null, null);
preprocessor(httpPreprocessor);
}

/**
* Creates a new instance.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@
import com.linecorp.armeria.client.redirect.RedirectConfig;
import com.linecorp.armeria.common.RequestId;
import com.linecorp.armeria.common.Scheme;
import com.linecorp.armeria.common.SerializationFormat;
import com.linecorp.armeria.common.SessionProtocol;
import com.linecorp.armeria.common.SuccessFunction;
import com.linecorp.armeria.common.annotation.Nullable;
import com.linecorp.armeria.common.annotation.UnstableApi;
import com.linecorp.armeria.common.auth.AuthToken;
import com.linecorp.armeria.common.auth.BasicToken;
import com.linecorp.armeria.common.auth.OAuth1aToken;
import com.linecorp.armeria.common.auth.OAuth2Token;
import com.linecorp.armeria.internal.client.ClientBuilderParamsUtil;

/**
* Creates a new client that connects to the specified {@link URI} using the builder pattern. Use the factory
Expand Down Expand Up @@ -95,6 +98,18 @@ public final class ClientBuilder extends AbstractClientOptionsBuilder {
this.scheme = scheme;
}

ClientBuilder(SerializationFormat serializationFormat,
ClientPreprocessors preprocessors, @Nullable String path) {
checkArgument(!preprocessors.isEmpty(),
"At least one preprocessor must be set in ClientPreprocessors.");
endpointGroup = null;
this.path = path;
scheme = Scheme.of(serializationFormat, SessionProtocol.HTTP);
uri = ClientBuilderParamsUtil.preprocessorToUri(scheme, preprocessors, path);
preprocessors.preprocessors().forEach(this::preprocessor);
preprocessors.rpcPreprocessors().forEach(this::rpcPreprocessor);
}

/**
* Returns a newly-created client which implements the specified {@code clientType}, based on the
* properties of this builder.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.linecorp.armeria.client.endpoint.EndpointGroup;
import com.linecorp.armeria.common.Scheme;
import com.linecorp.armeria.common.annotation.Nullable;
import com.linecorp.armeria.common.annotation.UnstableApi;

/**
* Provides the construction parameters of a client.
Expand All @@ -36,7 +37,9 @@ static ClientBuilderParams of(URI uri, Class<?> type, ClientOptions options) {
requireNonNull(uri, "uri");
requireNonNull(type, "type");
requireNonNull(options, "options");
return new DefaultClientBuilderParams(uri, type, options);
return new ClientBuilderParamsBuilder(uri).options(options)
.clientType(type)
.build();
}

/**
Expand All @@ -48,7 +51,10 @@ static ClientBuilderParams of(Scheme scheme, EndpointGroup endpointGroup,
requireNonNull(endpointGroup, "endpointGroup");
requireNonNull(type, "type");
requireNonNull(options, "options");
return new DefaultClientBuilderParams(scheme, endpointGroup, absolutePathRef, type, options);
return new ClientBuilderParamsBuilder(scheme, endpointGroup, absolutePathRef)
.clientType(type)
.options(options)
.build();
}

/**
Expand Down Expand Up @@ -80,4 +86,13 @@ static ClientBuilderParams of(Scheme scheme, EndpointGroup endpointGroup,
* Returns the options of the client.
*/
ClientOptions options();

/**
* Returns a {@link ClientBuilderParamsBuilder} which allows creation of a new
* {@link ClientBuilderParams} based on the current properties.
*/
@UnstableApi
default ClientBuilderParamsBuilder paramsBuilder() {
return new ClientBuilderParamsBuilder(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* Copyright 2024 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 com.linecorp.armeria.client;

import static com.linecorp.armeria.internal.client.ClientBuilderParamsUtil.nullOrEmptyToSlash;
import static java.util.Objects.requireNonNull;

import java.net.URI;
import java.net.URISyntaxException;

import com.linecorp.armeria.client.endpoint.EndpointGroup;
import com.linecorp.armeria.common.Scheme;
import com.linecorp.armeria.common.SerializationFormat;
import com.linecorp.armeria.common.SessionProtocol;
import com.linecorp.armeria.common.annotation.Nullable;
import com.linecorp.armeria.common.annotation.UnstableApi;
import com.linecorp.armeria.internal.client.ClientBuilderParamsUtil;
import com.linecorp.armeria.internal.client.endpoint.UndefinedEndpointGroup;
import com.linecorp.armeria.internal.common.util.TemporaryThreadLocals;

/**
* Allows creation of a new {@link ClientBuilderParams} based on a previous {@link ClientBuilderParams}.
*/
@UnstableApi
public final class ClientBuilderParamsBuilder {

private final URI uri;
private final EndpointGroup endpointGroup;
private final SessionProtocol sessionProtocol;

private SerializationFormat serializationFormat;
private String absolutePathRef;

@Nullable
private Class<?> type;
@Nullable
private ClientOptions options;

ClientBuilderParamsBuilder(ClientBuilderParams params) {
uri = params.uri();
endpointGroup = params.endpointGroup();
sessionProtocol = params.scheme().sessionProtocol();

serializationFormat = params.scheme().serializationFormat();
absolutePathRef = params.absolutePathRef();
type = params.clientType();
options = params.options();
}

ClientBuilderParamsBuilder(URI uri) {
this.uri = uri;
final Scheme scheme = Scheme.parse(uri.getScheme());
final EndpointGroup endpointGroup;
if (ClientBuilderParamsUtil.isInternalUri(uri)) {
endpointGroup = UndefinedEndpointGroup.of();
} else {
endpointGroup = Endpoint.parse(uri.getRawAuthority());
}
final String absolutePathRef;
try (TemporaryThreadLocals tempThreadLocals = TemporaryThreadLocals.acquire()) {
final StringBuilder buf = tempThreadLocals.stringBuilder();
buf.append(nullOrEmptyToSlash(uri.getRawPath()));
if (uri.getRawQuery() != null) {
buf.append('?').append(uri.getRawQuery());
}
if (uri.getRawFragment() != null) {
buf.append('#').append(uri.getRawFragment());
}
absolutePathRef = buf.toString();
}
this.endpointGroup = endpointGroup;
serializationFormat = scheme.serializationFormat();
sessionProtocol = scheme.sessionProtocol();
this.absolutePathRef = absolutePathRef;
}

ClientBuilderParamsBuilder(Scheme scheme, EndpointGroup endpointGroup, @Nullable String absolutePathRef) {
this.endpointGroup = endpointGroup;
final String schemeStr = scheme.effectiveUriText();
final String normalizedAbsolutePathRef = nullOrEmptyToSlash(absolutePathRef);
final URI uri;
if (endpointGroup instanceof Endpoint) {
uri = URI.create(schemeStr + "://" + ((Endpoint) endpointGroup).authority() +
normalizedAbsolutePathRef);
} else {
// Create a valid URI which will never succeed.
uri = URI.create(schemeStr + "://" + ClientBuilderParamsUtil.ENDPOINT_GROUP_PREFIX +
Integer.toHexString(System.identityHashCode(endpointGroup)) +
":1" + normalizedAbsolutePathRef);
}
this.uri = uri;
serializationFormat = scheme.serializationFormat();
sessionProtocol = scheme.sessionProtocol();
this.absolutePathRef = normalizedAbsolutePathRef;
}

/**
* Sets the {@link SerializationFormat} for this {@link ClientBuilderParams}.
*/
public ClientBuilderParamsBuilder serializationFormat(SerializationFormat serializationFormat) {
this.serializationFormat = requireNonNull(serializationFormat, "serializationFormat");
return this;
}

/**
* Sets the {@param absolutePathRef} for this {@link ClientBuilderParams}.
*/
public ClientBuilderParamsBuilder absolutePathRef(String absolutePathRef) {
this.absolutePathRef = requireNonNull(absolutePathRef, "absolutePathRef");
return this;
}

/**
* Sets the {@param type} for this {@link ClientBuilderParams}.
*/
public ClientBuilderParamsBuilder clientType(Class<?> type) {
this.type = requireNonNull(type, "type");
return this;
}

/**
* Sets the {@link ClientOptions} for this {@link ClientBuilderParams}.
*/
public ClientBuilderParamsBuilder options(ClientOptions options) {
this.options = requireNonNull(options, "options");
return this;
}

/**
* Builds a new {@link ClientBuilderParams} based on the configured properties.
*/
public ClientBuilderParams build() {
final ClientOptions options = requireNonNull(this.options, "options");
final Class<?> type = requireNonNull(this.type, "type");

final SerializationFormat serializationFormat = this.serializationFormat;
final String absolutePathRef = this.absolutePathRef;
final ClientFactory factory = options.factory();
final Scheme scheme = factory.validateScheme(Scheme.of(serializationFormat, sessionProtocol));
final String schemeStr = scheme.effectiveUriText();

final String path = nullOrEmptyToSlash(absolutePathRef);

final URI uri;
try {
uri = new URI(schemeStr + "://" + this.uri.getRawAuthority() + path);
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
return new DefaultClientBuilderParams(scheme, endpointGroup, path,
factory.validateUri(uri), type, options);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.linecorp.armeria.common.util.ReleasableHolder;
import com.linecorp.armeria.common.util.ShutdownHooks;
import com.linecorp.armeria.common.util.Unwrappable;
import com.linecorp.armeria.internal.client.ClientBuilderParamsUtil;

import io.micrometer.core.instrument.MeterRegistry;
import io.netty.channel.EventLoop;
Expand Down Expand Up @@ -291,8 +292,8 @@ default ClientFactory unwrap() {
default URI validateUri(URI uri) {
requireNonNull(uri, "uri");

if (Clients.isUndefinedUri(uri)) {
// We use a special singleton marker URI for clients that do not explicitly define a
if (ClientBuilderParamsUtil.isInternalUri(uri)) {
// We use a special marker URI for clients that do not explicitly define a
// host or scheme at construction time.
// As this isn't created by users, we don't need to normalize it.
return uri;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ public RpcPreClient rpcDecorate(RpcPreClient execution) {
return execution;
}

boolean isEmpty() {
return preprocessors.isEmpty() && rpcPreprocessors.isEmpty();
}

@Override
public boolean equals(Object object) {
if (this == object) {
Expand Down
Loading
Loading