Describe the bug

We have a downstream service with a multipart request endpoint for file upload

Zuul starts to proxy the request, the embedded tomcat in Spring rejects it and throws an exception because it exceeds the spring.servlet.multipart.max-file-size, however Zuul hits /error in the downstream. The original request was a POST request so the /error was called with POST

Another strage behaviour I noticed is that if I handle the MaxUploadSizeExceededException in a @ControllerAdvice then the return value becomes the URL sent downstream. eg) in the below case the url hit in downstream is POST /upload-error

// Downstream called with `/upload-error`
@ExceptionHandler(MaxUploadSizeExceededException.class)
public String handleFileSizeException(MaxUploadSizeExceededException err) {
  log.error("File is too large", err);
  return "upload-error";
}

And this seems to be not specific to MaxUploadSizeExceededException, we noticed the same behaviour when for a SocketTimeoutException, so this seem to happen whenever an Exception is thrown

Can you suggest a possible work around for this?

Possible Behaviour - Request to be rejected with 500, without requesting /error in downstream

Dependencies:

ext {
    set('springCloudVersion', "Hoxton.BUILD-SNAPSHOT")
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-zuul'
}

Repo Reproducing the Issue https://github.com/Mark1626/Paraphernalia/tree/master/spring-proxy-multipart

cc @spencergibb

Comment From: spencergibb

Zuul doesn't make multiple requests downstream. It looks to me that tomcat is changing the request to /error before it gets to zuul. Your proxying everything /** so it all gets proxied. Can you tighten the paths that get proxied?

Comment From: Mark1626

Changing from the /** routes to somthing more strict

zuul:
  routes:
    tomcat:
      path: /upload
      url: http://localhost:8080

After changing the route it does not make the /error call. How does this work under the box?

Comment From: Mark1626

I also want to callout another thing, this is from the documentation

 zuul:
  routes:
    users:
      path: /myusers/**
    legacy:
      path: /**

We have a similar usecase with some legacy systems, which is why I had the route as /** initially. Is there a way to handle this without changing the route?

https://cloud.spring.io/spring-cloud-netflix/multi/multi__router_and_filter_zuul.html#netflix-zuul-reverse-proxy

Comment From: spencergibb

I'm unsure what "How does this work under the box?" mean?

/** will proxy the error unless there is another route that matches /error.

Comment From: yyvess

@spencergibb I confirm the issue, requests are executed twice to downstream on version Hoxton-SR8.

That happen as example after a socket timeout on a backend call.

I suspect that issue is new without to be able to identify the change related to this issue.

My analysis: When no error handlers is setup the class ZuulHandlerMapping forward the request to the backend.

As the PreDecorationFilter is not re-executed, the requestURI value on the RequestContext.getCurrentContext() contains the original Uri and not the Uri /error. That cause the execution of a second call to the backend on the original URI !

I hop that issue can easily fixed on ZuulHandlerMapping.java by adding a check on the key "sendErrorFilter.ran" or the context must be updated with the correct uri.

if (ctx.containsKey("forward.to") || ctx.containsKey("sendErrorFilter.ran") ) {
   return null;

As workaround you can setup an error handler or add this configuration:

error.path: /zuul-error
zuul:
  ignored-patterns:
    - /zuul-error

Comment From: OlgaMaciaszek

@Mark1626 @yyvess Please provide a minimal, complete, verifiable example that reproduces the issue.

Comment From: spring-cloud-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: spring-cloud-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.