title: Spring Cloud Gateway throws 500 error when remote server close connection
body: <p>I implemented custom GatewayFilterFactory to implement custom security. I am observing intermittent issue which occurs when connection abruptly closed between gateway and remote server.</p>
  
  <p>Spring cloud gateway swallow exception and throwing 500 internal server error to the client.</p>
  
  <p>Complete stacktrace-</p>
  
  <pre><code>java.io.IOException: An existing connection was forcibly closed by the remote host
                at sun.nio.ch.SocketDispatcher.read0(Native Method) ~[na:1.8.0_201]
                at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43) ~[na:1.8.0_201]
                at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223) ~[na:1.8.0_201]
                at sun.nio.ch.IOUtil.read(IOUtil.java:192) ~[na:1.8.0_201]
                at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380) ~[na:1.8.0_201]
                at io.netty.buffer.PooledUnsafeDirectByteBuf.setBytes(PooledUnsafeDirectByteBuf.java:288) ~[netty-buffer-4.1.33.Final.jar:4.1.33.Final]
                at io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:1132) ~[netty-buffer-4.1.33.Final.jar:4.1.33.Final]
                at io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:347) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
                at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:148) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
                at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:677) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
                at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:612) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
                at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:529) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
                at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:491) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
                at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:905) ~[netty-common-4.1.33.Final.jar:4.1.33.Final]
                at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_201]
  
  2019-11-21 23:14:26.923 DEBUG [GatewayRoutingService,723236e66f776061,723236e66f776061,false] 18400 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping   : Route matched: grs_rapid_mercht_search_api_node
  
  
  avax.net.ssl.SSLException: handshake timed out
                at io.netty.handler.ssl.SslHandler.handshake(...)(Unknown Source) ~[netty-handler-4.1.33.Final.jar:4.1.33.Final]
  </code></pre>
  
  <p>Sample Implementation-</p>
  
  <pre><code>public class MyFilterFactory implements GatewayFilterFactory&lt;JwtService.JwtConfig&gt; {
  private static  final  Logger LOGGER = LogManager.getLogger(JwtFilterFactory.class);
  public static final String RETRY_ITERATION_KEY = "retry_iteration";
  
  @Override
  public GatewayFilter apply(JwtService.JwtConfig config) {
  
          Predicate&lt;RepeatContext&lt;ServerWebExchange&gt;&gt; repeatPredicate = context -&gt; {
              ServerWebExchange exchange = context.applicationContext();
              if (exceedsMaxIterations(exchange, config)) {
                  return false;
              }
  
              HttpStatus statusCode = exchange.getResponse().getStatusCode();
              return config.getUnAuthStatusCode().equals(statusCode)|| statusCode.is5xxServerError();
          };
  
      Repeat&lt;ServerWebExchange&gt;  statusCodeRepeat = Repeat.onlyIf(repeatPredicate)
                  .doOnRepeat(context -&gt; reset(context.applicationContext()));
  
      return (exchange, chain) -&gt; {
  
          Publisher&lt;Void&gt; publisher = chain.filter(exchange.mutate().request(
                  exchange.getRequest().mutate().
                          //Populate Request header
                                  header(HttpHeaders.AUTHORIZATION,jwtTokenFormatted)
                          .build()).build())
                  .doOnSuccessOrError((aVoid, throwable) -&gt; {
                      int iteration = exchange
                              .getAttributeOrDefault(RETRY_ITERATION_KEY, -1);
                      int newIteration = iteration + 1;
  
                      exchange.getAttributes().put(RETRY_ITERATION_KEY, newIteration);
                  });
  
          if (statusCodeRepeat != null) {
              publisher = ((Mono&lt;Void&gt;) publisher)
                      .repeatWhen(statusCodeRepeat.withApplicationContext(exchange));
          }
  
          return Mono.fromDirect(publisher);
      };
  
  }
  
  public boolean exceedsMaxIterations(ServerWebExchange exchange,
                                      JwtService.JwtConfig retryConfig) {
      Integer iteration = exchange.getAttribute(RETRY_ITERATION_KEY);
      // TODO: deal with null iteration
      boolean exceeds = iteration != null &amp;&amp; iteration &gt;= retryConfig.getRetries();
      LOGGER.trace("exceedsMaxIterations %b, iteration %d, configured retries %d", exceeds,
              iteration, retryConfig.getRetries());
      return exceeds;
  }
  
  
  public void reset(ServerWebExchange exchange) {
      LOGGER.debug("Inside reset method :{} ",exchange.getRequest().getHeaders());
      Set&lt;String&gt; addedHeaders = exchange.getAttributeOrDefault(
              CLIENT_RESPONSE_HEADER_NAMES, Collections.emptySet());
      addedHeaders
              .forEach(header -&gt; exchange.getResponse().getHeaders().remove(header));
      exchange.getAttributes().remove(GATEWAY_ALREADY_ROUTED_ATTR);
  
  }
  }
  </code></pre>
  
  <p>Any suggestions for below queries-</p>
  
  <ul>
  <li>How to make sure netty forward connection/handshake error to client instead of throwing 500 internal server error?</li>
  <li>How to reestablish the closed connection?</li>
  </ul>