Set entity contentLength for HttpClientRibbonCommand.
If the content length isn't set on the entity, when http client determines which header to set, it sets transfer-encoding to chunked rather than content-length. This fixes a missing content-length issue when using `HttpClientRibbonCommandFactory`. See gh-1042
This commit is contained in:
@@ -37,6 +37,7 @@ import lombok.Getter;
|
||||
public class RibbonApacheHttpRequest extends ClientRequest implements Cloneable {
|
||||
|
||||
private final String method;
|
||||
private Long contentLength;
|
||||
|
||||
private final MultiValueMap<String, String> headers;
|
||||
|
||||
@@ -44,11 +45,18 @@ public class RibbonApacheHttpRequest extends ClientRequest implements Cloneable
|
||||
|
||||
private final InputStream requestEntity;
|
||||
|
||||
public RibbonApacheHttpRequest(final String method, final URI uri,
|
||||
final Boolean retryable, final MultiValueMap<String, String> headers,
|
||||
final MultiValueMap<String, String> params, final InputStream requestEntity) {
|
||||
this(method, uri, retryable, headers, params, requestEntity, null);
|
||||
}
|
||||
|
||||
public RibbonApacheHttpRequest(final String method, final URI uri,
|
||||
final Boolean retryable, final MultiValueMap<String, String> headers,
|
||||
final MultiValueMap<String, String> params, final InputStream requestEntity) {
|
||||
final MultiValueMap<String, String> params, final InputStream requestEntity, Long contentLength) {
|
||||
|
||||
this.method = method;
|
||||
this.contentLength = contentLength;
|
||||
this.uri = uri;
|
||||
this.isRetriable = retryable;
|
||||
this.headers = headers;
|
||||
@@ -77,6 +85,11 @@ public class RibbonApacheHttpRequest extends ClientRequest implements Cloneable
|
||||
final BasicHttpEntity entity;
|
||||
entity = new BasicHttpEntity();
|
||||
entity.setContent(this.requestEntity);
|
||||
// if the entity contentLength isn't set, transfer-encoding will be set
|
||||
// to chunked in org.apache.http.protocol.RequestContent. See gh-1042
|
||||
if (contentLength != null) {
|
||||
entity.setContentLength(this.contentLength);
|
||||
}
|
||||
builder.setEntity(entity);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.netflix.hystrix.HystrixCommandKey;
|
||||
import com.netflix.hystrix.HystrixCommandProperties;
|
||||
import com.netflix.zuul.constants.ZuulConstants;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Christian Lohmann
|
||||
@@ -100,10 +101,15 @@ public class HttpClientRibbonCommand extends HystrixCommand<ClientHttpResponse>
|
||||
|
||||
protected ClientHttpResponse forward() throws Exception {
|
||||
final RequestContext context = RequestContext.getCurrentContext();
|
||||
Long contentLength = null;
|
||||
String contentLengthHeader = context.getRequest().getHeader("Content-Length");
|
||||
if (StringUtils.hasText(contentLengthHeader)) {
|
||||
contentLength = new Long(contentLengthHeader);
|
||||
}
|
||||
URI uriInstance = new URI(this.uri);
|
||||
RibbonApacheHttpRequest request = new RibbonApacheHttpRequest(this.method,
|
||||
uriInstance, this.retryable, this.headers, this.params,
|
||||
this.requestEntity);
|
||||
this.requestEntity, contentLength);
|
||||
final RibbonApacheHttpResponse response = this.client
|
||||
.executeWithLoadBalancer(request);
|
||||
context.set("ribbonResponse", response);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or 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 org.springframework.cloud.netflix.ribbon.apache;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.junit.Test;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class RibbonApacheHttpRequestTests {
|
||||
|
||||
@Test
|
||||
public void testNullEntity() throws Exception {
|
||||
URI uri = URI.create("http://example.com");
|
||||
LinkedMultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
headers.add("my-header", "my-value");
|
||||
LinkedMultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("myparam", "myparamval");
|
||||
RibbonApacheHttpRequest httpRequest = new RibbonApacheHttpRequest("GET", uri, false,
|
||||
headers, params, null);
|
||||
|
||||
HttpUriRequest request = httpRequest.toRequest(RequestConfig.custom().build());
|
||||
|
||||
assertThat("request is wrong type", request, is(not(instanceOf(HttpEntityEnclosingRequest.class))));
|
||||
assertThat("uri is wrong", request.getURI().toString(), startsWith(uri.toString()));
|
||||
assertThat("my-header is missing", request.getFirstHeader("my-header"), is(notNullValue()));
|
||||
assertThat("my-header is wrong", request.getFirstHeader("my-header").getValue(), is(equalTo("my-value")));
|
||||
assertThat("myparam is missing", request.getURI().getQuery(), is(equalTo("myparam=myparamval")));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNotNullEntity() throws Exception {
|
||||
URI uri = URI.create("http://example.com");
|
||||
LinkedMultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
|
||||
headers.add("Content-Length", "4");
|
||||
String entityValue = "abcd";
|
||||
RibbonApacheHttpRequest httpRequest = new RibbonApacheHttpRequest("POST", uri, false,
|
||||
headers, new LinkedMultiValueMap<String, String>(), new ByteArrayInputStream(entityValue.getBytes()),
|
||||
(long) entityValue.length());
|
||||
|
||||
HttpUriRequest request = httpRequest.toRequest(RequestConfig.custom().build());
|
||||
|
||||
assertThat("request is wrong type", request, is(instanceOf(HttpEntityEnclosingRequest.class)));
|
||||
assertThat("uri is wrong", request.getURI().toString(), startsWith(uri.toString()));
|
||||
assertThat("Content-Length is missing", request.getFirstHeader("Content-Length"), is(notNullValue()));
|
||||
assertThat("Content-Length is wrong", request.getFirstHeader("Content-Length").getValue(), is(equalTo("4")));
|
||||
|
||||
HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) request;
|
||||
assertThat("entity is missing", entityRequest.getEntity(), is(notNullValue()));
|
||||
HttpEntity entity = entityRequest.getEntity();
|
||||
assertThat("contentLength is wrong", entity.getContentLength(), is(equalTo(4L)));
|
||||
assertThat("content is missing", entity.getContent(), is(notNullValue()));
|
||||
String string = StreamUtils.copyToString(entity.getContent(), Charset.forName("UTF-8"));
|
||||
assertThat("content is wrong", string, is(equalTo(entityValue)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user