Hi,
using Spring Cloud Gateway 2.1.0.M1 with gives me this errors:
log:
java.lang.NoSuchMethodError: reactor.netty.http.client.HttpClient.noChunkedTransfer()Lreactor/netty/http/client/HttpClient;
at org.springframework.cloud.gateway.filter.NettyRoutingFilter.filter(NettyRoutingFilter.java:104) ~[spring-cloud-gateway-core-2.1.0.M1.jar:2.1.0.M1]
at org.springframework.cloud.gateway.handler.FilteringWebHandler$GatewayFilterAdapter.filter(FilteringWebHandler.java:135) ~[spring-cloud-gateway-core-2.1.0.M1.jar:2.1.0.M1]
at org.springframework.cloud.gateway.filter.OrderedGatewayFilter.filter(OrderedGatewayFilter.java:44) ~[spring-cloud-gateway-core-2.1.0.M1.jar:2.1.0.M1]
at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.lambda$filter$0(FilteringWebHandler.java:117) ~[spring-cloud-gateway-core-2.1.0.M1.jar:2.1.0.M1]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) ~[reactor-core-3.2.0.RELEASE.jar:3.2.0.RELEASE]
at reactor.core.publisher.MonoLift.subscribe(MonoLift.java:45) ~[reactor-core-3.2.0.RELEASE.jar:3.2.0.RELEASE]
...
http-client:
org.apache.http.NoHttpResponseException: localhost:8080 failed to respond
Can you share your dependencies. Looks like you have an old version of reactor Netty
build.gradle:
buildscript {
ext {
springBootVersion = '2.1.0.M4'
}
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 10
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
ext {
springCloudVersion = 'Greenwich.M1'
}
dependencies {
implementation('org.springframework.boot:spring-boot-starter-actuator')
implementation('org.springframework.cloud:spring-cloud-starter-config')
implementation('org.springframework.cloud:spring-cloud-starter-gateway')
implementation('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
implementation('org.springframework.cloud:spring-cloud-starter-sleuth')
runtimeOnly('org.springframework.boot:spring-boot-devtools')
testImplementation('org.springframework.boot:spring-boot-starter-test')
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}

@octopus-prime i have run into the same issue and fixed it by override NettyRoutingFilter like this -
/*
* Copyright 2013-2018 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.gateway.filter;
import java.net.URI;
import java.util.List;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.netty.NettyPipeline;
import reactor.netty.http.client.HttpClient;
import reactor.netty.http.client.HttpClientResponse;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.config.HttpClientProperties;
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter.Type;
import org.springframework.cloud.gateway.support.TimeoutException;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.NettyDataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.AbstractServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter.filterRequest;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CLIENT_RESPONSE_CONN_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.PRESERVE_HOST_HEADER_ATTRIBUTE;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.isAlreadyRouted;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setAlreadyRouted;
/**
* @author Spencer Gibb
* @author Biju Kunjummen
*/
public class NettyRoutingFilter implements GlobalFilter, Ordered {
private final HttpClient httpClient;
private final ObjectProvider<List<HttpHeadersFilter>> headersFilters;
private final HttpClientProperties properties;
public NettyRoutingFilter(HttpClient httpClient,
ObjectProvider<List<HttpHeadersFilter>> headersFilters,
HttpClientProperties properties) {
this.httpClient = httpClient;
this.headersFilters = headersFilters;
this.properties = properties;
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
String scheme = requestUrl.getScheme();
if (isAlreadyRouted(exchange) || (!"http".equals(scheme) && !"https".equals(scheme))) {
return chain.filter(exchange);
}
setAlreadyRouted(exchange);
ServerHttpRequest request = exchange.getRequest();
final HttpMethod method = HttpMethod.valueOf(request.getMethod().toString());
final String url = requestUrl.toString();
HttpHeaders filtered = filterRequest(this.headersFilters.getIfAvailable(),
exchange);
final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
filtered.forEach(httpHeaders::set);
String transferEncoding = request.getHeaders().getFirst(HttpHeaders.TRANSFER_ENCODING);
boolean chunkedTransfer = "chunked".equalsIgnoreCase(transferEncoding);
boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);
HttpClient client = this.httpClient.chunkedTransfer(chunkedTransfer);
Flux<HttpClientResponse> responseFlux = client
.request(method)
.uri(url)
.send((req, nettyOutbound) -> {
req.headers(httpHeaders);
if (preserveHost) {
String host = request.getHeaders().getFirst(HttpHeaders.HOST);
req.header(HttpHeaders.HOST, host);
}
return nettyOutbound
.options(NettyPipeline.SendOptions::flushOnEach)
.send(request.getBody().map(dataBuffer ->
((NettyDataBuffer) dataBuffer).getNativeBuffer()));
}).responseConnection((res, connection) -> {
ServerHttpResponse response = exchange.getResponse();
// put headers and status so filters can modify the response
HttpHeaders headers = new HttpHeaders();
res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));
if (headers.getContentType() != null) {
exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR, headers.getContentType());
}
HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(
this.headersFilters.getIfAvailable(), headers, exchange, Type.RESPONSE);
response.getHeaders().putAll(filteredResponseHeaders);
HttpStatus status = HttpStatus.resolve(res.status().code());
if (status != null) {
response.setStatusCode(status);
} else if (response instanceof AbstractServerHttpResponse) {
// https://jira.spring.io/browse/SPR-16748
((AbstractServerHttpResponse) response).setStatusCodeValue(res.status().code());
} else {
throw new IllegalStateException("Unable to set status code on response: " + res.status().code() + ", " + response.getClass());
}
// Defer committing the response until all route filters have run
// Put client response as ServerWebExchange attribute and write response later NettyWriteResponseFilter
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);
return Mono.just(res);
});
if (properties.getResponseTimeout() != null) {
//TODO: figure out how to make this a 504
responseFlux = responseFlux.timeout(properties.getResponseTimeout(),
Mono.error(new TimeoutException("Response took longer than timeout: " +
properties.getResponseTimeout())));
}
return responseFlux.then(chain.filter(exchange));
}
}
Very little change was needed -
HttpClient client = this.httpClient.chunkedTransfer(chunkedTransfer);
This issue is appeared due to HttpClient interface has been changed at reactor-netty-0.8.0.RELAESE, which is used as dependency within Spring-Boot 2.1.0.M4.
Gateway 2.1.0.M1 was built against boot 2.1.0.M3
Facing the same issue with Spring boot 2.1.0.RELEASE. Its using gateway 2.1.0.M1.
The next release will be in a few weeks that will work against 2.1.0.RELEASE
Is it fixed yet ?
It is fixed, release towards the end of the week
Is there any workaround till then ?
Use Finchley and boot 2.0.x or use snapshots with boot 2.1.0
18.11.12 use spring-cloud Finchley.SR2 & boot 2.0.6.Release is OK. @sagar-patro
What if we are using Greenwich.M1?
@Alos
Gateway 2.1.0.M1 was built against boot 2.1.0.M3
That was Greenwich.M1.
To use boot 2.1.0 you need to use Greenwich snapshots until our next release (today or tomorrow).
Thanks!
Most helpful comment
The next release will be in a few weeks that will work against 2.1.0.RELEASE