From f941d24a8de6614f742138cead420fd046ea69b2 Mon Sep 17 00:00:00 2001 From: Maxim Nesen Date: Tue, 22 Sep 2020 09:40:35 +0200 Subject: [PATCH 1/3] Expect:100-Continue header handling Signed-off-by: Maxim Nesen --- .../jersey/client/ClientProperties.java | 27 +++- .../client/http/Expect100ContinueFeature.java | 50 +++++++ .../client/internal/HttpUrlConnector.java | 25 +++- tests/e2e-client/pom.xml | 5 +- .../Expect100ContinueTest.java | 134 ++++++++++++++++++ 5 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 core-client/src/main/java/org/glassfish/jersey/client/http/Expect100ContinueFeature.java create mode 100644 tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/httpurlconnector/Expect100ContinueTest.java 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..dab9c8cc5a 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"; + /** + * Registers 100-Continue value for Expect header processing to be set in 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 = 64000L; + private ClientProperties() { // prevents instantiation } 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..ecdd2220b2 --- /dev/null +++ b/core-client/src/main/java/org/glassfish/jersey/client/http/Expect100ContinueFeature.java @@ -0,0 +1,50 @@ +/* + * 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); + } + public Expect100ContinueFeature(long thresholdSize) { + this.thresholdSize = thresholdSize; + } + + @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..692f352a43 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 @@ -333,9 +333,16 @@ private ClientResponse _apply(final ClientRequest request) throws IOException { if (entity != null) { RequestEntityProcessing entityProcessing = request.resolveProperty( ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.class); + final Long expectContinueSizeThreshold = ClientProperties.getValue( + request.getConfiguration().getProperties(), + ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, + ClientProperties.DEFAULT_EXPECT_100_CONTINUE_THRESHOLD_SIZE, Long.class); + + final long length = request.getLengthLong(); + final boolean allowStreaming = length > expectContinueSizeThreshold + || entityProcessing == RequestEntityProcessing.CHUNKED; 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 +358,8 @@ private ClientResponse _apply(final ClientRequest request) throws IOException { } } + processExpect100Continue(request, uc, allowStreaming); + request.setStreamProvider(contentLength -> { setOutboundHeaders(request.getStringHeaders(), uc); return uc.getOutputStream(); @@ -527,6 +536,20 @@ public Object run() throws NoSuchFieldException, } } + private void processExpect100Continue(ClientRequest request, HttpURLConnection uc, boolean allowStreaming) { + + final Boolean expectContinueActivated = ClientProperties.getValue(request.getConfiguration().getProperties(), + ClientProperties.EXPECT_100_CONTINUE, Boolean.class); + + 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 @@