Mercurial > hg > thermostat
changeset 2296:7596994808a2
Fix decoding of Request messages.
Reviewed-by: neugens
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-May/018803.html
author | Severin Gehwolf <sgehwolf@redhat.com> |
---|---|
date | Thu, 12 May 2016 15:46:30 +0200 |
parents | 5d4c3090df2d |
children | 524aeb3ae995 |
files | agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelRequestDecoder.java agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelRequestDecoderTest.java agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/JsonResponseParserTest.java agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/RequestDecoderTest.java agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/ResponseParserTest.java |
diffstat | 5 files changed, 787 insertions(+), 395 deletions(-) [+] |
line wrap: on
line diff
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelRequestDecoder.java Thu May 12 11:17:00 2016 -0400 +++ b/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelRequestDecoder.java Thu May 12 15:46:30 2016 +0200 @@ -91,7 +91,7 @@ class CommandChannelRequestDecoder extends ByteToMessageDecoder { private static final Logger logger = LoggingUtils.getLogger(CommandChannelRequestDecoder.class); - + @Override protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> out) throws Exception { logger.log(Level.FINEST, "Command channel server: decoding Request object"); @@ -104,19 +104,20 @@ if (typeAsString == null) { throw new InvalidMessageException("Could not decode message: " + ByteBufUtil.hexDump(buf)); } - // clean up resources - buf.readerIndex(buf.readerIndex() + stringDecCtx.getBytesRead()); - buf.discardReadBytes(); // Netty javadoc tells us it's safe to downcast to more concrete type. InetSocketAddress addr = (InetSocketAddress)ctx.channel().remoteAddress(); Request request = new Request(RequestType.valueOf(typeAsString), addr); - ParameterDecodingContext paramCtx = DecodingHelper.decodeParameters(buf); + int remainingLength = buf.readableBytes() - stringDecCtx.getBytesRead(); + ByteBuf adjustedBuffer = buf.slice(stringDecCtx.getBytesRead(), remainingLength); + ParameterDecodingContext paramCtx = DecodingHelper.decodeParameters(adjustedBuffer); if (paramCtx.getState() != ParameterDecodingState.ALL_PARAMETERS_READ) { // insufficient data return; } - // clean up resources - buf.readerIndex(buf.readerIndex() + paramCtx.getBytesRead()); + // clean up resources from the request type + parameters + int totalBytesRead = stringDecCtx.getBytesRead() + + paramCtx.getBytesRead(); + buf.readerIndex(buf.readerIndex() + totalBytesRead); buf.discardReadBytes(); for (Entry<String, String> kv: paramCtx.getValues().entrySet()) { request.setParameter(kv.getKey(), kv.getValue());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelRequestDecoderTest.java Thu May 12 15:46:30 2016 +0200 @@ -0,0 +1,580 @@ +/* + * Copyright 2012-2016 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.server.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.common.command.Message; +import com.redhat.thermostat.common.command.Messages; +import com.redhat.thermostat.common.command.Request; +import com.redhat.thermostat.common.command.Request.RequestType; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; + +public class CommandChannelRequestDecoderTest { + + private static final String LONG_PARAM_VALUE = "RULE count sleep method invocations\n" + + "CLASS com.redhat.thermostat.byteman.test.InfiniteLoop\n" + + "METHOD sleep\n" + + "AT ENTRY\n" + + "IF true\n" + + "DO\n" + + "incrementCounter(\"sleep-counter\")\n" + + "ENDRULE\n" + + "\n" + + "RULE send sleep method invocations to thermostat\n" + + "CLASS com.redhat.thermostat.byteman.test.InfiniteLoop\n" + + "METHOD sleep\n" + + "HELPER org.jboss.byteman.thermostat.helper.ThermostatHelper\n" + + "AT ENTRY\n" + + "BIND counterValue: int = readCounter(\"sleep-counter\")\n" + + "IF counterValue % 10 == 0\n" + + "DO\n" + + "send(\"com.redhat.thermostat.byteman.test.InfiniteLoop\", \"method sleep() count\", counterValue);\n" + + "ENDRULE\n"; + + /* + * This is serialized format for + * req = new Request(RequestType.RESPONSE_EXPECTED, blah); + * req.setParameter("param1", "value1"); + * req.setParameter("param2", "value2"); + */ + private static final byte[] ENCODED_REQUEST_WITH_PARAMS = new byte[] { + 0x00, 0x00, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, + 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x70, 0x61, 0x72, + 0x61, 0x6d, 0x31, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x31, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x32, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x32 + }; + + /* + * This is serialized format for: + * + * Request req = new Request(RequestType.RESPONSE_EXPECTED, blah); + * req.setReceiver("com.redhat.thermostat.vm.byteman.agent.internal.BytemanRequestReceiver"); + * req.setParameter("action-name", "vm-byteman-instrument"); + * req.setParameter("vm-id", "f5b4cc44-06f2-4847-97d3-b5b0b198f843"); + * req.setParameter("byteman-action", Integer.toString(0)); + * req.setParameter("listen-port", Integer.toString(listenPort)); + * req.setParameter("auth-token", "/UFDv9PT3nLMW1lEJUyTxfIHfU8bRxcAw4IdVYL7JzHROruzNvIpuvqrLUN01f0hGIGMmzc58iylrbpgY1wkUA04YL5WOF66ysfwLWwDA8M2R7khrK0xY6+DLSA1nOUT+ihmvGsrlk51x5aqH7Vvqqj+TDyj8Z3Gl3dMp6KEngtBxxhxO/pI9euQGUiwWGcY7CdTAoTSx2wh8bOsmDUllwOE7978ZhTRsSN0bd57eynDaae23TwInhez5iHOxuWlByjEv4RyDPSc0fASoKwH7IAj7VYMzuj72/c9xLOA54XuyruaMdqi0zlA4v/af7xJ90XVMWdW96EGBGkIlo33fA=="); + * req.setParameter("client-token", "MdJ08lQ4+4H9asOQaYxV6XDXbICi3njI7JmZ9W8WgN7LW8RIYZ0aCFpaHQIHsG38mOLvZRzLNwI45NG/qoxVu+BA8ekEBpLlHCotxLk+1F7yZonmkEfXq6o4imsVU/8EU36Dh7YRd5qOnerJ7NItrw/kBuVaAlsVL2Bbj7ryvVVE01GgMJlBLQjnjb21eO2yg2w4AKDOggHMwcCAaZl1wW1pKQJAvlT0FYHrwSUJQ51JrZEUJjFxZ38xxYHA0HMffUiDczqkFj08w59G/652JY/hWpoM9uzwZP+tV4sCWXFF5IuBB8PFwaeqgXDwrbxgYMjCgk9nNZuvdsdaV1bGRQ=="); + * req.setParameter("byteman-rule", LONG_PARAM_VALUE); + */ + private static final byte[] ENCODED_REQUEST_WITH_MANY_PARAMS = new byte[] { + 0x00, 0x00, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, + 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, + 0x00, 0x00, 0x00, 0x08, // 8 parameters, slice A end + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x15, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x76, 0x6d, 0x2d, 0x62, 0x79, + 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2d, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x58, + 0x61, 0x75, 0x74, 0x68, 0x2d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2f, 0x55, + 0x46, 0x44, 0x76, 0x39, 0x50, 0x54, 0x33, 0x6e, 0x4c, 0x4d, 0x57, 0x31, + 0x6c, 0x45, 0x4a, 0x55, 0x79, 0x54, 0x78, 0x66, 0x49, 0x48, 0x66, 0x55, + 0x38, 0x62, 0x52, 0x78, 0x63, 0x41, 0x77, 0x34, 0x49, 0x64, 0x56, 0x59, + 0x4c, 0x37, 0x4a, 0x7a, 0x48, 0x52, 0x4f, 0x72, 0x75, 0x7a, 0x4e, 0x76, + 0x49, 0x70, 0x75, 0x76, 0x71, 0x72, 0x4c, 0x55, 0x4e, 0x30, 0x31, 0x66, + 0x30, 0x68, 0x47, 0x49, 0x47, 0x4d, 0x6d, 0x7a, 0x63, 0x35, 0x38, 0x69, + 0x79, 0x6c, 0x72, 0x62, 0x70, 0x67, 0x59, 0x31, 0x77, 0x6b, 0x55, 0x41, + 0x30, 0x34, 0x59, 0x4c, 0x35, 0x57, 0x4f, 0x46, 0x36, 0x36, 0x79, 0x73, + 0x66, 0x77, 0x4c, 0x57, 0x77, 0x44, 0x41, 0x38, 0x4d, 0x32, 0x52, 0x37, + 0x6b, 0x68, 0x72, 0x4b, 0x30, 0x78, 0x59, 0x36, 0x2b, 0x44, 0x4c, 0x53, + 0x41, 0x31, 0x6e, 0x4f, 0x55, 0x54, 0x2b, 0x69, 0x68, 0x6d, 0x76, 0x47, + 0x73, 0x72, 0x6c, 0x6b, 0x35, 0x31, 0x78, 0x35, 0x61, 0x71, 0x48, 0x37, + 0x56, 0x76, 0x71, 0x71, 0x6a, 0x2b, 0x54, 0x44, 0x79, 0x6a, 0x38, 0x5a, + 0x33, 0x47, 0x6c, 0x33, 0x64, 0x4d, 0x70, 0x36, 0x4b, 0x45, 0x6e, 0x67, + 0x74, 0x42, 0x78, 0x78, 0x68, 0x78, 0x4f, 0x2f, 0x70, 0x49, 0x39, 0x65, + 0x75, 0x51, 0x47, 0x55, 0x69, 0x77, 0x57, 0x47, 0x63, 0x59, 0x37, 0x43, + 0x64, 0x54, 0x41, 0x6f, 0x54, 0x53, 0x78, 0x32, 0x77, 0x68, 0x38, 0x62, + 0x4f, 0x73, 0x6d, 0x44, 0x55, 0x6c, 0x6c, 0x77, 0x4f, 0x45, 0x37, 0x39, + 0x37, 0x38, 0x5a, 0x68, 0x54, 0x52, 0x73, 0x53, 0x4e, 0x30, 0x62, 0x64, + 0x35, 0x37, 0x65, 0x79, 0x6e, 0x44, 0x61, 0x61, 0x65, 0x32, 0x33, 0x54, + 0x77, 0x49, 0x6e, 0x68, 0x65, 0x7a, 0x35, 0x69, 0x48, 0x4f, 0x78, 0x75, + 0x57, 0x6c, 0x42, 0x79, 0x6a, 0x45, 0x76, 0x34, 0x52, 0x79, 0x44, 0x50, + 0x53, 0x63, 0x30, 0x66, 0x41, 0x53, 0x6f, 0x4b, 0x77, 0x48, 0x37, 0x49, + 0x41, 0x6a, 0x37, 0x56, 0x59, 0x4d, 0x7a, 0x75, 0x6a, 0x37, 0x32, 0x2f, + 0x63, 0x39, 0x78, 0x4c, 0x4f, 0x41, 0x35, 0x34, 0x58, 0x75, 0x79, 0x72, + 0x75, 0x61, 0x4d, 0x64, 0x71, 0x69, 0x30, 0x7a, 0x6c, 0x41, 0x34, 0x76, + 0x2f, 0x61, 0x66, 0x37, 0x78, 0x4a, 0x39, 0x30, 0x58, 0x56, 0x4d, 0x57, + 0x64, 0x57, 0x39, 0x36, 0x45, 0x47, 0x42, 0x47, 0x6b, 0x49, 0x6c, 0x6f, + 0x33, 0x33, 0x66, 0x41, 0x3d, 0x3d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x01, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2d, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x02, + 0x1a, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2d, 0x72, 0x75, 0x6c, + 0x65, 0x52, 0x55, 0x4c, 0x45, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, + 0x73, 0x6c, 0x65, 0x65, 0x70, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x20, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x72, + 0x65, 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f, + 0x73, 0x74, 0x61, 0x74, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, + 0x74, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x0a, 0x4d, 0x45, 0x54, 0x48, 0x4f, + 0x44, 0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x0a, 0x41, 0x54, 0x20, 0x45, + 0x4e, 0x54, 0x52, 0x59, 0x0a, 0x49, 0x46, 0x20, 0x74, 0x72, 0x75, 0x65, + 0x0a, 0x44, 0x4f, 0x0a, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x28, 0x22, 0x73, 0x6c, + 0x65, 0x65, 0x70, 0x2d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x22, + 0x29, 0x0a, 0x45, 0x4e, 0x44, 0x52, 0x55, 0x4c, 0x45, 0x0a, 0x0a, 0x52, + 0x55, 0x4c, 0x45, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x73, 0x6c, 0x65, + 0x65, 0x70, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x69, 0x6e, + 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, + 0x20, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x0a, + 0x43, 0x4c, 0x41, 0x53, 0x53, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x65, + 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f, 0x73, + 0x74, 0x61, 0x74, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, + 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x0a, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, // slice B end + 0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x0a, 0x48, 0x45, 0x4c, 0x50, 0x45, + 0x52, 0x20, 0x6f, 0x72, 0x67, 0x2e, 0x6a, 0x62, 0x6f, 0x73, 0x73, 0x2e, + 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2e, 0x74, 0x68, 0x65, 0x72, + 0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x2e, 0x68, 0x65, 0x6c, 0x70, 0x65, + 0x72, 0x2e, 0x54, 0x68, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, + 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x0a, 0x41, 0x54, 0x20, 0x45, 0x4e, + 0x54, 0x52, 0x59, 0x0a, 0x42, 0x49, 0x4e, 0x44, 0x20, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x20, 0x69, + 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x61, 0x64, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x28, 0x22, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x2d, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x29, 0x0a, 0x49, 0x46, + 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x20, 0x25, 0x20, 0x31, 0x30, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x0a, + 0x44, 0x4f, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x22, 0x63, 0x6f, 0x6d, + 0x2e, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, + 0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x6d, + 0x61, 0x6e, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x66, 0x69, + 0x6e, 0x69, 0x74, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x22, 0x2c, 0x20, 0x20, + 0x22, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x73, 0x6c, 0x65, 0x65, + 0x70, 0x28, 0x29, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2c, 0x20, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x29, 0x3b, 0x0a, 0x45, 0x4e, 0x44, 0x52, 0x55, 0x4c, 0x45, 0x0a, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x58, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x2d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x4d, 0x64, 0x4a, 0x30, 0x38, + 0x6c, 0x51, 0x34, 0x2b, 0x34, 0x48, 0x39, 0x61, 0x73, 0x4f, 0x51, 0x61, + 0x59, 0x78, 0x56, 0x36, 0x58, 0x44, 0x58, 0x62, 0x49, 0x43, 0x69, 0x33, + 0x6e, 0x6a, 0x49, 0x37, 0x4a, 0x6d, 0x5a, 0x39, 0x57, 0x38, 0x57, 0x67, + 0x4e, 0x37, 0x4c, 0x57, 0x38, 0x52, 0x49, 0x59, 0x5a, 0x30, 0x61, 0x43, + 0x46, 0x70, 0x61, 0x48, 0x51, 0x49, 0x48, 0x73, 0x47, 0x33, 0x38, 0x6d, + 0x4f, 0x4c, 0x76, 0x5a, 0x52, 0x7a, 0x4c, 0x4e, 0x77, 0x49, 0x34, 0x35, + 0x4e, 0x47, 0x2f, 0x71, 0x6f, 0x78, 0x56, 0x75, 0x2b, 0x42, 0x41, 0x38, + 0x65, 0x6b, 0x45, 0x42, 0x70, 0x4c, 0x6c, 0x48, 0x43, 0x6f, 0x74, 0x78, + 0x4c, 0x6b, 0x2b, 0x31, 0x46, 0x37, 0x79, 0x5a, 0x6f, 0x6e, 0x6d, 0x6b, + 0x45, 0x66, 0x58, 0x71, 0x36, 0x6f, 0x34, 0x69, 0x6d, 0x73, 0x56, 0x55, + 0x2f, 0x38, 0x45, 0x55, 0x33, 0x36, 0x44, 0x68, 0x37, 0x59, 0x52, 0x64, + 0x35, 0x71, 0x4f, 0x6e, 0x65, 0x72, 0x4a, 0x37, 0x4e, 0x49, 0x74, 0x72, + 0x77, 0x2f, 0x6b, 0x42, 0x75, 0x56, 0x61, 0x41, 0x6c, 0x73, 0x56, 0x4c, + 0x32, 0x42, 0x62, 0x6a, 0x37, 0x72, 0x79, 0x76, 0x56, 0x56, 0x45, 0x30, + 0x31, 0x47, 0x67, 0x4d, 0x4a, 0x6c, 0x42, 0x4c, 0x51, 0x6a, 0x6e, 0x6a, + 0x62, 0x32, 0x31, 0x65, 0x4f, 0x32, 0x79, 0x67, 0x32, 0x77, 0x34, 0x41, + 0x4b, 0x44, 0x4f, 0x67, 0x67, 0x48, 0x4d, 0x77, 0x63, 0x43, 0x41, 0x61, + 0x5a, 0x6c, 0x31, 0x77, 0x57, 0x31, 0x70, 0x4b, 0x51, 0x4a, 0x41, 0x76, + 0x6c, 0x54, 0x30, 0x46, 0x59, 0x48, 0x72, 0x77, 0x53, 0x55, 0x4a, 0x51, + 0x35, 0x31, 0x4a, 0x72, 0x5a, 0x45, 0x55, 0x4a, 0x6a, 0x46, 0x78, 0x5a, + 0x33, 0x38, 0x78, 0x78, 0x59, 0x48, 0x41, 0x30, 0x48, 0x4d, 0x66, 0x66, + 0x55, 0x69, 0x44, 0x63, 0x7a, 0x71, 0x6b, 0x46, 0x6a, 0x30, 0x38, 0x77, + 0x35, 0x39, 0x47, 0x2f, 0x36, 0x35, 0x32, 0x4a, 0x59, 0x2f, 0x68, 0x57, + 0x70, 0x6f, 0x4d, 0x39, 0x75, 0x7a, 0x77, 0x5a, 0x50, 0x2b, 0x74, 0x56, + 0x34, 0x73, 0x43, 0x57, 0x58, 0x46, 0x46, 0x35, 0x49, 0x75, 0x42, 0x42, + 0x38, 0x50, 0x46, 0x77, 0x61, 0x65, 0x71, 0x67, 0x58, 0x44, 0x77, 0x72, + 0x62, 0x78, 0x67, 0x59, 0x4d, 0x6a, 0x43, 0x67, 0x6b, 0x39, 0x6e, 0x4e, + 0x5a, 0x75, 0x76, 0x64, 0x73, 0x64, 0x61, 0x56, 0x31, 0x62, 0x47, 0x52, + 0x51, 0x3d, 0x3d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x05, 0x6c, + 0x69, 0x73, 0x74, 0x65, 0x6e, 0x2d, 0x70, 0x6f, 0x72, 0x74, 0x31, 0x33, + 0x33, 0x30, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x46, 0x72, + 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x63, 0x6f, 0x6d, 0x2e, 0x72, + 0x65, 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f, + 0x73, 0x74, 0x61, 0x74, 0x2e, 0x76, 0x6d, 0x2e, 0x62, 0x79, 0x74, 0x65, + 0x6d, 0x61, 0x6e, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x6d, + 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x24, 0x76, 0x6d, 0x2d, 0x69, 0x64, 0x66, 0x35, 0x62, 0x34, 0x63, 0x63, + 0x34, 0x34, 0x2d, 0x30, 0x36, 0x66, 0x32, 0x2d, 0x34, 0x38, 0x34, 0x37, + 0x2d, 0x39, 0x37, 0x64, 0x33, 0x2d, 0x62, 0x35, 0x62, 0x30, 0x62, 0x31, + 0x39, 0x38, 0x66, 0x38, 0x34, 0x33 // slice C end + }; + + private static final byte[] ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_A = new byte[] { + 0x00, 0x00, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, + 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, + 0x00, 0x00, 0x00, 0x08, // 8 parameters + }; + + private static final byte[] ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_B = new byte[] { + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x15, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x76, 0x6d, 0x2d, 0x62, 0x79, + 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2d, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x58, + 0x61, 0x75, 0x74, 0x68, 0x2d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2f, 0x55, + 0x46, 0x44, 0x76, 0x39, 0x50, 0x54, 0x33, 0x6e, 0x4c, 0x4d, 0x57, 0x31, + 0x6c, 0x45, 0x4a, 0x55, 0x79, 0x54, 0x78, 0x66, 0x49, 0x48, 0x66, 0x55, + 0x38, 0x62, 0x52, 0x78, 0x63, 0x41, 0x77, 0x34, 0x49, 0x64, 0x56, 0x59, + 0x4c, 0x37, 0x4a, 0x7a, 0x48, 0x52, 0x4f, 0x72, 0x75, 0x7a, 0x4e, 0x76, + 0x49, 0x70, 0x75, 0x76, 0x71, 0x72, 0x4c, 0x55, 0x4e, 0x30, 0x31, 0x66, + 0x30, 0x68, 0x47, 0x49, 0x47, 0x4d, 0x6d, 0x7a, 0x63, 0x35, 0x38, 0x69, + 0x79, 0x6c, 0x72, 0x62, 0x70, 0x67, 0x59, 0x31, 0x77, 0x6b, 0x55, 0x41, + 0x30, 0x34, 0x59, 0x4c, 0x35, 0x57, 0x4f, 0x46, 0x36, 0x36, 0x79, 0x73, + 0x66, 0x77, 0x4c, 0x57, 0x77, 0x44, 0x41, 0x38, 0x4d, 0x32, 0x52, 0x37, + 0x6b, 0x68, 0x72, 0x4b, 0x30, 0x78, 0x59, 0x36, 0x2b, 0x44, 0x4c, 0x53, + 0x41, 0x31, 0x6e, 0x4f, 0x55, 0x54, 0x2b, 0x69, 0x68, 0x6d, 0x76, 0x47, + 0x73, 0x72, 0x6c, 0x6b, 0x35, 0x31, 0x78, 0x35, 0x61, 0x71, 0x48, 0x37, + 0x56, 0x76, 0x71, 0x71, 0x6a, 0x2b, 0x54, 0x44, 0x79, 0x6a, 0x38, 0x5a, + 0x33, 0x47, 0x6c, 0x33, 0x64, 0x4d, 0x70, 0x36, 0x4b, 0x45, 0x6e, 0x67, + 0x74, 0x42, 0x78, 0x78, 0x68, 0x78, 0x4f, 0x2f, 0x70, 0x49, 0x39, 0x65, + 0x75, 0x51, 0x47, 0x55, 0x69, 0x77, 0x57, 0x47, 0x63, 0x59, 0x37, 0x43, + 0x64, 0x54, 0x41, 0x6f, 0x54, 0x53, 0x78, 0x32, 0x77, 0x68, 0x38, 0x62, + 0x4f, 0x73, 0x6d, 0x44, 0x55, 0x6c, 0x6c, 0x77, 0x4f, 0x45, 0x37, 0x39, + 0x37, 0x38, 0x5a, 0x68, 0x54, 0x52, 0x73, 0x53, 0x4e, 0x30, 0x62, 0x64, + 0x35, 0x37, 0x65, 0x79, 0x6e, 0x44, 0x61, 0x61, 0x65, 0x32, 0x33, 0x54, + 0x77, 0x49, 0x6e, 0x68, 0x65, 0x7a, 0x35, 0x69, 0x48, 0x4f, 0x78, 0x75, + 0x57, 0x6c, 0x42, 0x79, 0x6a, 0x45, 0x76, 0x34, 0x52, 0x79, 0x44, 0x50, + 0x53, 0x63, 0x30, 0x66, 0x41, 0x53, 0x6f, 0x4b, 0x77, 0x48, 0x37, 0x49, + 0x41, 0x6a, 0x37, 0x56, 0x59, 0x4d, 0x7a, 0x75, 0x6a, 0x37, 0x32, 0x2f, + 0x63, 0x39, 0x78, 0x4c, 0x4f, 0x41, 0x35, 0x34, 0x58, 0x75, 0x79, 0x72, + 0x75, 0x61, 0x4d, 0x64, 0x71, 0x69, 0x30, 0x7a, 0x6c, 0x41, 0x34, 0x76, + 0x2f, 0x61, 0x66, 0x37, 0x78, 0x4a, 0x39, 0x30, 0x58, 0x56, 0x4d, 0x57, + 0x64, 0x57, 0x39, 0x36, 0x45, 0x47, 0x42, 0x47, 0x6b, 0x49, 0x6c, 0x6f, + 0x33, 0x33, 0x66, 0x41, 0x3d, 0x3d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x01, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2d, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x02, + 0x1a, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2d, 0x72, 0x75, 0x6c, + 0x65, 0x52, 0x55, 0x4c, 0x45, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, + 0x73, 0x6c, 0x65, 0x65, 0x70, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x20, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x72, + 0x65, 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f, + 0x73, 0x74, 0x61, 0x74, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, + 0x74, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x0a, 0x4d, 0x45, 0x54, 0x48, 0x4f, + 0x44, 0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x0a, 0x41, 0x54, 0x20, 0x45, + 0x4e, 0x54, 0x52, 0x59, 0x0a, 0x49, 0x46, 0x20, 0x74, 0x72, 0x75, 0x65, + 0x0a, 0x44, 0x4f, 0x0a, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x28, 0x22, 0x73, 0x6c, + 0x65, 0x65, 0x70, 0x2d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x22, + 0x29, 0x0a, 0x45, 0x4e, 0x44, 0x52, 0x55, 0x4c, 0x45, 0x0a, 0x0a, 0x52, + 0x55, 0x4c, 0x45, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x73, 0x6c, 0x65, + 0x65, 0x70, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x69, 0x6e, + 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, + 0x20, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x0a, + 0x43, 0x4c, 0x41, 0x53, 0x53, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x65, + 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f, 0x73, + 0x74, 0x61, 0x74, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, + 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x0a, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, + }; + + private static final byte[] ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_C = new byte[] { + 0x20, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x0a, 0x48, 0x45, 0x4c, 0x50, 0x45, + 0x52, 0x20, 0x6f, 0x72, 0x67, 0x2e, 0x6a, 0x62, 0x6f, 0x73, 0x73, 0x2e, + 0x62, 0x79, 0x74, 0x65, 0x6d, 0x61, 0x6e, 0x2e, 0x74, 0x68, 0x65, 0x72, + 0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x2e, 0x68, 0x65, 0x6c, 0x70, 0x65, + 0x72, 0x2e, 0x54, 0x68, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, + 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x0a, 0x41, 0x54, 0x20, 0x45, 0x4e, + 0x54, 0x52, 0x59, 0x0a, 0x42, 0x49, 0x4e, 0x44, 0x20, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x20, 0x69, + 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x61, 0x64, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x28, 0x22, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x2d, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x29, 0x0a, 0x49, 0x46, + 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x20, 0x25, 0x20, 0x31, 0x30, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x0a, + 0x44, 0x4f, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x28, 0x22, 0x63, 0x6f, 0x6d, + 0x2e, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, + 0x6d, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x2e, 0x62, 0x79, 0x74, 0x65, 0x6d, + 0x61, 0x6e, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x66, 0x69, + 0x6e, 0x69, 0x74, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x22, 0x2c, 0x20, 0x20, + 0x22, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x73, 0x6c, 0x65, 0x65, + 0x70, 0x28, 0x29, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2c, 0x20, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x29, 0x3b, 0x0a, 0x45, 0x4e, 0x44, 0x52, 0x55, 0x4c, 0x45, 0x0a, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x58, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x2d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x4d, 0x64, 0x4a, 0x30, 0x38, + 0x6c, 0x51, 0x34, 0x2b, 0x34, 0x48, 0x39, 0x61, 0x73, 0x4f, 0x51, 0x61, + 0x59, 0x78, 0x56, 0x36, 0x58, 0x44, 0x58, 0x62, 0x49, 0x43, 0x69, 0x33, + 0x6e, 0x6a, 0x49, 0x37, 0x4a, 0x6d, 0x5a, 0x39, 0x57, 0x38, 0x57, 0x67, + 0x4e, 0x37, 0x4c, 0x57, 0x38, 0x52, 0x49, 0x59, 0x5a, 0x30, 0x61, 0x43, + 0x46, 0x70, 0x61, 0x48, 0x51, 0x49, 0x48, 0x73, 0x47, 0x33, 0x38, 0x6d, + 0x4f, 0x4c, 0x76, 0x5a, 0x52, 0x7a, 0x4c, 0x4e, 0x77, 0x49, 0x34, 0x35, + 0x4e, 0x47, 0x2f, 0x71, 0x6f, 0x78, 0x56, 0x75, 0x2b, 0x42, 0x41, 0x38, + 0x65, 0x6b, 0x45, 0x42, 0x70, 0x4c, 0x6c, 0x48, 0x43, 0x6f, 0x74, 0x78, + 0x4c, 0x6b, 0x2b, 0x31, 0x46, 0x37, 0x79, 0x5a, 0x6f, 0x6e, 0x6d, 0x6b, + 0x45, 0x66, 0x58, 0x71, 0x36, 0x6f, 0x34, 0x69, 0x6d, 0x73, 0x56, 0x55, + 0x2f, 0x38, 0x45, 0x55, 0x33, 0x36, 0x44, 0x68, 0x37, 0x59, 0x52, 0x64, + 0x35, 0x71, 0x4f, 0x6e, 0x65, 0x72, 0x4a, 0x37, 0x4e, 0x49, 0x74, 0x72, + 0x77, 0x2f, 0x6b, 0x42, 0x75, 0x56, 0x61, 0x41, 0x6c, 0x73, 0x56, 0x4c, + 0x32, 0x42, 0x62, 0x6a, 0x37, 0x72, 0x79, 0x76, 0x56, 0x56, 0x45, 0x30, + 0x31, 0x47, 0x67, 0x4d, 0x4a, 0x6c, 0x42, 0x4c, 0x51, 0x6a, 0x6e, 0x6a, + 0x62, 0x32, 0x31, 0x65, 0x4f, 0x32, 0x79, 0x67, 0x32, 0x77, 0x34, 0x41, + 0x4b, 0x44, 0x4f, 0x67, 0x67, 0x48, 0x4d, 0x77, 0x63, 0x43, 0x41, 0x61, + 0x5a, 0x6c, 0x31, 0x77, 0x57, 0x31, 0x70, 0x4b, 0x51, 0x4a, 0x41, 0x76, + 0x6c, 0x54, 0x30, 0x46, 0x59, 0x48, 0x72, 0x77, 0x53, 0x55, 0x4a, 0x51, + 0x35, 0x31, 0x4a, 0x72, 0x5a, 0x45, 0x55, 0x4a, 0x6a, 0x46, 0x78, 0x5a, + 0x33, 0x38, 0x78, 0x78, 0x59, 0x48, 0x41, 0x30, 0x48, 0x4d, 0x66, 0x66, + 0x55, 0x69, 0x44, 0x63, 0x7a, 0x71, 0x6b, 0x46, 0x6a, 0x30, 0x38, 0x77, + 0x35, 0x39, 0x47, 0x2f, 0x36, 0x35, 0x32, 0x4a, 0x59, 0x2f, 0x68, 0x57, + 0x70, 0x6f, 0x4d, 0x39, 0x75, 0x7a, 0x77, 0x5a, 0x50, 0x2b, 0x74, 0x56, + 0x34, 0x73, 0x43, 0x57, 0x58, 0x46, 0x46, 0x35, 0x49, 0x75, 0x42, 0x42, + 0x38, 0x50, 0x46, 0x77, 0x61, 0x65, 0x71, 0x67, 0x58, 0x44, 0x77, 0x72, + 0x62, 0x78, 0x67, 0x59, 0x4d, 0x6a, 0x43, 0x67, 0x6b, 0x39, 0x6e, 0x4e, + 0x5a, 0x75, 0x76, 0x64, 0x73, 0x64, 0x61, 0x56, 0x31, 0x62, 0x47, 0x52, + 0x51, 0x3d, 0x3d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x05, 0x6c, + 0x69, 0x73, 0x74, 0x65, 0x6e, 0x2d, 0x70, 0x6f, 0x72, 0x74, 0x31, 0x33, + 0x33, 0x30, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x46, 0x72, + 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x63, 0x6f, 0x6d, 0x2e, 0x72, + 0x65, 0x64, 0x68, 0x61, 0x74, 0x2e, 0x74, 0x68, 0x65, 0x72, 0x6d, 0x6f, + 0x73, 0x74, 0x61, 0x74, 0x2e, 0x76, 0x6d, 0x2e, 0x62, 0x79, 0x74, 0x65, + 0x6d, 0x61, 0x6e, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x6d, + 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x24, 0x76, 0x6d, 0x2d, 0x69, 0x64, 0x66, 0x35, 0x62, 0x34, 0x63, 0x63, + 0x34, 0x34, 0x2d, 0x30, 0x36, 0x66, 0x32, 0x2d, 0x34, 0x38, 0x34, 0x37, + 0x2d, 0x39, 0x37, 0x64, 0x33, 0x2d, 0x62, 0x35, 0x62, 0x30, 0x62, 0x31, + 0x39, 0x38, 0x66, 0x38, 0x34, 0x33 + }; + + /* + * This is serialized format for + * req = new Request(RequestType.RESPONSE_EXPECTED, blah) + */ + private static final byte[] ENCODED_REQUEST_WITH_NO_PARAMS = new byte[] { + 0x00, 0x00, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, + 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, 0x00, 0x00, 0x00, + 0x00 + }; + + private static final byte[][] GARBAGE_AS_REQUEST = new byte[][] { + // general garbage + { 0x0d, 0x0b, 0x0e, 0x0e, 0x0f }, + // first two bytes are broken + { 0x0f, 0x0d, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, + 0x45, 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, + 0x00, 0x00, 0x00, 0x00 }, + // last byte indicates params, which are missing + { 0x00, 0x00, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, + 0x45, 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, + 0x00, 0x00, 0x00, 0x0f } }; + + + private static final byte[] TYPE = RequestType.RESPONSE_EXPECTED.toString().getBytes(); + + private ChannelHandlerContext ctx; + private CommandChannelRequestDecoder decoder; + + @Before + public void setUp() { + ctx = mock(ChannelHandlerContext.class); + when(ctx.channel()).thenReturn(mock(Channel.class)); + decoder = new CommandChannelRequestDecoder(); + } + + @Test + public void testDecode() throws Exception { + ByteBuf buffer = Unpooled.buffer(); + buffer.writeInt(TYPE.length); + buffer.writeBytes(TYPE); + buffer.writeInt(0); + + List<Object> resultList = new ArrayList<>(); + decoder.decode(ctx, buffer, resultList); + assertEquals(1, resultList.size()); + Request request = (Request)resultList.get(0); + assertTrue(RequestType.RESPONSE_EXPECTED == (RequestType) request.getType()); + } + + @Test + public void testDecodeWithParameters() throws Exception { + String parmName = "parameter"; + String parmValue = "hello"; + ByteBuf buffer = Unpooled.buffer(); + buffer.writeInt(TYPE.length); + buffer.writeBytes(TYPE); + buffer.writeInt(1); + buffer.writeInt(parmName.getBytes().length); + buffer.writeInt(parmValue.getBytes().length); + buffer.writeBytes(parmName.getBytes()); + buffer.writeBytes(parmValue.getBytes()); + + List<Object> resultList = new ArrayList<>(); + decoder.decode(ctx, buffer, resultList); + assertEquals(1, resultList.size()); + Request request = (Request) resultList.get(0); + Collection<String> parmNames = request.getParameterNames(); + + assertEquals(1, parmNames.size()); + assertTrue(parmNames.contains(parmName)); + String decodedValue = request.getParameter(parmName); + assertEquals(parmValue, decodedValue); + } + + @Test + public void testDecodeWithLongParams() throws Exception { + String parmName = "parameter"; + String param2Name = "foo-bar"; + String param2Value = "RESPONSE_EXPECTED"; + ByteBuf buffer = Unpooled.buffer(); + buffer.writeInt(TYPE.length); + buffer.writeBytes(TYPE); + buffer.writeInt(2); + buffer.writeInt(parmName.getBytes().length); + buffer.writeInt(LONG_PARAM_VALUE.getBytes().length); + buffer.writeBytes(parmName.getBytes()); + buffer.writeBytes(LONG_PARAM_VALUE.getBytes()); + buffer.writeInt(param2Name.getBytes().length); + buffer.writeInt(param2Value.getBytes().length); + buffer.writeBytes(param2Name.getBytes()); + buffer.writeBytes(param2Value.getBytes()); + + List<Object> resultList = new ArrayList<>(); + decoder.decode(ctx, buffer, resultList); + assertEquals(1, resultList.size()); + Request request = (Request) resultList.get(0); + Collection<String> parmNames = request.getParameterNames(); + + assertEquals(2, parmNames.size()); + assertTrue(parmNames.contains(parmName)); + String decodedValue = request.getParameter(parmName); + assertEquals(LONG_PARAM_VALUE, decodedValue); + } + + @Test + public void testDecodeWithParametersFromBytesArray() throws Exception { + ByteBuf buffer = Unpooled.copiedBuffer(ENCODED_REQUEST_WITH_PARAMS); + Request expected = new Request(RequestType.RESPONSE_EXPECTED, null); + expected.setParameter("param1", "value1"); + expected.setParameter("param2", "value2"); + List<Object> resultList = new ArrayList<>(); + new CommandChannelRequestDecoder().decode(ctx, buffer, resultList); + assertEquals(1, resultList.size()); + Message actual = (Message)resultList.get(0); + assertTrue(actual instanceof Request); + assertTrue(Messages.equal(expected, (Request)actual)); + InetSocketAddress addr = new InetSocketAddress(1234); + buffer = Unpooled.copiedBuffer(ENCODED_REQUEST_WITH_NO_PARAMS); + expected = new Request(RequestType.RESPONSE_EXPECTED, addr); + resultList = new ArrayList<>(); + new CommandChannelRequestDecoder().decode(ctx, buffer, resultList); + assertEquals(1, resultList.size()); + actual = (Message)resultList.get(0); + assertTrue(actual instanceof Request); + assertTrue(Messages.equal(expected, (Request)actual)); + } + + /** + * Tests the case where the decode() method is called multiple times due + * to some data not yet available so as to be able to decode the request in + * it's entirety. + * + * @throws Exception + */ + @Test + public void testDecodeWithParamsFromBytes() throws Exception { + CommandChannelRequestDecoder decoder = new CommandChannelRequestDecoder(); + + // First decode all-at-once + List<Object> allAtOnceResultList = new ArrayList<>(); + ByteBuf firstBuffer = Unpooled.copiedBuffer(ENCODED_REQUEST_WITH_MANY_PARAMS); + decoder.decode(ctx, firstBuffer, allAtOnceResultList); + assertEquals(1, allAtOnceResultList.size()); + Message firstActual = (Message)allAtOnceResultList.get(0); + assertTrue(firstActual instanceof Request); + Request firstRequest = (Request)firstActual; + + List<Object> resultList = new ArrayList<>(); + ByteBuf buffer = Unpooled.copiedBuffer(ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_A); + int bytesAvailable = ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_A.length; + assertEquals(bytesAvailable, buffer.readableBytes()); + decoder.decode(ctx, buffer, resultList); // request type got decoded + assertEquals(bytesAvailable, buffer.readableBytes()); + assertEquals(0, resultList.size()); + ByteBuf remaining = Unpooled.copiedBuffer(buffer); + assertEquals(bytesAvailable, remaining.array().length); + buffer = Unpooled.copiedBuffer(remaining.array(), ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_B); + bytesAvailable = remaining.array().length + ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_B.length; + assertEquals(bytesAvailable, buffer.readableBytes()); + decoder.decode(ctx, buffer, resultList); // nothing additional should get decoded + assertEquals(bytesAvailable, buffer.readableBytes()); + assertEquals(0, resultList.size()); + buffer = Unpooled.copiedBuffer(remaining.array(), + ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_B, + ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_C); + bytesAvailable = remaining.array().length + + ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_B.length + + ENCODED_INCOMPLETE_REQUEST_WITH_MANY_PARAMS_C.length; + assertEquals(bytesAvailable, buffer.readableBytes()); + decoder.decode(ctx, buffer, resultList); // this time decoding should complete + assertEquals(0, buffer.readableBytes()); + assertEquals(1, resultList.size()); + Message actual = (Message)resultList.get(0); + assertTrue(actual instanceof Request); + Request actualReq = (Request) actual; + assertEquals(RequestType.RESPONSE_EXPECTED, actualReq.getType()); + assertEquals("Expected 8 parameters", 8, actualReq.getParameterNames().size()); + assertEquals(Integer.toString(0), actualReq.getParameter("byteman-action")); + String rule = actualReq.getParameter("byteman-rule"); + assertNotNull(rule); + assertEquals(538, rule.length()); + assertTrue(Messages.equal(firstRequest, actualReq)); + } + + @Test + public void decodingOfGarbageDoesNotAddToResultList() + throws Exception { + for (int i = 0; i < GARBAGE_AS_REQUEST.length; i++) { + ByteBuf buffer = Unpooled + .copiedBuffer(GARBAGE_AS_REQUEST[0]); + CommandChannelRequestDecoder decoder = new CommandChannelRequestDecoder(); + List<Object> outList = new ArrayList<>(); + decoder.decode(ctx, buffer, outList); + assertEquals(0, outList.size()); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/JsonResponseParserTest.java Thu May 12 15:46:30 2016 +0200 @@ -0,0 +1,199 @@ +/* + * Copyright 2012-2016 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.server.internal; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ByteChannel; +import java.nio.charset.Charset; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.redhat.thermostat.common.command.Response; +import com.redhat.thermostat.common.command.Response.ResponseType; + +public class JsonResponseParserTest { + + private ByteChannel agentChannel; + private JsonResponseParser parser; + + @Before + public void setUp() { + agentChannel = mock(ByteChannel.class); + parser = new JsonResponseParser(); + } + + @Test + public void testSuccess() throws IOException { + JsonObject jsonResponse = createResponse(); + byte[] encoded = toJson(jsonResponse); + mockByteChannel(encoded); + + Response response = parser.parseResponse(agentChannel); + assertEquals(ResponseType.OK, response.getType()); + } + + @Test(expected=IOException.class) + public void testEmpty() throws IOException { + mockByteChannel(new byte[0]); + parser.parseResponse(agentChannel); + } + + @Test(expected=IOException.class) + public void testNotJson() throws IOException { + mockByteChannel("Not JSON".getBytes(Charset.forName("UTF-8"))); + parser.parseResponse(agentChannel); + } + + @Test(expected=IOException.class) + public void testBadResponseRoot() throws IOException { + byte[] encoded = toJson(new JsonArray()); + mockByteChannel(encoded); + parser.parseResponse(agentChannel); + } + + @Test(expected=IOException.class) + public void testParamsMissing() throws IOException { + byte[] encoded = toJson(new JsonObject()); + mockByteChannel(encoded); + parser.parseResponse(agentChannel); + } + + @Test(expected=IOException.class) + public void testBadParams() throws IOException { + JsonObject jsonResponse = createResponse(); + jsonResponse.add(CommandChannelConstants.RESPONSE_JSON_TOP, new JsonArray()); + + byte[] encoded = toJson(new JsonObject()); + mockByteChannel(encoded); + parser.parseResponse(agentChannel); + } + + @Test(expected=IOException.class) + public void testNullParams() throws IOException { + JsonObject jsonResponse = createResponse(); + jsonResponse.add(CommandChannelConstants.RESPONSE_JSON_TOP, null); + + byte[] encoded = toJson(new JsonObject()); + mockByteChannel(encoded); + parser.parseResponse(agentChannel); + } + + @Test(expected=IOException.class) + public void testResponseTypeMissing() throws IOException { + JsonObject root = createResponse(); + JsonObject jsonResponse = root.get(CommandChannelConstants.RESPONSE_JSON_TOP).getAsJsonObject(); + jsonResponse.remove(CommandChannelConstants.RESPONSE_JSON_TYPE); + + byte[] encoded = toJson(root); + mockByteChannel(encoded); + parser.parseResponse(agentChannel); + } + + @Test(expected=IOException.class) + public void testResponseTypeNotString() throws IOException { + JsonObject root = createResponse(); + JsonObject jsonResponse = root.get(CommandChannelConstants.RESPONSE_JSON_TOP).getAsJsonObject(); + jsonResponse.add(CommandChannelConstants.RESPONSE_JSON_TYPE, new JsonArray()); + + byte[] encoded = toJson(root); + mockByteChannel(encoded); + parser.parseResponse(agentChannel); + } + + @Test(expected=IOException.class) + public void testResponseTypeNull() throws IOException { + JsonObject root = createResponse(); + JsonObject jsonResponse = root.get(CommandChannelConstants.RESPONSE_JSON_TOP).getAsJsonObject(); + jsonResponse.addProperty(CommandChannelConstants.RESPONSE_JSON_TYPE, (String) null); + + byte[] encoded = toJson(root); + mockByteChannel(encoded); + parser.parseResponse(agentChannel); + } + + @Test(expected=IOException.class) + public void testResponseTypeNotEnum() throws IOException { + JsonObject root = createResponse(); + JsonObject jsonResponse = root.get(CommandChannelConstants.RESPONSE_JSON_TOP).getAsJsonObject(); + jsonResponse.addProperty(CommandChannelConstants.RESPONSE_JSON_TYPE, "Not a ResponseType"); + + byte[] encoded = toJson(root); + mockByteChannel(encoded); + parser.parseResponse(agentChannel); + } + + private JsonObject createResponse() { + JsonObject params = new JsonObject(); + params.addProperty(CommandChannelConstants.RESPONSE_JSON_TYPE, ResponseType.OK.name()); + JsonObject root = new JsonObject(); + root.add(CommandChannelConstants.RESPONSE_JSON_TOP, params); + return root; + } + + private byte[] toJson(JsonElement element) { + Gson gson = new GsonBuilder().serializeNulls().create(); + String jsonString = gson.toJson(element); + return jsonString.getBytes(Charset.forName("UTF-8")); + } + + private void mockByteChannel(final byte[] encoded) throws IOException { + when(agentChannel.read(any(ByteBuffer.class))).thenAnswer(new Answer<Integer>() { + @Override + public Integer answer(InvocationOnMock invocation) throws Throwable { + ByteBuffer buf = (ByteBuffer) invocation.getArguments()[0]; + buf.put(encoded); + buf.flip(); + return encoded.length; + } + }); + } + +}
--- a/agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/RequestDecoderTest.java Thu May 12 11:17:00 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,189 +0,0 @@ -/* - * Copyright 2012-2016 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.server.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; - -import com.redhat.thermostat.common.command.Message; -import com.redhat.thermostat.common.command.Messages; -import com.redhat.thermostat.common.command.Request; -import com.redhat.thermostat.common.command.Request.RequestType; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; - -public class RequestDecoderTest { - - /* - * This is serialized format for - * req = new Request(RequestType.RESPONSE_EXPECTED, blah) - * req.setParameter("param1", "value1"); - * req.setParameter("param2", "value2"); - */ - private static final byte[] ENCODED_REQEUEST_WITH_PARAMS = new byte[] { - 0x00, 0x00, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, - 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x70, 0x61, 0x72, - 0x61, 0x6d, 0x31, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x31, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x32, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x32 - }; - - /* - * This is serialized format for - * req = new Request(RequestType.RESPONSE_EXPECTED, blah) - */ - private static final byte[] ENCODED_REQUEST_WITH_NO_PARAMS = new byte[] { - 0x00, 0x00, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, - 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, 0x00, 0x00, 0x00, - 0x00 - }; - - private static final byte[][] GARBAGE_AS_REQUEST = new byte[][] { - // general garbage - { 0x0d, 0x0b, 0x0e, 0x0e, 0x0f }, - // first two bytes are broken - { 0x0f, 0x0d, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, - 0x45, 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, - 0x00, 0x00, 0x00, 0x00 }, - // last byte indicates params, which are missing - { 0x00, 0x00, 0x00, 0x11, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, - 0x45, 0x5f, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, - 0x00, 0x00, 0x00, 0x0f } }; - - - private static final byte[] TYPE = RequestType.RESPONSE_EXPECTED.toString().getBytes(); - - private ChannelHandlerContext ctx; - private CommandChannelRequestDecoder decoder; - - @Before - public void setUp() { - ctx = mock(ChannelHandlerContext.class); - when(ctx.channel()).thenReturn(mock(Channel.class)); - decoder = new CommandChannelRequestDecoder(); - } - - @Test - public void testDecode() throws Exception { - ByteBuf buffer = Unpooled.buffer(); - buffer.writeInt(TYPE.length); - buffer.writeBytes(TYPE); - buffer.writeInt(0); - - List<Object> resultList = new ArrayList<>(); - decoder.decode(ctx, buffer, resultList); - assertEquals(1, resultList.size()); - Request request = (Request)resultList.get(0); - assertTrue(RequestType.RESPONSE_EXPECTED == (RequestType) request.getType()); - } - - @Test - public void testDecodeWithParameters() throws Exception { - String parmName = "parameter"; - String parmValue = "hello"; - ByteBuf buffer = Unpooled.buffer(); - buffer.writeInt(TYPE.length); - buffer.writeBytes(TYPE); - buffer.writeInt(1); - buffer.writeInt(parmName.getBytes().length); - buffer.writeInt(parmValue.getBytes().length); - buffer.writeBytes(parmName.getBytes()); - buffer.writeBytes(parmValue.getBytes()); - - List<Object> resultList = new ArrayList<>(); - decoder.decode(ctx, buffer, resultList); - assertEquals(1, resultList.size()); - Request request = (Request) resultList.get(0); - Collection<String> parmNames = request.getParameterNames(); - - assertEquals(1, parmNames.size()); - assertTrue(parmNames.contains(parmName)); - String decodedValue = request.getParameter(parmName); - assertEquals(parmValue, decodedValue); - } - - @Test - public void testDecodeWithParametersFromBytesArray() throws Exception { - ByteBuf buffer = Unpooled.copiedBuffer(ENCODED_REQEUEST_WITH_PARAMS); - Request expected = new Request(RequestType.RESPONSE_EXPECTED, null); - expected.setParameter("param1", "value1"); - expected.setParameter("param2", "value2"); - List<Object> resultList = new ArrayList<>(); - new CommandChannelRequestDecoder().decode(ctx, buffer, resultList); - assertEquals(1, resultList.size()); - Message actual = (Message)resultList.get(0); - assertTrue(actual instanceof Request); - assertTrue(Messages.equal(expected, (Request)actual)); - InetSocketAddress addr = new InetSocketAddress(1234); - buffer = Unpooled.copiedBuffer(ENCODED_REQUEST_WITH_NO_PARAMS); - expected = new Request(RequestType.RESPONSE_EXPECTED, addr); - resultList = new ArrayList<>(); - new CommandChannelRequestDecoder().decode(ctx, buffer, resultList); - assertEquals(1, resultList.size()); - actual = (Message)resultList.get(0); - assertTrue(actual instanceof Request); - assertTrue(Messages.equal(expected, (Request)actual)); - } - - @Test - public void decodingOfGarbageDoesNotAddToResultList() - throws Exception { - for (int i = 0; i < GARBAGE_AS_REQUEST.length; i++) { - ByteBuf buffer = Unpooled - .copiedBuffer(GARBAGE_AS_REQUEST[0]); - CommandChannelRequestDecoder decoder = new CommandChannelRequestDecoder(); - List<Object> outList = new ArrayList<>(); - decoder.decode(ctx, buffer, outList); - assertEquals(0, outList.size()); - } - } -} -
--- a/agent/command-server/src/test/java/com/redhat/thermostat/agent/command/server/internal/ResponseParserTest.java Thu May 12 11:17:00 2016 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,199 +0,0 @@ -/* - * Copyright 2012-2016 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.server.internal; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.ByteChannel; -import java.nio.charset.Charset; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.redhat.thermostat.common.command.Response; -import com.redhat.thermostat.common.command.Response.ResponseType; - -public class ResponseParserTest { - - private ByteChannel agentChannel; - private JsonResponseParser parser; - - @Before - public void setUp() { - agentChannel = mock(ByteChannel.class); - parser = new JsonResponseParser(); - } - - @Test - public void testSuccess() throws IOException { - JsonObject jsonResponse = createResponse(); - byte[] encoded = toJson(jsonResponse); - mockByteChannel(encoded); - - Response response = parser.parseResponse(agentChannel); - assertEquals(ResponseType.OK, response.getType()); - } - - @Test(expected=IOException.class) - public void testEmpty() throws IOException { - mockByteChannel(new byte[0]); - parser.parseResponse(agentChannel); - } - - @Test(expected=IOException.class) - public void testNotJson() throws IOException { - mockByteChannel("Not JSON".getBytes(Charset.forName("UTF-8"))); - parser.parseResponse(agentChannel); - } - - @Test(expected=IOException.class) - public void testBadResponseRoot() throws IOException { - byte[] encoded = toJson(new JsonArray()); - mockByteChannel(encoded); - parser.parseResponse(agentChannel); - } - - @Test(expected=IOException.class) - public void testParamsMissing() throws IOException { - byte[] encoded = toJson(new JsonObject()); - mockByteChannel(encoded); - parser.parseResponse(agentChannel); - } - - @Test(expected=IOException.class) - public void testBadParams() throws IOException { - JsonObject jsonResponse = createResponse(); - jsonResponse.add(CommandChannelConstants.RESPONSE_JSON_TOP, new JsonArray()); - - byte[] encoded = toJson(new JsonObject()); - mockByteChannel(encoded); - parser.parseResponse(agentChannel); - } - - @Test(expected=IOException.class) - public void testNullParams() throws IOException { - JsonObject jsonResponse = createResponse(); - jsonResponse.add(CommandChannelConstants.RESPONSE_JSON_TOP, null); - - byte[] encoded = toJson(new JsonObject()); - mockByteChannel(encoded); - parser.parseResponse(agentChannel); - } - - @Test(expected=IOException.class) - public void testResponseTypeMissing() throws IOException { - JsonObject root = createResponse(); - JsonObject jsonResponse = root.get(CommandChannelConstants.RESPONSE_JSON_TOP).getAsJsonObject(); - jsonResponse.remove(CommandChannelConstants.RESPONSE_JSON_TYPE); - - byte[] encoded = toJson(root); - mockByteChannel(encoded); - parser.parseResponse(agentChannel); - } - - @Test(expected=IOException.class) - public void testResponseTypeNotString() throws IOException { - JsonObject root = createResponse(); - JsonObject jsonResponse = root.get(CommandChannelConstants.RESPONSE_JSON_TOP).getAsJsonObject(); - jsonResponse.add(CommandChannelConstants.RESPONSE_JSON_TYPE, new JsonArray()); - - byte[] encoded = toJson(root); - mockByteChannel(encoded); - parser.parseResponse(agentChannel); - } - - @Test(expected=IOException.class) - public void testResponseTypeNull() throws IOException { - JsonObject root = createResponse(); - JsonObject jsonResponse = root.get(CommandChannelConstants.RESPONSE_JSON_TOP).getAsJsonObject(); - jsonResponse.addProperty(CommandChannelConstants.RESPONSE_JSON_TYPE, (String) null); - - byte[] encoded = toJson(root); - mockByteChannel(encoded); - parser.parseResponse(agentChannel); - } - - @Test(expected=IOException.class) - public void testResponseTypeNotEnum() throws IOException { - JsonObject root = createResponse(); - JsonObject jsonResponse = root.get(CommandChannelConstants.RESPONSE_JSON_TOP).getAsJsonObject(); - jsonResponse.addProperty(CommandChannelConstants.RESPONSE_JSON_TYPE, "Not a ResponseType"); - - byte[] encoded = toJson(root); - mockByteChannel(encoded); - parser.parseResponse(agentChannel); - } - - private JsonObject createResponse() { - JsonObject params = new JsonObject(); - params.addProperty(CommandChannelConstants.RESPONSE_JSON_TYPE, ResponseType.OK.name()); - JsonObject root = new JsonObject(); - root.add(CommandChannelConstants.RESPONSE_JSON_TOP, params); - return root; - } - - private byte[] toJson(JsonElement element) { - Gson gson = new GsonBuilder().serializeNulls().create(); - String jsonString = gson.toJson(element); - return jsonString.getBytes(Charset.forName("UTF-8")); - } - - private void mockByteChannel(final byte[] encoded) throws IOException { - when(agentChannel.read(any(ByteBuffer.class))).thenAnswer(new Answer<Integer>() { - @Override - public Integer answer(InvocationOnMock invocation) throws Throwable { - ByteBuffer buf = (ByteBuffer) invocation.getArguments()[0]; - buf.put(encoded); - buf.flip(); - return encoded.length; - } - }); - } - -}