blob: aba70f0817b1da9ba77ad4cd72cfac5555db1d78 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.qpid.protonj2.engine.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.apache.qpid.protonj2.buffer.ProtonBuffer;
import org.apache.qpid.protonj2.buffer.ProtonBufferAllocator;
import org.apache.qpid.protonj2.engine.Connection;
import org.apache.qpid.protonj2.engine.Engine;
import org.apache.qpid.protonj2.engine.EngineFactory;
import org.apache.qpid.protonj2.engine.OutgoingDelivery;
import org.apache.qpid.protonj2.engine.Receiver;
import org.apache.qpid.protonj2.engine.Sender;
import org.apache.qpid.protonj2.engine.Session;
import org.apache.qpid.protonj2.engine.exceptions.FrameDecodingException;
import org.apache.qpid.protonj2.test.driver.ProtonTestConnector;
import org.apache.qpid.protonj2.types.transport.AmqpError;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
@Timeout(20)
public class ProtonDecodeErrorTest extends ProtonEngineTestSupport {
@Test
public void testEmptyContainerIdInOpenProvokesDecodeError() throws Exception {
// Provide the bytes for Open, but omit the mandatory container-id to provoke a decode error.
byte[] bytes = new byte[] { 0x00, 0x00, 0x00, 0x0F, // Frame size = 15 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x10, (byte) 0xC0, // Described-type, ulong type, open descriptor, list0.
0x03, 0x01, 0x40 }; // size (3), count (1), container-id (null).
doInvalidOpenProvokesDecodeErrorTestImpl(bytes, "The container-id field cannot be omitted from the Open");
}
@Test
public void testEmptyOpenProvokesDecodeError() throws Exception {
// Provide the bytes for Open, but omit the mandatory container-id to provoke a decode error.
byte[] bytes = new byte[] { 0x00, 0x00, 0x00, 0x0C, // Frame size = 12 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x10, 0x45};// Described-type, ulong type, open descriptor, list0.
doInvalidOpenProvokesDecodeErrorTestImpl(bytes, "The container-id field cannot be omitted from the Open");
}
private void doInvalidOpenProvokesDecodeErrorTestImpl(byte[] bytes, String errorDescription) throws Exception {
Engine engine = EngineFactory.PROTON.createNonSaslEngine();
engine.errorHandler(result -> failure = result.failureCause());
ProtonTestConnector peer = createTestPeer(engine);
peer.expectAMQPHeader().respondWithAMQPHeader();
peer.expectOpen();
engine.start().open();
peer.waitForScriptToCompleteIgnoreErrors();
peer.expectClose().withError(AmqpError.DECODE_ERROR.toString(), errorDescription);
peer.remoteBytes().withBytes(bytes).now();
peer.waitForScriptToCompleteIgnoreErrors();
assertNotNull(failure);
assertTrue(failure instanceof FrameDecodingException);
assertEquals(errorDescription, failure.getMessage());
}
@Test
public void testEmptyBeginProvokesDecodeError() throws Exception {
// Provide the bytes for Begin, but omit any fields to provoke a decode error.
byte[] bytes = new byte[] {
0x00, 0x00, 0x00, 0x0C, // Frame size = 12 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x11, 0x45};// Described-type, ulong type, Begin descriptor, list0.
doInvalidBeginProvokesDecodeErrorTestImpl(bytes, "The next-outgoing-id field cannot be omitted from the Begin");
}
@Test
public void testTruncatedBeginProvokesDecodeError1() throws Exception {
// Provide the bytes for Begin, but only give a null (i-e not-present) for the remote-channel.
byte[] bytes = new byte[] {
0x00, 0x00, 0x00, 0x0F, // Frame size = 15 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x11, (byte) 0xC0, // Described-type, ulong type, Begin descriptor, list8.
0x03, 0x01, 0x40 }; // size (3), count (1), remote-channel (null).
doInvalidBeginProvokesDecodeErrorTestImpl(bytes, "The next-outgoing-id field cannot be omitted from the Begin");
}
@Test
public void testTruncatedBeginProvokesDecodeError2() throws Exception {
// Provide the bytes for Begin, but only give a [not-present remote-channel +] next-outgoing-id and incoming-window. Provokes a decode error as there must be 4 fields.
byte[] bytes = new byte[] {
0x00, 0x00, 0x00, 0x11, // Frame size = 17 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x11, (byte) 0xC0, // Described-type, ulong type, Begin descriptor, list8.
0x05, 0x03, 0x40, 0x43, 0x43 }; // size (5), count (3), remote-channel (null), next-outgoing-id (uint0), incoming-window (uint0).
doInvalidBeginProvokesDecodeErrorTestImpl(bytes, "The outgoing-window field cannot be omitted from the Begin");
}
@Test
public void testTruncatedBeginProvokesDecodeError3() throws Exception {
// Provide the bytes for Begin, but only give a [not-present remote-channel +] next-outgoing-id and incoming-window, and send not-present/null for outgoing-window. Provokes a decode error as must be present.
byte[] bytes = new byte[] {
0x00, 0x00, 0x00, 0x12, // Frame size = 18 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x11, (byte) 0xC0, // Described-type, ulong type, Begin descriptor, list8.
0x06, 0x04, 0x40, 0x43, 0x43, 0x40 }; // size (5), count (4), remote-channel (null), next-outgoing-id (uint0), incoming-window (uint0), outgoing-window (null).
doInvalidBeginProvokesDecodeErrorTestImpl(bytes, "The outgoing-window field cannot be omitted from the Begin");
}
private void doInvalidBeginProvokesDecodeErrorTestImpl(byte[] bytes, String errorDescription) throws Exception {
Engine engine = EngineFactory.PROTON.createNonSaslEngine();
engine.errorHandler(result -> failure = result.failureCause());
ProtonTestConnector peer = createTestPeer(engine);
peer.expectAMQPHeader().respondWithAMQPHeader();
peer.expectOpen().respond();
engine.start().open();
peer.waitForScriptToCompleteIgnoreErrors();
peer.expectClose().withError(AmqpError.DECODE_ERROR.toString(), errorDescription);
peer.remoteBytes().withBytes(bytes).now();
peer.waitForScriptToCompleteIgnoreErrors();
assertNotNull(failure);
assertTrue(failure instanceof FrameDecodingException);
assertEquals(errorDescription, failure.getMessage());
}
@Test
public void testEmptyFlowProvokesDecodeError() throws Exception {
// Provide the bytes for Flow, but omit any fields to provoke a decode error.
byte[] bytes = new byte[] {
0x00, 0x00, 0x00, 0x0C, // Frame size = 12 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x13, 0x45};// Described-type, ulong type, Flow descriptor, list0.
doInvalidFlowProvokesDecodeErrorTestImpl(bytes, "The incoming-window field cannot be omitted from the Flow");
}
@Test
public void testTruncatedFlowProvokesDecodeError1() throws Exception {
// Provide the bytes for Flow, but only give a 0 for the next-incoming-id. Provokes a decode error as there must be 4 fields.
byte[] bytes = new byte[] {
0x00, 0x00, 0x00, 0x0F, // Frame size = 15 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x13, (byte) 0xC0, // Described-type, ulong type, Flow descriptor, list8.
0x03, 0x01, 0x43 }; // size (3), count (1), next-incoming-id (uint0).
doInvalidFlowProvokesDecodeErrorTestImpl(bytes, "The incoming-window field cannot be omitted from the Flow");
}
@Test
public void testTruncatedFlowProvokesDecodeError2() throws Exception {
// Provide the bytes for Flow, but only give a next-incoming-id and incoming-window and next-outgoing-id. Provokes a decode error as there must be 4 fields.
byte[] bytes = new byte[] {
0x00, 0x00, 0x00, 0x11, // Frame size = 17 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x13, (byte) 0xC0, // Described-type, ulong type, Flow descriptor, list8.
0x05, 0x03, 0x43, 0x43, 0x43 }; // size (5), count (3), next-incoming-id (0), incoming-window (uint0), next-outgoing-id (uint0).
doInvalidFlowProvokesDecodeErrorTestImpl(bytes, "The outgoing-window field cannot be omitted from the Flow");
}
@Test
public void testTruncatedFlowProvokesDecodeError3() throws Exception {
// Provide the bytes for Flow, but only give a next-incoming-id and incoming-window and next-outgoing-id, and send not-present/null for outgoing-window. Provokes a decode error as must be present.
byte[] bytes = new byte[] {
0x00, 0x00, 0x00, 0x12, // Frame size = 18 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x13, (byte) 0xC0, // Described-type, ulong type, Flow descriptor, list8.
0x06, 0x04, 0x43, 0x43, 0x43, 0x40 }; // size (5), count (4), next-incoming-id (0), incoming-window (uint0), next-outgoing-id (uint0), outgoing-window (null).
doInvalidFlowProvokesDecodeErrorTestImpl(bytes, "The outgoing-window field cannot be omitted from the Flow");
}
private void doInvalidFlowProvokesDecodeErrorTestImpl(byte[] bytes, String errorDescription) throws Exception {
Engine engine = EngineFactory.PROTON.createNonSaslEngine();
engine.errorHandler(result -> failure = result.failureCause());
ProtonTestConnector peer = createTestPeer(engine);
peer.expectAMQPHeader().respondWithAMQPHeader();
peer.expectOpen().respond();
peer.expectBegin().respond();
peer.remoteBytes().withBytes(bytes).queue(); // Queue the frame for write after expected setup
peer.expectClose().withError(AmqpError.DECODE_ERROR.toString(), errorDescription);
Connection connection = engine.start().open();
connection.session().open();
peer.waitForScriptToCompleteIgnoreErrors();
assertNotNull(failure);
assertTrue(failure instanceof FrameDecodingException);
assertEquals(errorDescription, failure.getMessage());
}
@Test
public void testEmptyTransferProvokesDecodeError() throws Exception {
// Provide the bytes for Transfer, but omit any fields to provoke a decode error.
byte[] bytes = new byte[] {
0x00, 0x00, 0x00, 0x0C, // Frame size = 12 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x14, 0x45};// Described-type, ulong type, Transfer descriptor, list0.
doInvalidTransferProvokesDecodeErrorTestImpl(bytes, "The handle field cannot be omitted");
}
@Test
public void testTruncatedTransferProvokesDecodeError() throws Exception {
// Provide the bytes for Transfer, but only give a null for the not-present handle. Provokes a decode error as there must be a handle.
byte[] bytes = new byte[] {
0x00, 0x00, 0x00, 0x0F, // Frame size = 15 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x14, (byte) 0xC0, // Described-type, ulong type, Transfer descriptor, list8.
0x03, 0x01, 0x40 }; // size (3), count (1), handle (null / not-present).
doInvalidTransferProvokesDecodeErrorTestImpl(bytes, "The handle field cannot be omitted from the Transfer");
}
@Test
public void testTransferWithWrongHandleTypeCodeProvokesDecodeError() throws Exception {
// Provide the bytes for Transfer, but give the wrong type code for a not-really-present handle. Provokes a decode error.
byte[] bytes = new byte[] {
0x00, 0x00, 0x00, 0x0F, // Frame size = 15 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x14, (byte) 0xC0, // Described-type, ulong type, Transfer descriptor, list8.
0x03, 0x01, (byte) 0xA3 }; // size (3), count (1), handle (invalid sym8 type constructor given, not really present).
doInvalidTransferProvokesDecodeErrorTestImpl(bytes, "Expected Unsigned Integer type but found encoding: SYM8:0xa3");
}
private void doInvalidTransferProvokesDecodeErrorTestImpl(byte[] bytes, String errorDescription) throws Exception {
Engine engine = EngineFactory.PROTON.createNonSaslEngine();
engine.errorHandler(result -> failure = result.failureCause());
ProtonTestConnector peer = createTestPeer(engine);
peer.expectAMQPHeader().respondWithAMQPHeader();
peer.expectOpen().respond();
peer.expectBegin().respond();
peer.expectAttach().respond();
peer.expectFlow().withLinkCredit(1);
peer.remoteBytes().withBytes(bytes).queue(); // Queue the frame for write after expected setup
peer.expectClose().withError(AmqpError.DECODE_ERROR.toString(), errorDescription);
Connection connection = engine.start().open();
Session session = connection.session().open();
Receiver receiver = session.receiver("test").open();
receiver.addCredit(1);
peer.waitForScriptToCompleteIgnoreErrors();
assertNotNull(failure);
assertTrue(failure instanceof FrameDecodingException);
assertEquals(errorDescription, failure.getMessage());
}
@Test
public void testEmptyDispositionProvokesDecodeError() throws Exception {
// Provide the bytes for Disposition, but omit any fields to provoke a decode error.
byte[] bytes = new byte[] {
0x00, 0x00, 0x00, 0x0C, // Frame size = 12 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x15, 0x45};// Described-type, ulong type, Disposition descriptor, list0.
doInvalidDispositionProvokesDecodeErrorTestImpl(bytes, "The role field cannot be omitted from the Disposition");
}
@Test
public void testTruncatedDispositionProvokesDecodeError() throws Exception {
// Provide the bytes for Disposition, but only give a null/not-present for the 'first' field. Provokes a decode error as there must be a role and 'first'.
byte[] bytes = new byte[] {
0x00, 0x00, 0x00, 0x10, // Frame size = 16 bytes.
0x02, 0x00, 0x00, 0x00, // DOFF, TYPE, 2x CHANNEL
0x00, 0x53, 0x15, (byte) 0xC0, // Described-type, ulong type, Disposition descriptor, list8.
0x04, 0x02, 0x41, 0x40 }; // size (4), count (2), role (receiver - the peers perspective), first ( null / not-present)
doInvalidDispositionProvokesDecodeErrorTestImpl(bytes, "The first field cannot be omitted from the Disposition");
}
private void doInvalidDispositionProvokesDecodeErrorTestImpl(byte[] bytes, String errorDescription) throws Exception {
Engine engine = EngineFactory.PROTON.createNonSaslEngine();
engine.errorHandler(result -> failure = result.failureCause());
ProtonTestConnector peer = createTestPeer(engine);
ProtonBuffer payload = ProtonBufferAllocator.defaultAllocator().copy(new byte[] {0, 1, 2, 3, 4});
peer.expectAMQPHeader().respondWithAMQPHeader();
peer.expectOpen().respond();
peer.expectBegin().respond();
peer.expectAttach().respond();
peer.remoteFlow().withLinkCredit(10).queue();
peer.expectTransfer();
peer.remoteBytes().withBytes(bytes).queue(); // Queue the frame for write after expected setup
peer.expectClose().withError(AmqpError.DECODE_ERROR.toString(), errorDescription);
Connection connection = engine.start().open();
Session session = connection.session().open();
Sender sender = session.sender("test").open();
OutgoingDelivery delivery = sender.next();
delivery.writeBytes(payload.copy(true));
peer.waitForScriptToCompleteIgnoreErrors();
assertNotNull(failure);
assertTrue(failure instanceof FrameDecodingException);
assertEquals(errorDescription, failure.getMessage());
}
}