Skip to content
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

Add ability to set max response HTTP Header Size on httpclient #1457

Closed
biagioT opened this issue Dec 5, 2019 · 18 comments
Closed

Add ability to set max response HTTP Header Size on httpclient #1457

biagioT opened this issue Dec 5, 2019 · 18 comments

Comments

@biagioT
Copy link

biagioT commented Dec 5, 2019

I can't increase the value of 'max header size' in my Spring Cloud Gateway (Netty based).
Following the suggestions of other threads I tried to write this component:

@Component
public class NettyConfiguration implements WebServerFactoryCustomizer {

  @Value("${server.max-http-header-size}")
  private int maxHeaderSize;

  public void customize(NettyReactiveWebServerFactory container) {
      container.addServerCustomizers(
          httpServer -> httpServer.httpRequestDecoder(
        	    httpRequestDecoderSpec -> httpRequestDecoderSpec.maxHeaderSize(maxHeaderSize)
          )
      );
  }

The result is the following:
"TooLongFrameException: HTTP header is larger than 8192 bytes"

Stacktrace:

at io.netty.handler.codec.http.HttpObjectDecoder$HeaderParser.newException(HttpObjectDecoder.java:843) ~[netty-codec-http-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.handler.codec.http.HttpObjectDecoder$HeaderParser.process(HttpObjectDecoder.java:835) ~[netty-codec-http-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.buffer.AbstractByteBuf.forEachByteAsc0(AbstractByteBuf.java:1350) ~[netty-buffer-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.buffer.AbstractByteBuf.forEachByte(AbstractByteBuf.java:1330) ~[netty-buffer-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.handler.codec.http.HttpObjectDecoder$HeaderParser.parse(HttpObjectDecoder.java:807) ~[netty-codec-http-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.handler.codec.http.HttpObjectDecoder.readHeaders(HttpObjectDecoder.java:572) ~[netty-codec-http-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.handler.codec.http.HttpObjectDecoder.decode(HttpObjectDecoder.java:218) ~[netty-codec-http-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.handler.codec.http.HttpClientCodec$Decoder.decode(HttpClientCodec.java:202) ~[netty-codec-http-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:503) ~[netty-codec-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:442) ~[netty-codec-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:281) ~[netty-codec-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253) ~[netty-transport-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) ~[netty-transport-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) ~[netty-transport-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352) ~[netty-transport-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1422) ~[netty-transport-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) ~[netty-transport-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) ~[netty-transport-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:931) ~[netty-transport-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:792) ~[netty-transport-native-epoll-4.1.43.Final-linux-x86_64.jar:4.1.43.Final] 
at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:502) ~[netty-transport-native-epoll-4.1.43.Final-linux-x86_64.jar:4.1.43.Final] 
at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:407) ~[netty-transport-native-epoll-4.1.43.Final-linux-x86_64.jar:4.1.43.Final] 
at io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1050) ~[netty-common-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)  ~[netty-common-4.1.43.Final.jar:4.1.43.Final] 
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)  ~[netty-common-4.1.43.Final.jar:4.1.43.Final] 
at java.base/java.lang.Thread.run(Thread.java:844) ~[na:na]

Spring cloud version: Greenwich.RELEASE (I also tried with Hoxton.RELEASE).

@biagioT biagioT changed the title How to increase HTTP Header Size How to increase max HTTP Header Size Dec 5, 2019
@spencergibb
Copy link
Member

It should be @Configuration, not @Component.

@biagioT
Copy link
Author

biagioT commented Dec 5, 2019

It still does not work. I tried to debug the Netty source code and the customization is taken but ignored.

I noticed that a further customization is added:

Class EmbeddedWebServerFactoryCustomizerAutoConfiguration:

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {

       ...
 
        /**
	 * Nested configuration if Netty is being used.
	 */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(HttpServer.class)
	public static class NettyWebServerFactoryCustomizerConfiguration {

		@Bean
		public NettyWebServerFactoryCustomizer nettyWebServerFactoryCustomizer(Environment environment,
				ServerProperties serverProperties) {
			return new NettyWebServerFactoryCustomizer(environment, serverProperties);
		}

	}
}

Class NettyWebServerFactoryCustomizer

...
@Override
	public void customize(NettyReactiveWebServerFactory factory) {
		factory.setUseForwardHeaders(getOrDeduceUseForwardHeaders());
		PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
		propertyMapper.from(this.serverProperties::getMaxHttpHeaderSize)
				.to((maxHttpRequestHeaderSize) -> customizeMaxHttpHeaderSize(factory, maxHttpRequestHeaderSize));
		propertyMapper.from(this.serverProperties::getConnectionTimeout)
				.to((connectionTimeout) -> customizeGenericConnectionTimeout(factory, connectionTimeout));
		ServerProperties.Netty nettyProperties = this.serverProperties.getNetty();
		propertyMapper.from(nettyProperties::getConnectionTimeout).whenNonNull()
				.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
	}
...
private void customizeMaxHttpHeaderSize(NettyReactiveWebServerFactory factory, DataSize maxHttpHeaderSize) {
		factory.addServerCustomizers((httpServer) -> httpServer.httpRequestDecoder(
				(httpRequestDecoderSpec) -> httpRequestDecoderSpec.maxHeaderSize((int) maxHttpHeaderSize.toBytes())));
	}

Also in this case maximum header size ​​is set and also in this case it is ignored then by Netty.

@spencergibb
Copy link
Member

so, not sure this is a gateway issue, maybe boot or reactor netty?

@biagioT
Copy link
Author

biagioT commented Dec 5, 2019

it would seem so

@spencergibb
Copy link
Member

@philwebb or @simonbasle or @smaldini thoughts on where this might end up?

@philwebb
Copy link

philwebb commented Dec 5, 2019

I'm having a hard time reading the issue, can you fix the markdown formatting?

@biagioT
Copy link
Author

biagioT commented Dec 6, 2019

I'm having a hard time reading the issue, can you fix the markdown formatting?

Sorry for formatting, I tried to fix it.
In short, my problem is that I can't set the max header size value in my (Spring) Cloud Gateway.

@philwebb
Copy link

philwebb commented Dec 6, 2019

Thanks @biagioT, can you also put the three backticks (```) around the stacktrace? That's the area I'm most interested in reviewing.

@biagioT
Copy link
Author

biagioT commented Dec 6, 2019

Thanks @biagioT, can you also put the three backticks (```) around the stacktrace? That's the area I'm most interested in reviewing.

done

@biagioT
Copy link
Author

biagioT commented Dec 6, 2019

I give you more information about my problem, hoping it will be useful: the error occurs when I invoke a rest service (obviously passing through the gateway) with a bearer token (15 kb) in the request header; the service is performed correctly and at the end of the execution inserts the token (the same or refreshed) in the response header but something happens after that. So it might seem that the gateway lets pass the request but not the response.

(Should we also increase HttpResponseDecoderSpec maxHeaderSize?)

@philwebb
Copy link

philwebb commented Dec 6, 2019

The 8192 value is the default used by Netty (see HttpObjectDecoder) so it looks like your WebServerFactoryCustomizer isn't getting applied to the correct item. I'm not sure if this is a bug in Spring Boot or in Reactor Netty or if some other value needs to be customized.

I think we ideally could do with a way to replicate the issue ourselves then we can assign it to the appropriate team.

@biagioT Would you be able to share some code that reproduces the problem so that we can debug it locally. It doesn't need to be your real application (in fact it's better if it's not), it just need to be something that we can run locally.

@biagioT
Copy link
Author

biagioT commented Dec 6, 2019

I created the following project: https://github.com/biagioT/spring-cloud-test
To reproduce the problem, run the test in the ClientApiTest class after starting Gateway and API service.

@philwebb
Copy link

philwebb commented Dec 6, 2019

Thanks very much for the sample @biagioT. It looks to me like this isn't a Spring Boot issue but something that will need changes in Spring Clould Gateway.

The customization that you're configuring are getting applied, but the problem is that the exception is thrown from client component of Spring Cloud Gateway, not the server part.

I think Spring Cloud Gateway needs to configure the HttpClient in NettyRoutingFilter with a HttpResponseDecoderSpec that uses a bigger maxHeaderSize value.

It looks to me like the HttpClient is created in GatewayAutoConfiguration. I think it's possible for the tcpConfiguration method to use TcpClient.bootstrap with the result from a HttpResponseDecoderSpec.build().

I must admit. I don't know the reactor netty code all that well so I might be wrong.

Here's the relevent stack trace showing where the HttpClientConfiguration is currently initialized.

Daemon Thread [reactor-http-nio-5] (Suspended (entry into method <init> in HttpClientConfiguration))	
	HttpClientConfiguration.<init>() line: 69	
	HttpClientConfiguration.getOrCreate(Bootstrap) line: 108	
	HttpClientConfiguration.headers(Bootstrap, HttpHeaders) line: 203	
	HttpClientHeaders.apply(Bootstrap) line: 56	
	HttpClientHeaders.apply(Object) line: 31	
	TcpClientBootstrap.configure() line: 39	
	TcpClientBootstrap.configure() line: 39	
	TcpClientBootstrap.configure() line: 39	
	TcpClientBootstrap.configure() line: 39	
	TcpClientBootstrap(TcpClient).connect() line: 181	
	HttpClientFinalizer.connect() line: 68	
	HttpClientFinalizer.responseConnection(BiFunction<HttpClientResponse,Connection,Publisher<V>>) line: 85	
	NettyRoutingFilter.filter(ServerWebExchange, GatewayFilterChain) line: 137	
	FilteringWebHandler$GatewayFilterAdapter.filter(ServerWebExchange, GatewayFilterChain) line: 138	
	OrderedGatewayFilter.filter(ServerWebExchange, GatewayFilterChain) line: 44	
	FilteringWebHandler$DefaultGatewayFilterChain.lambda$filter$0(ServerWebExchange) line: 118	
	1276040704.get() line: not available	
	MonoDefer<T>.subscribe(CoreSubscriber<? super T>) line: 44	
	MonoDefer<T>.subscribe(CoreSubscriber<? super T>) line: 52	
	MonoDefer<T>.subscribe(CoreSubscriber<? super T>) line: 52	
	MonoDefer<T>.subscribe(CoreSubscriber<? super T>) line: 52	
	MonoDefer<T>.subscribe(CoreSubscriber<? super T>) line: 52	
	MonoPeekTerminal<T>(Mono<T>).subscribe(Subscriber<? super T>) line: 4087	
	MonoIgnoreThen$ThenIgnoreMain<T>.drain() line: 172	
	MonoIgnoreThen<T>.subscribe(CoreSubscriber<? super T>) line: 56	
	MonoPeekFuseable<T>(InternalMonoOperator<I,O>).subscribe(CoreSubscriber<? super O>) line: 55	
	MonoDefer<T>.subscribe(CoreSubscriber<? super T>) line: 52	
	MonoDefer<T>.subscribe(CoreSubscriber<? super T>) line: 52	
	MonoDoFinally<T>(InternalMonoOperator<I,O>).subscribe(CoreSubscriber<? super O>) line: 55	
	MonoDefer<T>.subscribe(CoreSubscriber<? super T>) line: 52	
	MonoDefer<T>(Mono<T>).subscribe(Subscriber<? super T>) line: 4087	
	MonoIgnoreThen$ThenIgnoreMain<T>.drain() line: 172	
	MonoIgnoreThen<T>.subscribe(CoreSubscriber<? super T>) line: 56	
	MonoFlatMap$FlatMapMain<T,R>.onNext(T) line: 150	
	FluxSwitchIfEmpty$SwitchIfEmptySubscriber<T>.onNext(T) line: 67	
	MonoNext$NextSubscriber<T>.onNext(T) line: 76	
	FluxConcatMap$ConcatMapImmediate<T,R>.innerNext(R) line: 274	
	FluxConcatMap$ConcatMapInner<R>.onNext(R) line: 851	
	FluxMap$MapSubscriber<T,R>.onNext(T) line: 114	
	FluxSwitchIfEmpty$SwitchIfEmptySubscriber<T>.onNext(T) line: 67	
	MonoFlatMap$FlatMapMain<T,R>(Operators$MonoSubscriber<I,O>).complete(O) line: 1592	
	MonoFlatMap$FlatMapMain<T,R>.onNext(T) line: 144	
	FluxMap$MapSubscriber<T,R>.onNext(T) line: 114	
	MonoNext$NextSubscriber<T>.onNext(T) line: 76	
	FluxConcatMap$ConcatMapImmediate<T,R>.innerNext(R) line: 274	
	FluxConcatMap$ConcatMapInner<R>.onNext(R) line: 851	
	FluxOnErrorResume$ResumeSubscriber<T>.onNext(T) line: 73	
	MonoPeekTerminal$MonoTerminalPeekSubscriber<T>.onNext(T) line: 173	
	MonoFilterWhen$MonoFilterWhenMain<T>(Operators$MonoSubscriber<I,O>).complete(O) line: 1592	
	MonoFilterWhen$MonoFilterWhenMain<T>.onNext(T) line: 140	
	Operators$ScalarSubscription<T>.request(long) line: 2148	
	MonoFilterWhen$MonoFilterWhenMain<T>.onSubscribe(Subscription) line: 103	
	MonoJust<T>.subscribe(CoreSubscriber<? super T>) line: 54	
	MonoOnErrorResume<T>(Mono<T>).subscribe(Subscriber<? super T>) line: 4087	
	FluxConcatMap$ConcatMapImmediate<T,R>.drain() line: 441	
	FluxConcatMap$ConcatMapImmediate<T,R>.onNext(T) line: 243	
	FluxDematerialize$DematerializeSubscriber<T>.onNext(Signal<T>) line: 91	
	FluxDematerialize$DematerializeSubscriber<T>.onNext(Object) line: 38	
	FluxIterable$IterableSubscription<T>.slowPath(long) line: 243	
	FluxIterable$IterableSubscription<T>.request(long) line: 201	
	FluxDematerialize$DematerializeSubscriber<T>.request(long) line: 120	
	FluxConcatMap$ConcatMapImmediate<T,R>.onSubscribe(Subscription) line: 228	
	FluxDematerialize$DematerializeSubscriber<T>.onSubscribe(Subscription) line: 70	
	FluxIterable<T>.subscribe(CoreSubscriber<? super T>, Iterator<? extends T>, Runnable) line: 139	
	FluxIterable<T>.subscribe(CoreSubscriber<? super T>) line: 63	
	FluxDematerialize<T>(InternalFluxOperator<I,O>).subscribe(CoreSubscriber<? super O>) line: 53	
	FluxDefer<T>.subscribe(CoreSubscriber<? super T>) line: 54	
	MonoMap<T,R>(Mono<T>).subscribe(Subscriber<? super T>) line: 4087	
	FluxConcatMap$ConcatMapImmediate<T,R>.drain() line: 441	
	FluxConcatMap$ConcatMapImmediate<T,R>.onSubscribe(Subscription) line: 211	
	FluxIterable<T>.subscribe(CoreSubscriber<? super T>, Iterator<? extends T>, Runnable) line: 139	
	FluxIterable<T>.subscribe(CoreSubscriber<? super T>) line: 63	
	MonoFlatMap<T,R>(InternalMonoOperator<I,O>).subscribe(CoreSubscriber<? super O>) line: 55	
	MonoDefer<T>.subscribe(CoreSubscriber<? super T>) line: 52	
	MonoOnAssembly<T>(InternalMonoOperator<I,O>).subscribe(CoreSubscriber<? super O>) line: 55	
	MonoDefer<T>.subscribe(CoreSubscriber<? super T>) line: 52	
	MonoOnErrorResume<T>(Mono<T>).subscribe(Subscriber<? super T>) line: 4087	
	MonoIgnoreThen$ThenIgnoreMain<T>.drain() line: 172	
	MonoIgnoreThen<T>.subscribe(CoreSubscriber<? super T>) line: 56	
	MonoPeekTerminal<T>(InternalMonoOperator<I,O>).subscribe(CoreSubscriber<? super O>) line: 55	
	HttpServerHandle.onStateChange(Connection, ConnectionObserver$State) line: 64	
	TcpServerBind$ChildObserver.onStateChange(Connection, ConnectionObserver$State) line: 226	
	HttpServerOperations.onInboundNext(ChannelHandlerContext, Object) line: 441	
	ChannelOperationsHandler.channelRead(ChannelHandlerContext, Object) line: 93	
	DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelRead(Object) line: 374	
	AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext, Object) line: 360	
	DefaultChannelHandlerContext(AbstractChannelHandlerContext).fireChannelRead(Object) line: 352	
	HttpTrafficHandler.channelRead(ChannelHandlerContext, Object) line: 166	
	DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelRead(Object) line: 374	
	AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext, Object) line: 360	
	DefaultChannelHandlerContext(AbstractChannelHandlerContext).fireChannelRead(Object) line: 352	
	CombinedChannelDuplexHandler$1(CombinedChannelDuplexHandler$DelegatingChannelHandlerContext).fireChannelRead(Object) line: 438	
	ByteToMessageDecoder.fireChannelRead(ChannelHandlerContext, CodecOutputList, int) line: 326	
	ByteToMessageDecoder.fireChannelRead(ChannelHandlerContext, List<Object>, int) line: 313	
	HttpServerCodec$HttpServerRequestDecoder(ByteToMessageDecoder).callDecode(ChannelHandlerContext, ByteBuf, List<Object>) line: 427	
	HttpServerCodec$HttpServerRequestDecoder(ByteToMessageDecoder).channelRead(ChannelHandlerContext, Object) line: 281	
	HttpServerCodec(CombinedChannelDuplexHandler<I,O>).channelRead(ChannelHandlerContext, Object) line: 253	
	DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelRead(Object) line: 374	
	AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext, Object) line: 360	
	DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).fireChannelRead(Object) line: 352	
	DefaultChannelPipeline$HeadContext.channelRead(ChannelHandlerContext, Object) line: 1422	
	DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).invokeChannelRead(Object) line: 374	
	AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext, Object) line: 360	
	DefaultChannelPipeline.fireChannelRead(Object) line: 931	
	NioSocketChannel$NioSocketChannelUnsafe(AbstractNioByteChannel$NioByteUnsafe).read() line: 163	
	NioEventLoop.processSelectedKey(SelectionKey, AbstractNioChannel) line: 700	
	NioEventLoop.processSelectedKeysOptimized() line: 635	
	NioEventLoop.processSelectedKeys() line: 552	
	NioEventLoop.run() line: 514	
	SingleThreadEventExecutor$6.run() line: 1050	
	ThreadExecutorMap$2.run() line: 74	
	FastThreadLocalRunnable.run() line: 30	
	DefaultLoopResources$EventLoop(Thread).run() line: 834	

