// Copyright (c) 2007-Present Pivotal Software, Inc. All rights reserved. // // This software, the RabbitMQ Java client library, is triple-licensed under the // Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2 // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, // please see LICENSE-APACHE2. // // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, // either express or implied. See the LICENSE file for specific language governing // rights and limitations of this software. // // If you have any questions regarding licensing, please contact us at // info@rabbitmq.com. package com.rabbitmq.client.test.functional; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.DefaultSocketConfigurator; import com.rabbitmq.client.impl.*; import com.rabbitmq.client.impl.recovery.AutorecoveringConnection; import com.rabbitmq.client.test.BrokerTestCase; import com.rabbitmq.client.test.TestUtils; import org.junit.Test; import javax.net.SocketFactory; import java.io.IOException; import java.net.Socket; /** * Test that the server correctly handles us when we send it bad frames */ public class UnexpectedFrames extends BrokerTestCase { private interface Confuser { public Frame confuse(Frame frame) throws IOException; } private static class ConfusedFrameHandler extends SocketFrameHandler { private boolean confusedOnce = false; public ConfusedFrameHandler(Socket socket) throws IOException { super(socket); } @Override public void writeFrame(Frame frame) throws IOException { if (confusedOnce) { super.writeFrame(frame); } else { Frame confusedFrame = confuser.confuse(frame); if (confusedFrame != frame) confusedOnce = true; if (confusedFrame != null) { super.writeFrame(confusedFrame); } } } public Confuser confuser = new Confuser() { public Frame confuse(Frame frame) { // Do nothing to start with, we need to negotiate before the // server will send us unexpected_frame errors return frame; } }; } private static class ConfusedConnectionFactory extends ConnectionFactory { public ConfusedConnectionFactory() { super(); if(TestUtils.USE_NIO) { useNio(); } else { useBlockingIo(); } } @Override protected FrameHandlerFactory createFrameHandlerFactory() { return new ConfusedFrameHandlerFactory(); } } private static class ConfusedFrameHandlerFactory extends SocketFrameHandlerFactory { private ConfusedFrameHandlerFactory() { super(1000, SocketFactory.getDefault(), new DefaultSocketConfigurator(), false); } @Override public FrameHandler create(Socket sock) throws IOException { return new ConfusedFrameHandler(sock); } } public UnexpectedFrames() { super(); connectionFactory = new ConfusedConnectionFactory(); } @Test public void missingHeader() throws IOException { expectUnexpectedFrameError(new Confuser() { public Frame confuse(Frame frame) { if (frame.type == AMQP.FRAME_HEADER) { return null; } return frame; } }); } @Test public void missingMethod() throws IOException { expectUnexpectedFrameError(new Confuser() { public Frame confuse(Frame frame) { if (frame.type == AMQP.FRAME_METHOD) { // We can't just skip the method as that will lead us to // send 0 bytes and hang waiting for a response. return new Frame(AMQP.FRAME_HEADER, frame.channel, frame.getPayload()); } return frame; } }); } @Test public void missingBody() throws IOException { expectUnexpectedFrameError(new Confuser() { public Frame confuse(Frame frame) { if (frame.type == AMQP.FRAME_BODY) { return null; } return frame; } }); } @Test public void wrongClassInHeader() throws IOException { expectUnexpectedFrameError(new Confuser() { public Frame confuse(Frame frame) { if (frame.type == AMQP.FRAME_HEADER) { byte[] payload = frame.getPayload(); Frame confusedFrame = new Frame(AMQP.FRAME_HEADER, frame.channel, payload); // First two bytes = class ID, must match class ID from // method. payload[0] = 12; payload[1] = 34; return confusedFrame; } return frame; } }); } @Test public void heartbeatOnChannel() throws IOException { expectUnexpectedFrameError(new Confuser() { public Frame confuse(Frame frame) { if (frame.type == AMQP.FRAME_METHOD) { return new Frame(AMQP.FRAME_HEARTBEAT, frame.channel); } return frame; } }); } @Test public void unknownFrameType() throws IOException { expectError(AMQP.FRAME_ERROR, new Confuser() { public Frame confuse(Frame frame) { if (frame.type == AMQP.FRAME_METHOD) { return new Frame(0, frame.channel, "1234567890\0001234567890".getBytes()); } return frame; } }); } private void expectError(int error, Confuser confuser) throws IOException { ((ConfusedFrameHandler)((AutorecoveringConnection)connection).getDelegate().getFrameHandler()). confuser = confuser; //NB: the frame confuser relies on the encoding of the //method field to be at least 8 bytes long channel.basicPublish("", "routing key", null, "Hello".getBytes()); expectError(error); } private void expectUnexpectedFrameError(Confuser confuser) throws IOException { expectError(AMQP.UNEXPECTED_FRAME, confuser); } }