/*
 * Copyright 2002-2016 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.http.server.reactive;

import java.nio.charset.StandardCharsets;
import java.time.Duration;

import org.junit.Before;
import org.junit.Test;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.codec.BodyExtractors;
import org.springframework.util.Assert;
import org.springframework.web.client.reactive.ClientRequest;
import org.springframework.web.client.reactive.WebClient;

/**
 * @author Sebastien Deleuze
 */
public class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTests {

	private WebClient webClient;

	@Before
	public void setup() throws Exception {
		super.setup();
		this.webClient = WebClient.create(new ReactorClientHttpConnector());
	}

	@Test
	public void writeAndFlushWith() throws Exception {
		ClientRequest<Void> request = ClientRequest.GET("http://localhost:" + port + "/write-and-flush").build();
		Mono<String> result = this.webClient
				.exchange(request)
				.flatMap(response -> response.body(BodyExtractors.toFlux(String.class)))
				.takeUntil(s -> s.endsWith("data1"))
				.reduce((s1, s2) -> s1 + s2);

		StepVerifier.create(result)
				.expectNext("data0data1")
				.expectComplete()
				.verify(Duration.ofSeconds(5L));
	}

	@Test  // SPR-14991
	public void writeAndAutoFlushOnComplete() {
		ClientRequest<Void> request = ClientRequest.GET("http://localhost:" + port + "/write-and-complete").build();
		Mono<String> result = this.webClient
				.exchange(request)
				.flatMap(response -> response.bodyToFlux(String.class))
				.reduce((s1, s2) -> s1 + s2);

		StepVerifier.create(result)
				.consumeNextWith(value -> Assert.isTrue(value.length() == 200000))
				.expectComplete()
				.verify(Duration.ofSeconds(5L));
	}

	@Override
	protected HttpHandler createHttpHandler() {
		return new FlushingHandler();
	}

	private static class FlushingHandler implements HttpHandler {

		@Override
		public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
			String path = request.getURI().getPath();
			if (path.endsWith("write-and-flush")) {
				Flux<Publisher<DataBuffer>> responseBody = Flux
						.intervalMillis(50)
						.map(l -> toDataBuffer("data" + l, response.bufferFactory()))
						.take(2)
						.map(Flux::just);
				responseBody = responseBody.concatWith(Flux.never());
				return response.writeAndFlushWith(responseBody);
			}
			else if (path.endsWith("write-and-complete")){
				Flux<DataBuffer> responseBody = Flux
						.just("0123456789")
						.repeat(20000)
						.map(value -> toDataBuffer(value, response.bufferFactory()));
				return response.writeWith(responseBody);
			}
			return response.writeWith(Flux.empty());
		}

		private DataBuffer toDataBuffer(String value, DataBufferFactory factory) {
			byte[] data = (value).getBytes(StandardCharsets.UTF_8);
			DataBuffer buffer = factory.allocateBuffer(data.length);
			buffer.write(data);
			return buffer;
		}

	}

}

@@ -666,16 +666,37 @@
 ctive;%0A%0A
+import java.io.File;%0A
 import j
@@ -801,24 +801,64 @@
 junit.Test;%0A
+import org.junit.runners.Parameterized;%0A
 import org.r
@@ -1266,293 +1266,996 @@
 ork.
-util.Assert;%0Aimport org.springframework.web.client.reactive.ClientRequest;%0Aimport org.springframework.web.client.reactive.WebClient;%0A%0A/**%0A * @author Sebastien Deleuze%0A */%0Apublic class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTests %7B%0A%0A%09private WebClient webClient;
+http.server.reactive.bootstrap.JettyHttpServer;%0Aimport org.springframework.http.server.reactive.bootstrap.RxNettyHttpServer;%0Aimport org.springframework.http.server.reactive.bootstrap.TomcatHttpServer;%0Aimport org.springframework.http.server.reactive.bootstrap.UndertowHttpServer;%0Aimport org.springframework.util.Assert;%0Aimport org.springframework.web.client.reactive.ClientRequest;%0Aimport org.springframework.web.client.reactive.WebClient;%0A%0A/**%0A * @author Sebastien Deleuze%0A */%0Apublic class FlushingIntegrationTests extends AbstractHttpHandlerIntegrationTests %7B%0A%0A%09private WebClient webClient;%0A%0A%09// TODO To be removed when Reactor Netty test will be green again%0A%09@Parameterized.Parameters(name = %22server %5B%7B0%7D%5D%22)%0A%09public static Object%5B%5D%5B%5D arguments() %7B%0A%09%09File base = new File(System.getProperty(%22java.io.tmpdir%22));%0A%09%09return new Object%5B%5D%5B%5D %7B%0A%09%09%09%09%7Bnew JettyHttpServer()%7D,%0A%09%09%09%09%7Bnew RxNettyHttpServer()%7D,%0A%09%09%09%09%7Bnew TomcatHttpServer(base.getAbsolutePath())%7D,%0A%09%09%09%09%7Bnew UndertowHttpServer()%7D%0A%09%09%7D;%0A%09%7D
 %0A%0A%09@

Disable temporarily reactor-netty in FlushingIntegrationTests