diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java index e4f20be6a4..b01857adcc 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -405,6 +405,31 @@ public final class ClientProperties { */ public static final String REQUEST_ENTITY_PROCESSING = "jersey.config.client.request.entity.processing"; + /** + * Allows for HTTP Expect:100-Continue being handled by the HttpUrlConnector (default Jersey + * connector). + * + * @since 2.32 + */ + public static final String EXPECT_100_CONTINUE = "jersey.config.client.request.expect.100.continue.processing"; + + /** + * Property for threshold size for content length after which Expect:100-Continue header would be applied + * before the main request. + * + * @since 2.32 + */ + public static final String + EXPECT_100_CONTINUE_THRESHOLD_SIZE = "jersey.config.client.request.expect.100.continue.threshold.size"; + + /** + * Default threshold size (64kb) after which which Expect:100-Continue header would be applied before + * the main request. + * + * @since 2.32 + */ + public static final Long DEFAULT_EXPECT_100_CONTINUE_THRESHOLD_SIZE = 65536L; + private ClientProperties() { // prevents instantiation } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java index 162dedaa96..012ef337da 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -167,7 +167,7 @@ public T resolveProperty(final String name, final T defaultValue) { private T resolveProperty(final String name, Object defaultValue, final Class type) { // Check runtime configuration first - Object result = clientConfig.getProperty(name); + Object result = getConfiguration().getProperty(name); if (result != null) { defaultValue = result; } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/http/Expect100ContinueFeature.java b/core-client/src/main/java/org/glassfish/jersey/client/http/Expect100ContinueFeature.java new file mode 100644 index 0000000000..3af806c994 --- /dev/null +++ b/core-client/src/main/java/org/glassfish/jersey/client/http/Expect100ContinueFeature.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.client.http; + +import org.glassfish.jersey.client.ClientProperties; + +import javax.ws.rs.core.Feature; +import javax.ws.rs.core.FeatureContext; + +public class Expect100ContinueFeature implements Feature { + + private long thresholdSize; + + public Expect100ContinueFeature() { + this(ClientProperties.DEFAULT_EXPECT_100_CONTINUE_THRESHOLD_SIZE); + } + + private Expect100ContinueFeature(long thresholdSize) { + this.thresholdSize = thresholdSize; + } + + /** + * Creates Expect100ContinueFeature with custom (not default) threshold size for content length. + * + * @param thresholdSize size of threshold + * @return Expect100Continue Feature + */ + public static Expect100ContinueFeature withCustomThreshold(long thresholdSize) { + return new Expect100ContinueFeature(thresholdSize); + } + + /** + * Creates Expect100Continue Feature with default threshold size + * + * @return Expect100Continue Feature + */ + public static Expect100ContinueFeature basic() { + return new Expect100ContinueFeature(); + } + + @Override + public boolean configure(FeatureContext configurableContext) { + if (configurableContext.getConfiguration().getProperty( + ClientProperties.EXPECT_100_CONTINUE) == null) { + configurableContext.property(ClientProperties.EXPECT_100_CONTINUE, Boolean.TRUE); + } else { + return false; //Expect:100-Continue handling is already done via property config + } + if (configurableContext.getConfiguration().getProperty( + ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE) == null) { + configurableContext.property(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, thresholdSize); + } + return true; + } + +} \ No newline at end of file diff --git a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java index a4255a7f7c..0f0614c519 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java @@ -334,8 +334,9 @@ private ClientResponse _apply(final ClientRequest request) throws IOException { RequestEntityProcessing entityProcessing = request.resolveProperty( ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.class); + final long length = request.getLengthLong(); + if (entityProcessing == null || entityProcessing != RequestEntityProcessing.BUFFERED) { - final long length = request.getLengthLong(); if (fixLengthStreaming && length > 0) { uc.setFixedLengthStreamingMode(length); } else if (entityProcessing == RequestEntityProcessing.CHUNKED) { @@ -351,6 +352,8 @@ private ClientResponse _apply(final ClientRequest request) throws IOException { } } + processExpect100Continue(request, uc, length, entityProcessing); + request.setStreamProvider(contentLength -> { setOutboundHeaders(request.getStringHeaders(), uc); return uc.getOutputStream(); @@ -527,6 +530,26 @@ public Object run() throws NoSuchFieldException, } } + private static void processExpect100Continue(ClientRequest request, HttpURLConnection uc, + long length, RequestEntityProcessing entityProcessing) { + final Boolean expectContinueActivated = request.resolveProperty( + ClientProperties.EXPECT_100_CONTINUE, Boolean.class); + final Long expectContinueSizeThreshold = request.resolveProperty( + ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, + ClientProperties.DEFAULT_EXPECT_100_CONTINUE_THRESHOLD_SIZE); + + final boolean allowStreaming = length > expectContinueSizeThreshold + || entityProcessing == RequestEntityProcessing.CHUNKED; + + if (!Boolean.TRUE.equals(expectContinueActivated) + || !("POST".equals(uc.getRequestMethod()) || "PUT".equals(uc.getRequestMethod())) + || !allowStreaming + ) { + return; + } + uc.setRequestProperty("Expect", "100-Continue"); + } + @Override public String getName() { return "HttpUrlConnection " + AccessController.doPrivileged(PropertiesHelper.getSystemProperty("java.version")); diff --git a/tests/e2e-client/pom.xml b/tests/e2e-client/pom.xml index 78517271fc..6a58e5256a 100644 --- a/tests/e2e-client/pom.xml +++ b/tests/e2e-client/pom.xml @@ -1,7 +1,7 @@