/* * Copyright 2013-2015 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.integration.x.http; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import org.hamcrest.Matchers; import org.junit.Test; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.test.util.SocketUtils; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessagingException; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestTemplate; /** * @author Mark Fisher * @author David Turanski * @author Jennifer Hickey * @author Gary Russell * @author Peter Rietzler */ public class NettyHttpInboundChannelAdapterTests { @Test public void test() throws Exception { final List<Message<?>> messages = new ArrayList<Message<?>>(); final CountDownLatch latch = new CountDownLatch(2); DirectChannel channel = new DirectChannel(); channel.subscribe(new MessageHandler() { @Override public void handleMessage(Message<?> message) throws MessagingException { messages.add(message); latch.countDown(); } }); int port = SocketUtils.findAvailableServerSocket(); NettyHttpInboundChannelAdapter adapter = new NettyHttpInboundChannelAdapter(port); adapter.setOutputChannel(channel); adapter.start(); RestTemplate template = new RestTemplate(); URI uri1 = new URI("http://localhost:" + port + "/test1"); URI uri2 = new URI("http://localhost:" + port + "/test2"); ResponseEntity<?> response1 = template.postForEntity(uri1, "foo", Object.class); ResponseEntity<?> response2 = template.postForEntity(uri2, "bar", Object.class); assertEquals(HttpStatus.OK, response1.getStatusCode()); assertEquals(HttpStatus.OK, response2.getStatusCode()); assertTrue(latch.await(1, TimeUnit.SECONDS)); assertEquals(2, messages.size()); Message<?> message1 = messages.get(0); Message<?> message2 = messages.get(1); assertEquals("foo", message1.getPayload()); assertEquals("bar", message2.getPayload()); assertEquals("/test1", message1.getHeaders().get("requestPath")); assertEquals("/test2", message2.getHeaders().get("requestPath")); adapter.stop(); } @Test public void testContentTypeHeaderMapsToSiContentTypeHeader() throws Exception { final List<Message<?>> messages = new ArrayList<Message<?>>(); final CountDownLatch latch = new CountDownLatch(1); DirectChannel channel = new DirectChannel(); channel.subscribe(new MessageHandler() { @Override public void handleMessage(Message<?> message) throws MessagingException { messages.add(message); latch.countDown(); } }); int port = SocketUtils.findAvailableServerSocket(); NettyHttpInboundChannelAdapter adapter = new NettyHttpInboundChannelAdapter(port); adapter.setOutputChannel(channel); adapter.start(); RestTemplate template = new RestTemplate(); URI uri1 = new URI("http://localhost:" + port + "/test1"); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.TEXT_PLAIN); HttpEntity<String> entity = new HttpEntity<String>("foo", headers); ResponseEntity<?> response = template.postForEntity(uri1, entity, HttpEntity.class); assertEquals(HttpStatus.OK, response.getStatusCode()); assertTrue(latch.await(1, TimeUnit.SECONDS)); assertEquals(1, messages.size()); Message<?> message = messages.get(0); assertEquals(MediaType.TEXT_PLAIN_VALUE, message.getHeaders().get(MessageHeaders.CONTENT_TYPE)); adapter.stop(); } @Test public void testBinaryContent() throws Exception { final List<Message<?>> messages = new ArrayList<Message<?>>(); final CountDownLatch latch = new CountDownLatch(1); DirectChannel channel = new DirectChannel(); channel.subscribe(new MessageHandler() { @Override public void handleMessage(Message<?> message) throws MessagingException { messages.add(message); latch.countDown(); } }); int port = SocketUtils.findAvailableServerSocket(); NettyHttpInboundChannelAdapter adapter = new NettyHttpInboundChannelAdapter(port); adapter.setOutputChannel(channel); adapter.start(); RestTemplate template = new RestTemplate(); URI uri1 = new URI("http://localhost:" + port + "/test1"); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); HttpEntity<byte[]> entity = new HttpEntity<byte[]>("foo".getBytes(), headers); ResponseEntity<?> response = template.postForEntity(uri1, entity, HttpEntity.class); assertEquals(HttpStatus.OK, response.getStatusCode()); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertEquals(1, messages.size()); Message<?> message = messages.get(0); assertEquals(MediaType.APPLICATION_OCTET_STREAM_VALUE, message.getHeaders().get(MessageHeaders.CONTENT_TYPE)); assertThat(message.getPayload(), Matchers.instanceOf(byte[].class)); assertEquals("foo", new String((byte[]) message.getPayload())); adapter.stop(); } @Test public void testLargeBinaryContent() throws Exception { final List<Message<?>> messages = new ArrayList<Message<?>>(); final CountDownLatch latch = new CountDownLatch(1); DirectChannel channel = new DirectChannel(); channel.subscribe(new MessageHandler() { @Override public void handleMessage(Message<?> message) throws MessagingException { messages.add(message); latch.countDown(); } }); int port = SocketUtils.findAvailableServerSocket(); NettyHttpInboundChannelAdapter adapter = new NettyHttpInboundChannelAdapter(port); adapter.setOutputChannel(channel); adapter.setMaxContentLength(10_000_000); adapter.start(); RestTemplate template = new RestTemplate(); SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); requestFactory.setReadTimeout(10000); template.setRequestFactory(requestFactory); URI uri1 = new URI("http://localhost:" + port + "/test1"); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); String largeContent = new String(new byte[10_000_000]); HttpEntity<byte[]> entity = new HttpEntity<byte[]>(largeContent.getBytes(), headers); ResponseEntity<?> response = template.postForEntity(uri1, entity, HttpEntity.class); assertEquals(HttpStatus.OK, response.getStatusCode()); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertEquals(1, messages.size()); Message<?> message = messages.get(0); assertEquals(MediaType.APPLICATION_OCTET_STREAM_VALUE, message.getHeaders().get(MessageHeaders.CONTENT_TYPE)); assertThat(message.getPayload(), Matchers.instanceOf(byte[].class)); assertEquals(largeContent, new String((byte[]) message.getPayload())); adapter.stop(); } @Test public void testTooLargeBinaryContent() throws Exception { DirectChannel channel = new DirectChannel(); channel.subscribe(new MessageHandler() { @Override public void handleMessage(Message<?> message) throws MessagingException { } }); int port = SocketUtils.findAvailableServerSocket(); NettyHttpInboundChannelAdapter adapter = new NettyHttpInboundChannelAdapter(port); adapter.setOutputChannel(channel); adapter.setMaxContentLength(1000); adapter.start(); RestTemplate template = new RestTemplate(); SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); requestFactory.setReadTimeout(10000); template.setRequestFactory(requestFactory); URI uri1 = new URI("http://localhost:" + port + "/test1"); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); String largeContent = new String(new byte[10_000]); HttpEntity<byte[]> entity = new HttpEntity<byte[]>(largeContent.getBytes(), headers); try { template.postForEntity(uri1, entity, HttpEntity.class); fail("Exception expected"); } catch (HttpClientErrorException e) { assertEquals(HttpStatus.PAYLOAD_TOO_LARGE, e.getStatusCode()); } adapter.stop(); } @Test(expected = HttpServerErrorException.class) public void testErrorResponse() throws URISyntaxException { DirectChannel channel = new DirectChannel(); channel.subscribe(new MessageHandler() { @Override public void handleMessage(Message<?> message) throws MessagingException { throw new RuntimeException(); } }); int port = SocketUtils.findAvailableServerSocket(); NettyHttpInboundChannelAdapter adapter = new NettyHttpInboundChannelAdapter(port); adapter.setOutputChannel(channel); adapter.start(); RestTemplate template = new RestTemplate(); URI uri1 = new URI("http://localhost:" + port + "/test1"); template.postForEntity(uri1, "foo", Object.class); adapter.stop(); } @Test public void testCustomExecutor() throws Exception { final List<Message<?>> messages = new ArrayList<Message<?>>(); final Set<String> threadNames = new HashSet<String>(); final CountDownLatch latch = new CountDownLatch(1); DirectChannel channel = new DirectChannel(); channel.subscribe(new MessageHandler() { @Override public void handleMessage(Message<?> message) throws MessagingException { threadNames.add(Thread.currentThread().getName()); messages.add(message); latch.countDown(); } }); int port = SocketUtils.findAvailableServerSocket(); NettyHttpInboundChannelAdapter adapter = new NettyHttpInboundChannelAdapter(port); adapter.setOutputChannel(channel); adapter.setExecutor(Executors.newFixedThreadPool(1, new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "executor-test"); } })); adapter.start(); RestTemplate template = new RestTemplate(); URI uri1 = new URI("http://localhost:" + port + "/test1"); URI uri2 = new URI("http://localhost:" + port + "/test2"); ResponseEntity<?> response1 = template.postForEntity(uri1, "foo", Object.class); ResponseEntity<?> response2 = template.postForEntity(uri2, "bar", Object.class); assertEquals(HttpStatus.OK, response1.getStatusCode()); assertEquals(HttpStatus.OK, response2.getStatusCode()); assertTrue(latch.await(1, TimeUnit.SECONDS)); // Ensure messages were received on the single thread with custom name assertEquals(Collections.singleton("executor-test"), threadNames); assertEquals(2, messages.size()); Message<?> message1 = messages.get(0); Message<?> message2 = messages.get(1); assertEquals("foo", message1.getPayload()); assertEquals("bar", message2.getPayload()); assertEquals("/test1", message1.getHeaders().get("requestPath")); assertEquals("/test2", message2.getHeaders().get("requestPath")); adapter.stop(); } @Test(expected = IllegalArgumentException.class) public void testNullExecutor() { int port = SocketUtils.findAvailableServerSocket(); NettyHttpInboundChannelAdapter adapter = new NettyHttpInboundChannelAdapter(port); adapter.setExecutor(null); } }