Skip to content

"Unsupported transfer encoding: chunked" When using TestRestTemplate with Apache HTTP client and spring-cloud-starter-function-web #43547

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

Closed
matthew-js-porter opened this issue Dec 17, 2024 · 3 comments
Labels
for: external-project For an external project and not something we can fix status: invalid An issue that we don't feel is valid

Comments

@matthew-js-porter
Copy link

When using TestRestTemplate to call and application using spring-cloud-starter-function-web with org.apache.httpcomponents.client5:httpclient5 a ClientProtocolException error is thrown. The same test runs when using JdkClientHttpRequestFactory instead.

Error

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:61395/": Unsupported transfer encoding: chunked

	at org.springframework.web.client.RestTemplate.createResourceAccessException(RestTemplate.java:926)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:906)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:801)
	at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:518)
	at org.springframework.boot.test.web.client.TestRestTemplate.postForObject(TestRestTemplate.java:390)
	at com.example.apache_test_resttemplate.ApacheTestRestTemplateApplicationTests.saysHello(ApacheTestResttemplateApplicationTests.java:19)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: org.apache.hc.client5.http.ClientProtocolException: Unsupported transfer encoding: chunked
	at org.apache.hc.client5.http.impl.classic.InternalHttpClient.doExecute(InternalHttpClient.java:177)
	at org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:87)
	at org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:55)
	at org.apache.hc.client5.http.classic.HttpClient.executeOpen(HttpClient.java:183)
	at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:99)
	at org.springframework.http.client.AbstractStreamingClientHttpRequest.executeInternal(AbstractStreamingClientHttpRequest.java:71)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:81)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:900)
	... 7 more
Caused by: org.apache.hc.core5.http.NotImplementedException: Unsupported transfer encoding: chunked
	at org.apache.hc.core5.http.impl.DefaultContentLengthStrategy.determineLength(DefaultContentLengthStrategy.java:90)
	at org.apache.hc.core5.http.impl.io.DefaultBHttpClientConnection.receiveResponseEntity(DefaultBHttpClientConnection.java:355)
	at org.apache.hc.core5.http.impl.io.HttpRequestExecutor.execute(HttpRequestExecutor.java:210)
	at org.apache.hc.client5.http.impl.classic.InternalExecRuntime.lambda$execute$0(InternalExecRuntime.java:236)
	at org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager$InternalConnectionEndpoint.execute(PoolingHttpClientConnectionManager.java:791)
	at org.apache.hc.client5.http.impl.classic.InternalExecRuntime.execute(InternalExecRuntime.java:233)
	at org.apache.hc.client5.http.impl.classic.MainClientExec.execute(MainClientExec.java:121)
	at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
	at org.apache.hc.client5.http.impl.classic.ConnectExec.execute(ConnectExec.java:199)
	at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
	at org.apache.hc.client5.http.impl.classic.ProtocolExec.execute(ProtocolExec.java:192)
	at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
	at org.apache.hc.client5.http.impl.classic.ContentCompressionExec.execute(ContentCompressionExec.java:150)
	at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
	at org.apache.hc.client5.http.impl.classic.HttpRequestRetryExec.execute(HttpRequestRetryExec.java:113)
	at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
	at org.apache.hc.client5.http.impl.classic.RedirectExec.execute(RedirectExec.java:110)
	at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
	at org.apache.hc.client5.http.impl.classic.InternalHttpClient.doExecute(InternalHttpClient.java:174)
	... 14 more

Steps to Reproduce

  1. Generate a 3.4.0 project from https://start.spring.io/
  2. add Dependency Management for Spring Cloud
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>2024.0.0</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
  1. add spring-cloud-starter-function-web
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-function-web</artifactId>
</dependency>
  1. Create function handler
    FunctionHandler.java
@Component
public class FunctionHandler implements Function<Person, String> {

    @Override
    public String apply(final Person person) {
        return "Hello %s %s".formatted(person.firstName(), person.lastName());
    }
}

Person.java

public record Person(String firstName, String lastName) {}
  1. Create Test
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApacheTestRestTemplateApplicationTests {


	@Autowired
	private TestRestTemplate testRestTemplate;

	@Test
	void saysHello() {
		final String response = testRestTemplate.postForObject("/", new Person("First", "Last"), String.class);
		assertThat(response).isEqualTo("Hello First Last");
	}
}
  1. Run test and observe that the test is successful.
  2. Add httpclient5 dependency
<dependency>
	<groupId>org.apache.httpcomponents.client5</groupId>
	<artifactId>httpclient5</artifactId>
</dependency>
  1. Run tests and observe error
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Dec 17, 2024
@wilkinsona
Copy link
Member

The response includes two Transfer-Encoding headers:

transfer-encoding: chunked
Transfer-Encoding: chunked

The Apache HTTP client doesn't tolerate this and throws an exception when it processes the second header. As far as I can tell, the transfer-encoding header is being set on the response by org.springframework.cloud.function.web.util.FunctionWebRequestProcessingHelper. It should probably be ignored as, for example, Content-Length currently is.

I think you should raise a Spring Cloud Function issue for this. You may also want to raise an Apache HTTP client issue to see if they want to tolerate multiple Transfer-Encoding headers with the same value.

@wilkinsona wilkinsona closed this as not planned Won't fix, can't repro, duplicate, stale Dec 17, 2024
@wilkinsona wilkinsona added status: invalid An issue that we don't feel is valid for: external-project For an external project and not something we can fix and removed status: waiting-for-triage An issue we've not yet triaged labels Dec 17, 2024
@matthew-js-porter
Copy link
Author

@wilkinsona Thanks! that explains it. Opened the Spring Cloud Function issue:
spring-cloud/spring-cloud-function#1220

@olegz
Copy link
Contributor

olegz commented Dec 18, 2024

@wilkinsona indeed it's on us so feel free to close this issue here and I'll handle it in functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: external-project For an external project and not something we can fix status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

4 participants