@philwebb
Copy link

philwebb commented Dec 6, 2019

@spencergibb I think this issue needs to stay in spring-cloud-gateway
for now. If my analysis is wrong then let me know and I'll look again.

@biagioT
Copy link
Author

biagioT commented Dec 6, 2019

Thanks for the support. For now I have solved the problem by defining the HttpClient bean, identical to GatewayAutoConfiguration definition but with an addition:

@Bean
	public HttpClient gatewayHttpClient(HttpClientProperties properties) {

		// configure pool resources
		HttpClientProperties.Pool pool = properties.getPool();

		ConnectionProvider connectionProvider;
		if (pool.getType() == DISABLED) {
			connectionProvider = ConnectionProvider.newConnection();
		} else if (pool.getType() == FIXED) {
			connectionProvider = ConnectionProvider.fixed(pool.getName(), pool.getMaxConnections(),
					pool.getAcquireTimeout());
		} else {
			connectionProvider = ConnectionProvider.elastic(pool.getName());
		}

		HttpClient httpClient = HttpClient.create(connectionProvider)

				.httpResponseDecoder(new Function<HttpResponseDecoderSpec, HttpResponseDecoderSpec>() {

					@Override
					public HttpResponseDecoderSpec apply(HttpResponseDecoderSpec t) {
						t.maxHeaderSize((int) maxHeaderSize.toBytes());
						return t;
					}
				})

				.tcpConfiguration(tcpClient -> {

					if (properties.getConnectTimeout() != null) {
						tcpClient = tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
								properties.getConnectTimeout());
					}

					// configure proxy if proxy host is set.
					HttpClientProperties.Proxy proxy = properties.getProxy();

					if (StringUtils.hasText(proxy.getHost())) {

						tcpClient = tcpClient.proxy(proxySpec -> {
							ProxyProvider.Builder builder = proxySpec.type(ProxyProvider.Proxy.HTTP)
									.host(proxy.getHost());

							PropertyMapper map = PropertyMapper.get();

							map.from(proxy::getPort).whenNonNull().to(builder::port);
							map.from(proxy::getUsername).whenHasText().to(builder::username);
							map.from(proxy::getPassword).whenHasText().to(password -> builder.password(s -> password));
							map.from(proxy::getNonProxyHostsPattern).whenHasText().to(builder::nonProxyHosts);
						});
					}

					return tcpClient;
				});

		HttpClientProperties.Ssl ssl = properties.getSsl();
		if ((ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0)
				|| ssl.getTrustedX509CertificatesForTrustManager().length > 0 || ssl.isUseInsecureTrustManager()) {
			httpClient = httpClient.secure(sslContextSpec -> {
				// configure ssl
				SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();

				X509Certificate[] trustedX509Certificates = ssl.getTrustedX509CertificatesForTrustManager();
				if (trustedX509Certificates.length > 0) {
					sslContextBuilder = sslContextBuilder.trustManager(trustedX509Certificates);
				} else if (ssl.isUseInsecureTrustManager()) {
					sslContextBuilder = sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
				}

				try {
					sslContextBuilder = sslContextBuilder.keyManager(ssl.getKeyManagerFactory());
				} catch (Exception e) {
					logger.error("", e);
				}

				sslContextSpec.sslContext(sslContextBuilder).defaultConfiguration(ssl.getDefaultConfigurationType())
						.handshakeTimeout(ssl.getHandshakeTimeout())
						.closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
						.closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
			});
		}

		if (properties.isWiretap()) {
			httpClient = httpClient.wiretap(true);
		}

		return httpClient;
	}

