diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..6741fdc93 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: [velo] +patreon: velo132 diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 81dcf7078..8bffe2fee 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,4 @@ updates: directory: "/" schedule: interval: "weekly" + open-pull-requests-limit: 100 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..131d61999 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,99 @@ +# ---------------------------------------------------------------------------- +# Copyright 2012-2014 The Feign Authors +# +# The Netty Project 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. +# ---------------------------------------------------------------------------- +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +name: "CodeQL" + +on: + push: + branches: ["master"] + pull_request: + # The branches below must be a subset of the branches above + branches: ["master"] + schedule: + - cron: '0 13 * * 3' + +permissions: + contents: read + +jobs: + analyze: + permissions: + actions: read # for github/codeql-action/init to get workflow details + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/analyze to upload SARIF results + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + # Override automatic language detection by changing the below list + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + language: ['java'] + # Learn more... + # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Cache .m2/repository + - name: Cache local Maven repository + uses: actions/cache@v3 + continue-on-error: true + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ matrix.language }} ${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven-${{ matrix.language }} + ${{ runner.os }}-maven- + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + # - name: Autobuild + # uses: github/codeql-action/autobuild@v2 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + - name: Setup Java JDK + uses: actions/setup-java@v4.0.0 + with: + distribution: 'temurin' + java-version: '21' + + - name: Compile project + run: ./mvnw -B -ntp clean package -Pquickbuild -Dtoolchain.skip=true + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/core/src/main/java/feign/AsyncFeign.java b/core/src/main/java/feign/AsyncFeign.java index 1ff3b043f..8006b05ed 100644 --- a/core/src/main/java/feign/AsyncFeign.java +++ b/core/src/main/java/feign/AsyncFeign.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 The Feign Authors + * Copyright 2012-2024 The Feign 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 @@ -210,7 +210,7 @@ closeAfterDecode, decodeVoid, responseInterceptorChain()), responseHandler, logger, logLevel, propagationPolicy, methodInfoResolver, new RequestTemplateFactoryResolver(encoder, queryMapEncoder), - options, decoder, errorDecoder); + options); final ReflectiveFeign feign = new ReflectiveFeign<>(contract, methodHandlerFactory, invocationHandlerFactory, defaultContextSupplier); diff --git a/core/src/main/java/feign/AsynchronousMethodHandler.java b/core/src/main/java/feign/AsynchronousMethodHandler.java index 23cefcfaa..56e7ebb63 100644 --- a/core/src/main/java/feign/AsynchronousMethodHandler.java +++ b/core/src/main/java/feign/AsynchronousMethodHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 The Feign Authors + * Copyright 2012-2024 The Feign 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 @@ -15,8 +15,6 @@ import feign.InvocationHandlerFactory.MethodHandler; import feign.Request.Options; -import feign.codec.Decoder; -import feign.codec.ErrorDecoder; import java.io.IOException; import java.util.List; import java.util.Optional; @@ -31,39 +29,18 @@ final class AsynchronousMethodHandler implements MethodHandler { - private final MethodMetadata metadata; - private final Target target; private final AsyncClient client; - private final Retryer retryer; - private final List requestInterceptors; - private final Logger logger; - private final Logger.Level logLevel; - private final RequestTemplate.Factory buildTemplateFromArgs; - private final Options options; - private final ExceptionPropagationPolicy propagationPolicy; private final C requestContext; private final AsyncResponseHandler asyncResponseHandler; private final MethodInfo methodInfo; - - - private AsynchronousMethodHandler(Target target, AsyncClient client, Retryer retryer, - List requestInterceptors, - Logger logger, Logger.Level logLevel, MethodMetadata metadata, - RequestTemplate.Factory buildTemplateFromArgs, Options options, - AsyncResponseHandler asyncResponseHandler, ExceptionPropagationPolicy propagationPolicy, - C requestContext, MethodInfo methodInfo) { - - this.target = checkNotNull(target, "target"); - this.client = checkNotNull(client, "client for %s", target); - this.retryer = checkNotNull(retryer, "retryer for %s", target); - this.requestInterceptors = - checkNotNull(requestInterceptors, "requestInterceptors for %s", target); - this.logger = checkNotNull(logger, "logger for %s", target); - this.logLevel = checkNotNull(logLevel, "logLevel for %s", target); - this.metadata = checkNotNull(metadata, "metadata for %s", target); - this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target); - this.options = checkNotNull(options, "options for %s", target); - this.propagationPolicy = propagationPolicy; + private final MethodHandlerConfiguration methodHandlerConfiguration; + + private AsynchronousMethodHandler(MethodHandlerConfiguration methodHandlerConfiguration, + AsyncClient client, AsyncResponseHandler asyncResponseHandler, C requestContext, + MethodInfo methodInfo) { + this.methodHandlerConfiguration = + checkNotNull(methodHandlerConfiguration, "methodHandlerConfiguration"); + this.client = checkNotNull(client, "client for %s", methodHandlerConfiguration.getTarget()); this.requestContext = requestContext; this.asyncResponseHandler = asyncResponseHandler; this.methodInfo = methodInfo; @@ -71,9 +48,9 @@ private AsynchronousMethodHandler(Target target, AsyncClient client, Retry @Override public Object invoke(Object[] argv) throws Throwable { - RequestTemplate template = buildTemplateFromArgs.create(argv); + RequestTemplate template = methodHandlerConfiguration.getBuildTemplateFromArgs().create(argv); Options options = findOptions(argv); - Retryer retryer = this.retryer.clone(); + Retryer retryer = this.methodHandlerConfiguration.getRetryer().clone(); try { if (methodInfo.isAsyncReturnType()) { return executeAndDecode(template, options, retryer); @@ -94,8 +71,10 @@ private CompletableFuture executeAndDecode(RequestTemplate template, .whenComplete((response, throwable) -> { if (throwable != null) { if (!resultFuture.isDone() && shouldRetry(retryer, throwable, resultFuture)) { - if (logLevel != Logger.Level.NONE) { - logger.logRetry(metadata.configKey(), logLevel); + if (methodHandlerConfiguration.getLogLevel() != Logger.Level.NONE) { + methodHandlerConfiguration.getLogger().logRetry( + methodHandlerConfiguration.getMetadata().configKey(), + methodHandlerConfiguration.getLogLevel()); } resultFuture.setInner( @@ -159,7 +138,7 @@ private boolean shouldRetry(Retryer retryer, return true; } catch (RetryableException th) { Throwable cause = th.getCause(); - if (propagationPolicy == UNWRAP && cause != null) { + if (methodHandlerConfiguration.getPropagationPolicy() == UNWRAP && cause != null) { resultFuture.completeExceptionally(cause); } else { resultFuture.completeExceptionally(th); @@ -171,8 +150,10 @@ private boolean shouldRetry(Retryer retryer, private CompletableFuture executeAndDecode(RequestTemplate template, Options options) { Request request = targetRequest(template); - if (logLevel != Logger.Level.NONE) { - logger.logRequest(metadata.configKey(), logLevel, request); + if (methodHandlerConfiguration.getLogLevel() != Logger.Level.NONE) { + methodHandlerConfiguration.getLogger().logRequest( + methodHandlerConfiguration.getMetadata().configKey(), + methodHandlerConfiguration.getLogLevel(), request); } long start = System.nanoTime(); @@ -186,8 +167,10 @@ private CompletableFuture executeAndDecode(RequestTemplate template, Opt : new CompletionException(throwable); if (completionException.getCause() instanceof IOException) { IOException ioException = (IOException) completionException.getCause(); - if (logLevel != Logger.Level.NONE) { - logger.logIOException(metadata.configKey(), logLevel, ioException, + if (methodHandlerConfiguration.getLogLevel() != Logger.Level.NONE) { + methodHandlerConfiguration.getLogger().logIOException( + methodHandlerConfiguration.getMetadata().configKey(), + methodHandlerConfiguration.getLogLevel(), ioException, elapsedTime(start)); } @@ -210,7 +193,8 @@ private static Response ensureRequestIsSet(Response response, private CompletableFuture handleResponse(Response response, long elapsedTime) { return asyncResponseHandler.handleResponse( - metadata.configKey(), response, methodInfo.underlyingReturnType(), elapsedTime); + methodHandlerConfiguration.getMetadata().configKey(), response, + methodInfo.underlyingReturnType(), elapsedTime); } private long elapsedTime(long start) { @@ -218,21 +202,21 @@ private long elapsedTime(long start) { } private Request targetRequest(RequestTemplate template) { - for (RequestInterceptor interceptor : requestInterceptors) { + for (RequestInterceptor interceptor : methodHandlerConfiguration.getRequestInterceptors()) { interceptor.apply(template); } - return target.apply(template); + return methodHandlerConfiguration.getTarget().apply(template); } private Options findOptions(Object[] argv) { if (argv == null || argv.length == 0) { - return this.options; + return this.methodHandlerConfiguration.getOptions(); } return Stream.of(argv) .filter(Options.class::isInstance) .map(Options.class::cast) .findFirst() - .orElse(this.options); + .orElse(this.methodHandlerConfiguration.getOptions()); } static class Factory implements MethodHandler.Factory { @@ -247,8 +231,6 @@ static class Factory implements MethodHandler.Factory { private final MethodInfoResolver methodInfoResolver; private final RequestTemplateFactoryResolver requestTemplateFactoryResolver; private final Options options; - private final Decoder decoder; - private final ErrorDecoder errorDecoder; Factory(AsyncClient client, Retryer retryer, List requestInterceptors, AsyncResponseHandler responseHandler, @@ -256,9 +238,7 @@ static class Factory implements MethodHandler.Factory { ExceptionPropagationPolicy propagationPolicy, MethodInfoResolver methodInfoResolver, RequestTemplateFactoryResolver requestTemplateFactoryResolver, - Options options, - Decoder decoder, - ErrorDecoder errorDecoder) { + Options options) { this.client = checkNotNull(client, "client"); this.retryer = checkNotNull(retryer, "retryer"); this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors"); @@ -270,19 +250,20 @@ static class Factory implements MethodHandler.Factory { this.requestTemplateFactoryResolver = checkNotNull(requestTemplateFactoryResolver, "requestTemplateFactoryResolver"); this.options = checkNotNull(options, "options"); - this.errorDecoder = checkNotNull(errorDecoder, "errorDecoder"); - this.decoder = checkNotNull(decoder, "decoder"); } + @Override public MethodHandler create(Target target, - MethodMetadata md, + MethodMetadata metadata, C requestContext) { final RequestTemplate.Factory buildTemplateFromArgs = - requestTemplateFactoryResolver.resolve(target, md); - return new AsynchronousMethodHandler(target, client, retryer, requestInterceptors, - logger, logLevel, md, buildTemplateFromArgs, options, responseHandler, - propagationPolicy, requestContext, - methodInfoResolver.resolve(target.type(), md.method())); + requestTemplateFactoryResolver.resolve(target, metadata); + + MethodHandlerConfiguration methodHandlerConfiguration = + new MethodHandlerConfiguration(metadata, target, retryer, requestInterceptors, logger, + logLevel, buildTemplateFromArgs, options, propagationPolicy); + return new AsynchronousMethodHandler(methodHandlerConfiguration, client, responseHandler, + requestContext, methodInfoResolver.resolve(target.type(), metadata.method())); } } } diff --git a/core/src/main/java/feign/MethodHandlerConfiguration.java b/core/src/main/java/feign/MethodHandlerConfiguration.java new file mode 100644 index 000000000..0524235e0 --- /dev/null +++ b/core/src/main/java/feign/MethodHandlerConfiguration.java @@ -0,0 +1,93 @@ +/* + * Copyright 2012-2024 The Feign 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 feign; + +import java.util.List; +import static feign.Util.checkNotNull; + +public class MethodHandlerConfiguration { + + private final MethodMetadata metadata; + + private final Target target; + + private final Retryer retryer; + + private final List requestInterceptors; + + private final Logger logger; + + private final Logger.Level logLevel; + + private final RequestTemplate.Factory buildTemplateFromArgs; + + private final Request.Options options; + + private final ExceptionPropagationPolicy propagationPolicy; + + public MethodMetadata getMetadata() { + return metadata; + } + + public Target getTarget() { + return target; + } + + public Retryer getRetryer() { + return retryer; + } + + public List getRequestInterceptors() { + return requestInterceptors; + } + + public Logger getLogger() { + return logger; + } + + public Logger.Level getLogLevel() { + return logLevel; + } + + public RequestTemplate.Factory getBuildTemplateFromArgs() { + return buildTemplateFromArgs; + } + + public Request.Options getOptions() { + return options; + } + + public ExceptionPropagationPolicy getPropagationPolicy() { + return propagationPolicy; + } + + + public MethodHandlerConfiguration(MethodMetadata metadata, Target target, + Retryer retryer, List requestInterceptors, + Logger logger, + Logger.Level logLevel, RequestTemplate.Factory buildTemplateFromArgs, + Request.Options options, ExceptionPropagationPolicy propagationPolicy) { + this.target = checkNotNull(target, "target"); + this.retryer = checkNotNull(retryer, "retryer for %s", target); + this.requestInterceptors = + checkNotNull(requestInterceptors, "requestInterceptors for %s", target); + this.logger = checkNotNull(logger, "logger for %s", target); + this.logLevel = checkNotNull(logLevel, "logLevel for %s", target); + this.metadata = checkNotNull(metadata, "metadata for %s", target); + this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target); + this.options = checkNotNull(options, "options for %s", target); + this.propagationPolicy = propagationPolicy; + + } +} diff --git a/core/src/main/java/feign/SynchronousMethodHandler.java b/core/src/main/java/feign/SynchronousMethodHandler.java index 2f9284564..f113fb3ea 100644 --- a/core/src/main/java/feign/SynchronousMethodHandler.java +++ b/core/src/main/java/feign/SynchronousMethodHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 The Feign Authors + * Copyright 2012-2024 The Feign 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 @@ -18,8 +18,6 @@ import static feign.Util.checkNotNull; import feign.InvocationHandlerFactory.MethodHandler; import feign.Request.Options; -import feign.codec.Decoder; -import feign.codec.ErrorDecoder; import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; @@ -27,44 +25,24 @@ final class SynchronousMethodHandler implements MethodHandler { - private final MethodMetadata metadata; - private final Target target; private final Client client; - private final Retryer retryer; - private final List requestInterceptors; - private final Logger logger; - private final Logger.Level logLevel; - private final RequestTemplate.Factory buildTemplateFromArgs; - private final Options options; - private final ExceptionPropagationPolicy propagationPolicy; private final ResponseHandler responseHandler; + private final MethodHandlerConfiguration methodHandlerConfiguration; + private SynchronousMethodHandler(MethodHandlerConfiguration methodHandlerConfiguration, + Client client, ResponseHandler responseHandler) { - private SynchronousMethodHandler(Target target, Client client, Retryer retryer, - List requestInterceptors, - Logger logger, Logger.Level logLevel, MethodMetadata metadata, - RequestTemplate.Factory buildTemplateFromArgs, Options options, - ResponseHandler responseHandler, ExceptionPropagationPolicy propagationPolicy) { - - this.target = checkNotNull(target, "target"); - this.client = checkNotNull(client, "client for %s", target); - this.retryer = checkNotNull(retryer, "retryer for %s", target); - this.requestInterceptors = - checkNotNull(requestInterceptors, "requestInterceptors for %s", target); - this.logger = checkNotNull(logger, "logger for %s", target); - this.logLevel = checkNotNull(logLevel, "logLevel for %s", target); - this.metadata = checkNotNull(metadata, "metadata for %s", target); - this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target); - this.options = checkNotNull(options, "options for %s", target); - this.propagationPolicy = propagationPolicy; + this.methodHandlerConfiguration = + checkNotNull(methodHandlerConfiguration, "methodHandlerConfiguration"); + this.client = checkNotNull(client, "client for %s", methodHandlerConfiguration.getTarget()); this.responseHandler = responseHandler; } @Override public Object invoke(Object[] argv) throws Throwable { - RequestTemplate template = buildTemplateFromArgs.create(argv); + RequestTemplate template = methodHandlerConfiguration.getBuildTemplateFromArgs().create(argv); Options options = findOptions(argv); - Retryer retryer = this.retryer.clone(); + Retryer retryer = this.methodHandlerConfiguration.getRetryer().clone(); while (true) { try { return executeAndDecode(template, options); @@ -73,14 +51,16 @@ public Object invoke(Object[] argv) throws Throwable { retryer.continueOrPropagate(e); } catch (RetryableException th) { Throwable cause = th.getCause(); - if (propagationPolicy == UNWRAP && cause != null) { + if (methodHandlerConfiguration.getPropagationPolicy() == UNWRAP && cause != null) { throw cause; } else { throw th; } } - if (logLevel != Logger.Level.NONE) { - logger.logRetry(metadata.configKey(), logLevel); + if (methodHandlerConfiguration.getLogLevel() != Logger.Level.NONE) { + methodHandlerConfiguration.getLogger().logRetry( + methodHandlerConfiguration.getMetadata().configKey(), + methodHandlerConfiguration.getLogLevel()); } continue; } @@ -90,8 +70,10 @@ public Object invoke(Object[] argv) throws Throwable { Object executeAndDecode(RequestTemplate template, Options options) throws Throwable { Request request = targetRequest(template); - if (logLevel != Logger.Level.NONE) { - logger.logRequest(metadata.configKey(), logLevel, request); + if (methodHandlerConfiguration.getLogLevel() != Logger.Level.NONE) { + methodHandlerConfiguration.getLogger().logRequest( + methodHandlerConfiguration.getMetadata().configKey(), + methodHandlerConfiguration.getLogLevel(), request); } Response response; @@ -104,15 +86,18 @@ Object executeAndDecode(RequestTemplate template, Options options) throws Throwa .requestTemplate(template) .build(); } catch (IOException e) { - if (logLevel != Logger.Level.NONE) { - logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start)); + if (methodHandlerConfiguration.getLogLevel() != Logger.Level.NONE) { + methodHandlerConfiguration.getLogger().logIOException( + methodHandlerConfiguration.getMetadata().configKey(), + methodHandlerConfiguration.getLogLevel(), e, elapsedTime(start)); } throw errorExecuting(request, e); } long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); return responseHandler.handleResponse( - metadata.configKey(), response, metadata.returnType(), elapsedTime); + methodHandlerConfiguration.getMetadata().configKey(), response, + methodHandlerConfiguration.getMetadata().returnType(), elapsedTime); } long elapsedTime(long start) { @@ -120,21 +105,23 @@ long elapsedTime(long start) { } Request targetRequest(RequestTemplate template) { - for (RequestInterceptor interceptor : requestInterceptors) { + for (RequestInterceptor interceptor : methodHandlerConfiguration.getRequestInterceptors()) { interceptor.apply(template); } - return target.apply(template); + return methodHandlerConfiguration.getTarget().apply(template); } Options findOptions(Object[] argv) { if (argv == null || argv.length == 0) { - return this.options.getMethodOptions(metadata.method().getName()); + return this.methodHandlerConfiguration.getOptions() + .getMethodOptions(methodHandlerConfiguration.getMetadata().method().getName()); } return Stream.of(argv) .filter(Options.class::isInstance) .map(Options.class::cast) .findFirst() - .orElse(this.options.getMethodOptions(metadata.method().getName())); + .orElse(this.methodHandlerConfiguration.getOptions() + .getMethodOptions(methodHandlerConfiguration.getMetadata().method().getName())); } static class Factory implements MethodHandler.Factory { @@ -167,14 +154,16 @@ static class Factory implements MethodHandler.Factory { this.options = checkNotNull(options, "options"); } + @Override public MethodHandler create(Target target, MethodMetadata md, Object requestContext) { final RequestTemplate.Factory buildTemplateFromArgs = requestTemplateFactoryResolver.resolve(target, md); - return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, - logger, logLevel, md, buildTemplateFromArgs, options, - responseHandler, propagationPolicy); + MethodHandlerConfiguration methodHandlerConfiguration = + new MethodHandlerConfiguration(md, target, retryer, requestInterceptors, logger, logLevel, + buildTemplateFromArgs, options, propagationPolicy); + return new SynchronousMethodHandler(methodHandlerConfiguration, client, responseHandler); } } } diff --git a/pom.xml b/pom.xml index d740a420f..b2eae8dd1 100644 --- a/pom.xml +++ b/pom.xml @@ -818,6 +818,20 @@ + + quickbuild + + true + true + true + true + true + true + true + true + + + windows