package org.apache.camel.component.undertow;

import org.junit.Test;
import org.xnio.XnioIoThread;
import org.xnio.channels.EmptyStreamSourceChannel;
import org.xnio.channels.StreamSourceChannel;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

public class DefaultUndertowHttpBindingTest {

    @Test(timeout = 1000)
    public void readEntireDelayedPayload() throws Exception {
        String[] delayedPayloads = new String[] {
                "",
                "chunk",
        };

        StreamSourceChannel source = source(delayedPayloads);

        DefaultUndertowHttpBinding binding = new DefaultUndertowHttpBinding();
        String result = new String(binding.readFromChannel(source));

        checkResult(result, delayedPayloads);
    }

    @Test(timeout = 1000)
    public void readEntireMultiDelayedPayload() throws Exception {
        String[] delayedPayloads = new String[] {
                "",
                "first ",
                "second",
        };

        StreamSourceChannel source = source(delayedPayloads);

        DefaultUndertowHttpBinding binding = new DefaultUndertowHttpBinding();
        String result = new String(binding.readFromChannel(source));

        checkResult(result, delayedPayloads);
    }

    private void checkResult(String result, String[] delayedPayloads) {
        assertThat(result, is(
                Stream.of(delayedPayloads)
                        .collect(Collectors.joining())));
    }

    @Test(timeout = 1000)
    public void readEntireMultiDelayedWithPausePayload() throws Exception {
        String[] delayedPayloads = new String[] {
                "",
                "first ",
                "",
                "second",
        };

        StreamSourceChannel source = source(delayedPayloads);

        DefaultUndertowHttpBinding binding = new DefaultUndertowHttpBinding();
        String result = new String(binding.readFromChannel(source));

        checkResult(result, delayedPayloads);
    }

    private StreamSourceChannel source(final String[] delayedPayloads) {
        Thread sourceThread = Thread.currentThread();

        return new EmptyStreamSourceChannel(thread()) {
            int chunk = 0;
            boolean mustWait = false;  // make sure that the caller is not spinning on read==0

            @Override
            public int read(ByteBuffer dst) throws IOException {
                if (mustWait) {
                    fail("must wait before reading");
                }
                if (chunk < delayedPayloads.length) {
                    byte[] delayedPayload = delayedPayloads[chunk].getBytes();
                    dst.put(delayedPayload);
                    chunk++;
                    if (delayedPayload.length == 0) {
                        mustWait = true;
                    }
                    return delayedPayload.length;
                }
                return -1;
            }

            @Override
            public void resumeReads() {
                /**
                 * {@link io.undertow.server.HttpServerExchange.ReadDispatchChannel} delays resumes in the main thread
                 */
                if (sourceThread != Thread.currentThread()) {
                    super.resumeReads();
                }
            }

            @Override
            public void awaitReadable() throws IOException {
                mustWait = false;
                super.awaitReadable();
            }
        };
    }

    private XnioIoThread thread() {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        return new XnioIoThread(null, 0) {
            @Override
            public void execute(Runnable runnable) {
                executor.execute(runnable);
            }

            @Override
            public Key executeAfter(Runnable runnable, long l, TimeUnit timeUnit) {
                execute(runnable);
                return null;
            }

            @Override
            public Key executeAtInterval(Runnable runnable, long l, TimeUnit timeUnit) {
                execute(runnable);
                return null;
            }
        };
    }
}
@@ -1,198 +1,851 @@
-package org.apache.camel.component.undertow;%0A%0Aimport org.junit.Test;%0Aimport org.xnio.XnioIoThread;%0Aimport org.xnio.channels.EmptyStreamSourceChannel;%0Aimport org.xnio.channels.StreamSourceChannel
+/**%0A * Licensed to the Apache Software Foundation (ASF) under one or more%0A * contributor license agreements.  See the NOTICE file distributed with%0A * this work for additional information regarding copyright ownership.%0A * The ASF licenses this file to You under the Apache License, Version 2.0%0A * (the %22License%22); you may not use this file except in compliance with%0A * the License.  You may obtain a copy of the License at%0A *%0A *      http://www.apache.org/licenses/LICENSE-2.0%0A *%0A * Unless required by applicable law or agreed to in writing, software%0A * distributed under the License is distributed on an %22AS IS%22 BASIS,%0A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.%0A * See the License for the specific language governing permissions and%0A * limitations under the License.%0A */%0Apackage org.apache.camel.component.undertow
 ;%0A%0Ai
@@ -1090,16 +1090,167 @@
 tream;%0A%0A
+import org.junit.Test;%0Aimport org.xnio.XnioIoThread;%0Aimport org.xnio.channels.EmptyStreamSourceChannel;%0Aimport org.xnio.channels.StreamSourceChannel;%0A%0A
 import s
@@ -1554,36 +1554,28 @@
             
-    %22%22,%0A    
+%22%22,%0A
             
@@ -2011,36 +2011,28 @@
             
-    %22%22,%0A    
+%22%22,%0A
             
@@ -2033,36 +2033,32 @@
       %22first %22,%0A
-    
             %22sec
@@ -2711,36 +2711,28 @@
             
-    %22%22,%0A    
+%22%22,%0A
             
@@ -2749,36 +2749,28 @@
             
-    %22%22,%0A    
+%22%22,%0A
             
@@ -3265,12 +3265,8 @@
 hunk
- = 0
 ;%0A  
@@ -3291,24 +3291,16 @@
 mustWait
- = false
 ;  // ma

apply Camel checkstyle