@yhnsatish
Copy link

Hi guys

Thanks a lot for the workaround.It saved us from a bad situation just before the release.

I struggled a bit though, to get it working, because we were using spring cloud 'Greenwich.Release' which packaged io.projectreactor.netty:reactor-netty:0.8.5.RELEASE but since the httpResponseDecoder() API is exposed only in 0.8.9.RELEASE version or later, I naively added io.projectreactor.netty:reactor-netty:0.8.9.RELEASE as explict dependency, but then gateway started throwing 400 Bad request for every request.

Finally relaized that I had to upgrade the full webflux stack, so updating the springBootVersion to "2.1.6.RELEASE" upgraded the webflux to '2.1.6.RELEASE' which packaged '0.8.9.RELEASE' version of reactor-netty.
And that solved the problem.

Cheers
Satish

@spencergibb
Copy link
Member

@yhnsatish upgrade to Greenwich.SR4

@spencergibb spencergibb changed the title How to increase max HTTP Header Size Add ability to set max response HTTP Header Size on httpclient Jan 14, 2020
@spencergibb spencergibb added this to the 2.1.5.RELEASE milestone Jan 14, 2020
@Joko-cgi
Copy link

It's 2024 and I just had the same issue with my spring cloud gateway (spring boot 3.3.1). During backend execution (after the gateway) we add a custom token to the response (way bigger then 8KB) and we observed the exact same behaviour.

I did some deep source code analysis to find anything to customize, and then I found the org.springframework.cloud.gateway.config.HttpClientProperties class. It can be customized via application properties: spring.cloud.gateway.httpclient.*

Setting the property spring.cloud.gateway.httpclient.max-header-size=40KB has resolved my problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants