Mercurial > hg > release > thermostat-1.6
view agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/ProcessOutputStreamReaderTest.java @ 2049:a92d602216ad
Update copyright license headers for 2017
PR3290
Reviewed-by: jerboaa
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-January/021974.html
author | Andrew Azores <aazores@redhat.com> |
---|---|
date | Tue, 17 Jan 2017 12:19:56 -0500 |
parents | 31f274ab27a5 |
children |
line wrap: on
line source
/* * Copyright 2012-2017 Red Hat, Inc. * * This file is part of Thermostat. * * Thermostat is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2, or (at your * option) any later version. * * Thermostat is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Thermostat; see the file COPYING. If not see * <http://www.gnu.org/licenses/>. * * Linking this code with other modules is making a combined work * based on this code. Thus, the terms and conditions of the GNU * General Public License cover the whole combination. * * As a special exception, the copyright holders of this code give * you permission to link this code with independent modules to * produce an executable, regardless of the license terms of these * independent modules, and to copy and distribute the resulting * executable under terms of your choice, provided that you also * meet, for each linked independent module, the terms and conditions * of the license of that module. An independent module is a module * which is not derived from or based on this code. If you modify * this code, you may extend this exception to your version of the * library, but you are not obligated to do so. If you do not wish * to do so, delete this exception statement from your version. */ package com.redhat.thermostat.agent.command.internal; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.net.InetSocketAddress; import java.util.Collections; import java.util.List; import java.util.Random; import org.jboss.netty.buffer.ChannelBuffer; import org.junit.Test; import org.mockito.ArgumentCaptor; import com.redhat.thermostat.agent.command.internal.ProcessOutputStreamReader.ProcessOutputStreamRequestListener; import com.redhat.thermostat.agent.command.internal.ProcessStreamReader.ExceptionListener; import com.redhat.thermostat.common.command.Request; import com.redhat.thermostat.common.command.Request.RequestType; public class ProcessOutputStreamReaderTest { private ProcessOutputStreamRequestListener listener; private ExceptionListener exListener; @Test public void testSuccessNoParams() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); byte[] testData = getRequestAsBytes(req); parseRequest(testData); Request request = getRequest(); assertEquals(RequestType.RESPONSE_EXPECTED, request.getType()); assertEquals(new InetSocketAddress("127.0.0.1", 12222), request.getTarget()); assertEquals(Collections.emptySet(), request.getParameterNames()); } private byte[] getRequestAsBytes(Request req) throws IOException { ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF(req.getTarget().getHostString()); dos.writeInt(req.getTarget().getPort()); TestRequestEncoder encoder = new TestRequestEncoder(); ChannelBuffer buf = encoder.encode(req); byte[] reqBytes = buf.toByteBuffer().array(); dos.writeInt(reqBytes.length); dos.write(reqBytes); dos.writeUTF(CommandChannelConstants.END_REQUEST_TOKEN); dos.close(); return testData.toByteArray(); } @Test public void testSuccessOneParam() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); req.setReceiver("com.example.MyReceiver"); byte[] testData = getRequestAsBytes(req); parseRequest(testData); Request request = getRequest(); assertEquals(RequestType.RESPONSE_EXPECTED, request.getType()); assertEquals(new InetSocketAddress("127.0.0.1", 12222), request.getTarget()); assertEquals("com.example.MyReceiver", request.getReceiver()); } @Test public void testSuccessMultiParams() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); req.setReceiver("com.example.MyReceiver"); req.setParameter("param", "value"); byte[] testData = getRequestAsBytes(req); parseRequest(testData); Request request = getRequest(); assertEquals(RequestType.RESPONSE_EXPECTED, request.getType()); assertEquals(new InetSocketAddress("127.0.0.1", 12222), request.getTarget()); assertEquals("com.example.MyReceiver", request.getReceiver()); assertEquals("value", request.getParameter("param")); } @Test public void testEOFBeginToken() throws InterruptedException, IOException { // This should not be an error parseRequest(new byte[0]); verify(listener, never()).requestReceived(any(Request.class)); } @Test public void testBadBeginToken() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); byte[] testData = getBadBeginTokenRequestAsBytes(req); parseRequest(testData); verify(listener, never()).requestReceived(any(Request.class)); IOException ex = getFirstException(); assertTrue(CommandChannelIOException.class.isInstance(ex)); } private byte[] getBadBeginTokenRequestAsBytes(Request req) throws IOException { ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF("<BEGIN REQ>"); dos.writeUTF(req.getTarget().getHostString()); dos.writeInt(req.getTarget().getPort()); TestRequestEncoder encoder = new TestRequestEncoder(); ChannelBuffer buf = encoder.encode(req); byte[] reqBytes = buf.toByteBuffer().array(); dos.writeInt(reqBytes.length); dos.write(reqBytes); dos.writeUTF(CommandChannelConstants.END_REQUEST_TOKEN); dos.close(); return testData.toByteArray(); } @Test public void testEOFHostname() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); byte[] testData = getEOFHostnameRequestAsBytes(req); parseRequest(testData); verify(listener, never()).requestReceived(any(Request.class)); IOException ex = getFirstException(); assertTrue(EOFException.class.isInstance(ex)); } private byte[] getEOFHostnameRequestAsBytes(Request req) throws IOException { ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.close(); return testData.toByteArray(); } @Test public void testEOFPort() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); byte[] testData = getEOFPortRequestAsBytes(req); parseRequest(testData); verify(listener, never()).requestReceived(any(Request.class)); IOException ex = getFirstException(); assertTrue(EOFException.class.isInstance(ex)); } private byte[] getEOFPortRequestAsBytes(Request req) throws IOException { ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF("127.0.0.1"); dos.close(); return testData.toByteArray(); } @Test public void testNotIntPort() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); byte[] testData = getNotIntPortRequestAsBytes(req); parseRequest(testData); verify(listener, never()).requestReceived(any(Request.class)); IOException ex = getFirstException(); assertTrue(CommandChannelIOException.class.isInstance(ex)); } private byte[] getNotIntPortRequestAsBytes(Request req) throws IOException { ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF(req.getTarget().getHostString()); dos.writeUTF("hello"); TestRequestEncoder encoder = new TestRequestEncoder(); ChannelBuffer buf = encoder.encode(req); byte[] reqBytes = buf.toByteBuffer().array(); dos.writeInt(reqBytes.length); dos.write(reqBytes); dos.writeUTF(CommandChannelConstants.END_REQUEST_TOKEN); dos.close(); return testData.toByteArray(); } @Test public void testPortOutOfRange() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); byte[] testData = getPortOutOfRangeRequestAsBytes(req); parseRequest(testData); verify(listener, never()).requestReceived(any(Request.class)); IOException ex = getFirstException(); assertTrue(CommandChannelIOException.class.isInstance(ex)); } private byte[] getPortOutOfRangeRequestAsBytes(Request req) throws IOException { ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF(req.getTarget().getHostString()); dos.writeInt(200000); TestRequestEncoder encoder = new TestRequestEncoder(); ChannelBuffer buf = encoder.encode(req); byte[] reqBytes = buf.toByteBuffer().array(); dos.writeInt(reqBytes.length); dos.write(reqBytes); dos.writeUTF(CommandChannelConstants.END_REQUEST_TOKEN); dos.close(); return testData.toByteArray(); } @Test public void testEOFEncodedLength() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); byte[] testData = getEOFEncodedLengthRequestAsBytes(req); parseRequest(testData); verify(listener, never()).requestReceived(any(Request.class)); IOException ex = getFirstException(); assertTrue(CommandChannelIOException.class.isInstance(ex)); } private byte[] getEOFEncodedLengthRequestAsBytes(Request req) throws IOException { ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF(req.getTarget().getHostString()); dos.writeInt(12222); TestRequestEncoder encoder = new TestRequestEncoder(); ChannelBuffer buf = encoder.encode(req); byte[] reqBytes = buf.toByteBuffer().array(); dos.write(reqBytes); dos.writeUTF(CommandChannelConstants.END_REQUEST_TOKEN); dos.close(); return testData.toByteArray(); } @Test public void testMissingRequestType() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); byte[] testData = getMissingTypeRequestAsBytes(req); parseRequest(testData); verify(listener, never()).requestReceived(any(Request.class)); IOException ex = getFirstException(); assertTrue(CommandChannelIOException.class.isInstance(ex)); } private byte[] getMissingTypeRequestAsBytes(Request req) throws IOException { ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF(req.getTarget().getHostString()); dos.writeInt(req.getTarget().getPort()); TestRequestEncoder encoder = new TestRequestEncoder(); ChannelBuffer buf = encoder.encode(req, null, null); byte[] reqBytes = buf.toByteBuffer().array(); dos.writeInt(reqBytes.length); dos.write(reqBytes); dos.writeUTF(CommandChannelConstants.END_REQUEST_TOKEN); dos.close(); return testData.toByteArray(); } @Test public void testMissingParams() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); req.setParameter("bad", "param"); byte[] testData = getMissingParamsRequestAsBytes(req); parseRequest(testData); verify(listener, never()).requestReceived(any(Request.class)); IOException ex = getFirstException(); assertTrue(CommandChannelIOException.class.isInstance(ex)); } private byte[] getMissingParamsRequestAsBytes(Request req) throws IOException { ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF(req.getTarget().getHostString()); dos.writeInt(req.getTarget().getPort()); TestRequestEncoder encoder = new TestRequestEncoder(); ChannelBuffer buf = encoder.encode(req, "RESPONSE_EXPECTED", "bad"); byte[] reqBytes = buf.toByteBuffer().array(); dos.writeInt(reqBytes.length); dos.write(reqBytes); dos.writeUTF(CommandChannelConstants.END_REQUEST_TOKEN); dos.close(); return testData.toByteArray(); } @Test public void testBadRequestType() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); byte[] testData = getBadTypeRequestAsBytes(req); parseRequest(testData); verify(listener, never()).requestReceived(any(Request.class)); IOException ex = getFirstException(); assertTrue(CommandChannelIOException.class.isInstance(ex)); } private byte[] getBadTypeRequestAsBytes(Request req) throws IOException { ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF(req.getTarget().getHostString()); dos.writeInt(req.getTarget().getPort()); TestRequestEncoder encoder = new TestRequestEncoder(); ChannelBuffer buf = encoder.encode(req, "BadType", null); byte[] reqBytes = buf.toByteBuffer().array(); dos.writeInt(reqBytes.length); dos.write(reqBytes); dos.writeUTF(CommandChannelConstants.END_REQUEST_TOKEN); dos.close(); return testData.toByteArray(); } @Test public void testMissingEncodedRequest() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); byte[] testData = getMissingEncodedRequestAsBytes(req); parseRequest(testData); verify(listener, never()).requestReceived(any(Request.class)); IOException ex = getFirstException(); assertTrue(CommandChannelIOException.class.isInstance(ex)); } private byte[] getMissingEncodedRequestAsBytes(Request req) throws IOException { ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF(req.getTarget().getHostString()); dos.writeInt(req.getTarget().getPort()); dos.writeInt(0); dos.writeUTF(CommandChannelConstants.END_REQUEST_TOKEN); dos.close(); return testData.toByteArray(); } @Test public void testEOFEndToken() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); byte[] testData = getEOFEndTokenRequestAsBytes(req); parseRequest(testData); verify(listener, never()).requestReceived(any(Request.class)); IOException ex = getFirstException(); assertTrue(EOFException.class.isInstance(ex)); } private byte[] getEOFEndTokenRequestAsBytes(Request req) throws IOException { ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF(req.getTarget().getHostString()); dos.writeInt(req.getTarget().getPort()); TestRequestEncoder encoder = new TestRequestEncoder(); ChannelBuffer buf = encoder.encode(req); byte[] reqBytes = buf.toByteBuffer().array(); dos.writeInt(reqBytes.length); dos.write(reqBytes); dos.close(); return testData.toByteArray(); } @Test public void testBadEndToken() throws InterruptedException, IOException { Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); byte[] testData = getBadEndTokenRequestAsBytes(req); parseRequest(testData); verify(listener, never()).requestReceived(any(Request.class)); IOException ex = getFirstException(); assertTrue(CommandChannelIOException.class.isInstance(ex)); } private byte[] getBadEndTokenRequestAsBytes(Request req) throws IOException { ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF(req.getTarget().getHostString()); dos.writeInt(req.getTarget().getPort()); TestRequestEncoder encoder = new TestRequestEncoder(); ChannelBuffer buf = encoder.encode(req); byte[] reqBytes = buf.toByteBuffer().array(); dos.writeInt(reqBytes.length); dos.write(reqBytes); dos.writeUTF("<END REQ>"); dos.close(); return testData.toByteArray(); } @Test public void testEarlyEnd() throws IOException, InterruptedException { // Create a request with a parameter designed to mimic the CommandChannelConstants.END_REQUEST_TOKEN token, // and give an incorrect encoded length pointing the byte before the parameter name. // The encoded length should always be correct when receiving the request from the server, // but this test ensures this class can handle malicious input. Request req1 = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeUTF(CommandChannelConstants.END_REQUEST_TOKEN); dos.close(); String encodedEndRequest = new String(baos.toByteArray()); req1.setParameter(encodedEndRequest, ""); // Try another request to make sure the command channel is still usable Request req2 = new Request(RequestType.NO_RESPONSE_EXPECTED, new InetSocketAddress("localhost", 123)); req2.setParameter("hello", "world"); byte[] testData = getEarlyEndRequestsAsBytes(req1, encodedEndRequest.getBytes().length, req2); parseRequest(testData); List<Request> requests = getRequests(); assertEquals(2, requests.size()); Request result = requests.get(0); assertEquals(RequestType.RESPONSE_EXPECTED, result.getType()); assertEquals(new InetSocketAddress("127.0.0.1", 12222), result.getTarget()); assertEquals(Collections.emptySet(), result.getParameterNames()); result = requests.get(1); assertEquals(RequestType.NO_RESPONSE_EXPECTED, result.getType()); assertEquals(new InetSocketAddress("localhost", 123), result.getTarget()); assertEquals(1, result.getParameterNames().size()); assertEquals("world", result.getParameter("hello")); } private byte[] getEarlyEndRequestsAsBytes(Request req1, int subLength, Request req2) throws IOException { // First request ends early ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF(req1.getTarget().getHostString()); dos.writeInt(req1.getTarget().getPort()); TestRequestEncoder encoder = new TestRequestEncoder(); // Replace the number of parameters with 0 ChannelBuffer buf = encoder.encode(req1, "RESPONSE_EXPECTED", null, 0); byte[] reqBytes = buf.toByteBuffer().array(); dos.writeInt(reqBytes.length - subLength); dos.write(reqBytes); // Try another request to ensure the first malformed request doesn't // affect future request dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF(req2.getTarget().getHostString()); dos.writeInt(req2.getTarget().getPort()); buf = encoder.encode(req2); reqBytes = buf.toByteBuffer().array(); dos.writeInt(reqBytes.length); dos.write(reqBytes); dos.writeUTF(CommandChannelConstants.END_REQUEST_TOKEN); dos.close(); return testData.toByteArray(); } @Test public void testGarbageAtEnd() throws IOException, InterruptedException { // Create a request with extra data at the end, which is not included // in the request length. // Note: This garbage data can cause any following data in the stream to // be read and discarded. In practice this should never happen because // the command channel should not send extraneous data. Request req = new Request(RequestType.RESPONSE_EXPECTED, new InetSocketAddress("127.0.0.1", 12222)); byte[] testData = getRequestWithExtraData(req); try { parseRequest(testData); } catch (IOException e) { // Pass } // We should still have parsed a valid request Request result = getRequest(); assertEquals(RequestType.RESPONSE_EXPECTED, result.getType()); assertEquals(new InetSocketAddress("127.0.0.1", 12222), result.getTarget()); assertEquals(Collections.emptySet(), result.getParameterNames()); } private byte[] getRequestWithExtraData(Request req) throws IOException { // First request has extra garbage data at the end ByteArrayOutputStream testData = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(testData); dos.writeUTF(CommandChannelConstants.BEGIN_REQUEST_TOKEN); dos.writeUTF(req.getTarget().getHostString()); dos.writeInt(req.getTarget().getPort()); TestRequestEncoder encoder = new TestRequestEncoder(); ChannelBuffer buf = encoder.encode(req); byte[] reqBytes = buf.toByteBuffer().array(); dos.writeInt(reqBytes.length); dos.write(reqBytes); dos.writeUTF(CommandChannelConstants.END_REQUEST_TOKEN); Random rand = new Random(); byte[] garbage = new byte[32]; rand.nextBytes(garbage); dos.write(garbage); dos.close(); return testData.toByteArray(); } private Request getRequest() { List<Request> requests = getRequests(); assertEquals(1, requests.size()); return requests.get(0); } private List<Request> getRequests() { ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class); verify(listener, atLeastOnce()).requestReceived(requestCaptor.capture()); List<Request> requests = requestCaptor.getAllValues(); return requests; } private IOException getFirstException() { List<IOException> exceptions = getExceptions(); //assertEquals(1, exceptions.size()); return exceptions.get(0); } private List<IOException> getExceptions() { ArgumentCaptor<IOException> exceptionCaptor = ArgumentCaptor.forClass(IOException.class); verify(exListener, atLeastOnce()).notifyException(exceptionCaptor.capture()); List<IOException> exceptions = exceptionCaptor.getAllValues(); return exceptions; } private void parseRequest(byte[] testData) throws InterruptedException, IOException { InputStream is = new ByteArrayInputStream(testData); listener = mock(ProcessOutputStreamRequestListener.class); exListener = mock(ExceptionListener.class); ProcessOutputStreamReader reader = new ProcessOutputStreamReader(is, listener, exListener); reader.run(); } }