question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

RestTemplate - providing custom ResponseErrorHandler does not prevent an error with http 401 responses [SPR-16781]

See original GitHub issue

Jeff Synnestvedt opened SPR-16781 and commented

When a rest template is customized with a ResponseErrorHandler  that does not return true on hasError  or does not throw an exception in handleError for an http 401 response then something like the following is thrown:

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:36639/someservice": cannot retry due to server authentication, in streaming mode; nested exception is java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:741) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:684) at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:629) at com.example.demo.DemoApplicationTests.error401_withcustomhandler_noerrors(DemoApplicationTests.java:118) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73) at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)Caused by: java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1674) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474) at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480) at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:55) at org.springframework.boot.actuate.metrics.web.client.RestTemplateExchangeTags.getStatusMessage(RestTemplateExchangeTags.java:94) at org.springframework.boot.actuate.metrics.web.client.RestTemplateExchangeTags.status(RestTemplateExchangeTags.java:86) at org.springframework.boot.actuate.metrics.web.client.DefaultRestTemplateExchangeTagsProvider.getTags(DefaultRestTemplateExchangeTagsProvider.java:43) at org.springframework.boot.actuate.metrics.web.client.MetricsClientHttpRequestInterceptor.getTimeBuilder(MetricsClientHttpRequestInterceptor.java:97) at org.springframework.boot.actuate.metrics.web.client.MetricsClientHttpRequestInterceptor.intercept(MetricsClientHttpRequestInterceptor.java:70) at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:92) at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:76) at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:727) ... 35 more

I am expecting that if my error handler doesn’t want to consider 401’s errors then it shouldn’t throw an error further down the stack.


Affects: 5.0.5

Reference URL: https://github.com/spring-projects/spring-framework-issues/pull/179

Attachments:

Issue Links:

  • #14004 Update reference documentation on handling 401 response in the RestTemplate (“duplicates”)

1 votes, 3 watchers

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:10

github_iconTop GitHub Comments

3reactions
thomasturrellcommented, Dec 7, 2022

I believe that in Spring 6 the correct dependancy is:

<dependency>
  <groupId>org.apache.httpcomponents.client5</groupId>
  <artifactId>httpclient5</artifactId>
</dependency>
3reactions
spring-projects-issuescommented, Jan 13, 2019

Brian Clozel commented

Hi Jeff Synnestvedt,

First, thanks a lot for your repro project!

I agree this is annoying, but I don’t think there’s anything we can fix here. But there are ways to work around this behavior.

The ResponseErrorHandler contract is about checking whether the given response should be considered as an error - but it doesn’t mean it will catch all errors that can happen when reading/extracting the response body at a later phase (for example, an IOException while reading the response body). This is what’s happening in your sample code.

This behavior is still surprising and there are ways to fix that.

You can choose to use a different HTTP client (i.e. not the JDK one); Spring Framework supports several. Adding the following dependency to your sample fixes things:

<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
</dependency>

This points to an implementation detail in the JDK HTTP client. In Spring Framework, we’re using the SimpleClientHttpResponse to make sure that we use the error stream or the regular input stream, depending on the case. In this example, none of them works.

I suspect this might be linked to a combination of the test server/response used in your setup and the JDK client, since sending the same request against "http://httpbin.org/status/401" works well. The difference should be somewhere in the HttpUrlConnection implementation.

I’m closing this issue for now, but don’t hesitate to reopen it if you’ve found a way to improve this behavior. Thanks!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Spring RestTemplate Error Handling - Baeldung
Learn how to handle errors with Spring's RestTemplate. ... ResponseErrorHandler will read the HTTP status from the response and either:.
Read more >
http - java.net.HttpRetryException: cannot retry due to server ...
So, I've added custom Error handler to restTemplate to treat some error coded as non errors - it helps parse HTTP body very...
Read more >
HttpClientException 401 Unauthorized - JiGyeong's study room
RestTemplate - providing custom ResponseErrorHandler does not prevent an error with http 401 responses [SPR-16781] · Issue #213.
Read more >
RestTemplate Error Handling Example - Apps Developer Blog
Today, we'll implement a RestTemplate Error Handling Example. The objective is to implement a reusable error handler that managesthe errors ...
Read more >
Spring Boot RestTemplate Error Handling
Implementing a custom error handler ... Sometimes, a try-catch block is not enough to handle errors as it is not scalable when the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found