@Override public IQ handleIQRequest(IQ iqRequest) { Data data = (Data) iqRequest; InBandBytestreamSession ibbSession = this.manager.getSessions().get( data.getDataPacketExtension().getSessionID()); try { if (ibbSession == null) { this.manager.replyItemNotFoundPacket(data); } else { ibbSession.processIQPacket(data); } } catch (NotConnectedException e) { return null; } return null; }
public boolean accept(Stanza packet) { // sender equals remote peer if (!packet.getFrom().equalsIgnoreCase(remoteJID)) { return false; } DataPacketExtension data; if (packet instanceof Data) { data = ((Data) packet).getDataPacketExtension(); } else { // stanza contains data packet extension data = packet.getExtension( DataPacketExtension.ELEMENT, DataPacketExtension.NAMESPACE); if (data == null) { return false; } } // session ID equals this session ID if (!data.getSessionID().equals(byteStreamRequest.getSessionID())) { return false; } return true; }
@Override protected synchronized void writeToXML(DataPacketExtension data) throws IOException { // create IQ stanza containing data packet IQ iq = new Data(data); iq.setTo(remoteJID); try { connection.createPacketCollectorAndSend(iq).nextResultOrThrow(); } catch (Exception e) { // close session unless it is already closed if (!this.isClosed) { InBandBytestreamSession.this.close(); // Sadly we are unable to use the IOException(Throwable) constructor because this // constructor is only supported from Android API 9 on. IOException ioException = new IOException(); ioException.initCause(e); throw ioException; } } }
/** * Valid data packets should be confirmed. * * @throws Exception should not happen */ @Test public void shouldConfirmReceivedDataPacket() throws Exception { // verify data packet confirmation is of type RESULT protocol.addResponse(null, Verification.requestTypeRESULT); InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); InputStream inputStream = session.getInputStream(); StanzaListener listener = Whitebox.getInternalState(inputStream, StanzaListener.class); String base64Data = Base64.encode("Data"); DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); Data data = new Data(dpe); listener.processPacket(data); protocol.verifyAll(); }
@Override protected synchronized void writeToXML(DataPacketExtension data) throws IOException { // create IQ stanza containing data packet IQ iq = new Data(data); iq.setTo(remoteJID); try { SyncPacketSend.getReply(connection, iq); } catch (XMPPException e) { // close session unless it is already closed if (!this.isClosed) { InBandBytestreamSession.this.close(); throw new IOException("Error while sending Data: " + e.getMessage()); } } }
@Override public Data parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException, SmackException { DataPacketExtension data = packetExtensionProvider.parse(parser); Data iq = new Data(data); return iq; }
protected StanzaFilter getDataPacketFilter() { /* * filter all IQ stanzas having type 'SET' (represented by Data class), containing a * data stanza(/packet) extension, matching session ID and recipient */ return new AndFilter(new StanzaTypeFilter(Data.class), new IBBDataPacketFilter()); }
/** * If a data stanza(/packet) of an unknown session is received it should be replied * with an <item-not-found/> error. * * @throws Exception should not happen */ @Test public void shouldReplyErrorIfSessionIsUnknown() throws Exception { // mock connection XMPPConnection connection = mock(XMPPConnection.class); // initialize InBandBytestreamManager to get the DataListener InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); // get the DataListener from InBandByteStreamManager DataListener dataListener = Whitebox.getInternalState(byteStreamManager, DataListener.class); DataPacketExtension dpe = new DataPacketExtension("unknownSessionID", 0, "Data"); Data data = new Data(dpe); data.setFrom(initiatorJID); data.setTo(targetJID); dataListener.handleIQRequest(data); // wait because packet is processed in an extra thread Thread.sleep(200); // capture reply to the In-Band Bytestream close request ArgumentCaptor<IQ> argument = ArgumentCaptor.forClass(IQ.class); verify(connection).sendStanza(argument.capture()); // assert that reply is the correct error packet assertEquals(initiatorJID, argument.getValue().getTo()); assertEquals(IQ.Type.error, argument.getValue().getType()); assertEquals(XMPPError.Condition.item_not_found, argument.getValue().getError().getCondition()); }
/** * Initialize fields used in the tests. * @throws XMPPException * @throws SmackException */ @Before public void setup() throws XMPPException, SmackException { // build protocol verifier protocol = new Protocol(); // create mocked XMPP connection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, xmppServer); // initialize InBandBytestreamManager to get the InitiationListener byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); // create a In-Band Bytestream open packet initBytestream = new Open(sessionID, blockSize); initBytestream.setFrom(initiatorJID); initBytestream.setTo(targetJID); incrementingSequence = new Verification<Data, IQ>() { long lastSeq = 0; public void verify(Data request, IQ response) { assertEquals(lastSeq++, request.getDataPacketExtension().getSeq()); } }; }
/** * Test that the data is correctly chunked. * * @throws Exception should not happen */ @Test public void shouldSendDataCorrectly() throws Exception { // create random data Random rand = new Random(); final byte[] controlData = new byte[256 * blockSize]; rand.nextBytes(controlData); // compares the data of each packet with the control data Verification<Data, IQ> dataVerification = new Verification<Data, IQ>() { public void verify(Data request, IQ response) { byte[] decodedData = request.getDataPacketExtension().getDecodedData(); int seq = (int) request.getDataPacketExtension().getSeq(); for (int i = 0; i < decodedData.length; i++) { assertEquals(controlData[(seq * blockSize) + i], decodedData[i]); } } }; // set acknowledgments for the data packets IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); for (int i = 0; i < controlData.length / blockSize; i++) { protocol.addResponse(resultIQ, incrementingSequence, dataVerification); } InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); OutputStream outputStream = session.getOutputStream(); outputStream.write(controlData); outputStream.flush(); protocol.verifyAll(); }
/** * If the input stream is closed the output stream should not be closed as well. * * @throws Exception should not happen */ @Test public void shouldNotCloseBothStreamsIfOutputStreamIsClosed() throws Exception { InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); OutputStream outputStream = session.getOutputStream(); outputStream.close(); // verify data packet confirmation is of type RESULT protocol.addResponse(null, Verification.requestTypeRESULT); // insert data to read InputStream inputStream = session.getInputStream(); StanzaListener listener = Whitebox.getInternalState(inputStream, StanzaListener.class); String base64Data = Base64.encode("Data"); DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); Data data = new Data(dpe); listener.processPacket(data); // verify no packet send protocol.verifyAll(); try { outputStream.flush(); fail("should throw an exception"); } catch (IOException e) { assertTrue(e.getMessage().contains("closed")); } assertTrue(inputStream.read() != 0); }
/** * If the data stanza(/packet) has a sequence that is already used an 'unexpected-request' error should * be returned. See XEP-0047 Section 2.2. * * @throws Exception should not happen */ @Test public void shouldReplyWithErrorIfAlreadyUsedSequenceIsReceived() throws Exception { // verify reply to first valid data packet is of type RESULT protocol.addResponse(null, Verification.requestTypeRESULT); // verify reply to invalid data packet is an error protocol.addResponse(null, Verification.requestTypeERROR, new Verification<IQ, IQ>() { public void verify(IQ request, IQ response) { assertEquals(XMPPError.Condition.unexpected_request, request.getError().getCondition()); } }); // get IBB sessions data packet listener InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); InputStream inputStream = session.getInputStream(); StanzaListener listener = Whitebox.getInternalState(inputStream, StanzaListener.class); // build data packets String base64Data = Base64.encode("Data"); DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); Data data1 = new Data(dpe); Data data2 = new Data(dpe); // notify listener listener.processPacket(data1); listener.processPacket(data2); protocol.verifyAll(); }
/** * If the data stanza(/packet) contains invalid Base64 encoding an 'bad-request' error should be * returned. See XEP-0047 Section 2.2. * * @throws Exception should not happen */ @Test public void shouldReplyWithErrorIfDataIsInvalid() throws Exception { // verify reply to invalid data packet is an error protocol.addResponse(null, Verification.requestTypeERROR, new Verification<IQ, IQ>() { public void verify(IQ request, IQ response) { assertEquals(XMPPError.Condition.bad_request, request.getError().getCondition()); } }); // get IBB sessions data packet listener InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); InputStream inputStream = session.getInputStream(); StanzaListener listener = Whitebox.getInternalState(inputStream, StanzaListener.class); // build data packets DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, "AA=BB"); Data data = new Data(dpe); // notify listener listener.processPacket(data); protocol.verifyAll(); }
/** * If a data stanza(/packet) is received out of order the session should be closed. See XEP-0047 Section * 2.2. * * @throws Exception should not happen */ @Test public void shouldSendCloseRequestIfInvalidSequenceReceived() throws Exception { IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); // confirm data packet with invalid sequence protocol.addResponse(resultIQ); // confirm close request protocol.addResponse(resultIQ, Verification.requestTypeSET, Verification.correspondingSenderReceiver); // get IBB sessions data packet listener InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); InputStream inputStream = session.getInputStream(); StanzaListener listener = Whitebox.getInternalState(inputStream, StanzaListener.class); // build invalid packet with out of order sequence String base64Data = Base64.encode("Data"); DataPacketExtension dpe = new DataPacketExtension(sessionID, 123, base64Data); Data data = new Data(dpe); // add data packets listener.processPacket(data); // read until exception is thrown try { inputStream.read(); fail("exception should be thrown"); } catch (IOException e) { assertTrue(e.getMessage().contains("Packets out of sequence")); } protocol.verifyAll(); }
/** * Test the input stream read() method. * * @throws Exception should not happen */ @Test public void shouldReadAllReceivedData2() throws Exception { // create random data Random rand = new Random(); byte[] controlData = new byte[3 * blockSize]; rand.nextBytes(controlData); IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); // get IBB sessions data packet listener InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); InputStream inputStream = session.getInputStream(); StanzaListener listener = Whitebox.getInternalState(inputStream, StanzaListener.class); // set data packet acknowledgment and notify listener for (int i = 0; i < controlData.length / blockSize; i++) { protocol.addResponse(resultIQ); String base64Data = Base64.encodeToString(controlData, i * blockSize, blockSize); DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); Data data = new Data(dpe); listener.processPacket(data); } // read data byte[] bytes = new byte[3 * blockSize]; for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte) inputStream.read(); } // verify data for (int i = 0; i < bytes.length; i++) { assertEquals(controlData[i], bytes[i]); } protocol.verifyAll(); }
/** * If the output stream is closed the input stream should not be closed as well. * * @throws Exception should not happen */ @Test public void shouldNotCloseBothStreamsIfInputStreamIsClosed() throws Exception { // acknowledgment for data packet IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); protocol.addResponse(resultIQ); // get IBB sessions data packet listener InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); InputStream inputStream = session.getInputStream(); StanzaListener listener = Whitebox.getInternalState(inputStream, StanzaListener.class); // build data packet String base64Data = Base64.encode("Data"); DataPacketExtension dpe = new DataPacketExtension(sessionID, 0, base64Data); Data data = new Data(dpe); // add data packets listener.processPacket(data); inputStream.close(); protocol.verifyAll(); try { while (inputStream.read() != -1) { } inputStream.read(); fail("should throw an exception"); } catch (IOException e) { assertTrue(e.getMessage().contains("closed")); } session.getOutputStream().flush(); }
public void processPacket(Packet packet) { Data data = (Data) packet; InBandBytestreamSession ibbSession = this.manager.getSessions().get( data.getDataPacketExtension().getSessionID()); if (ibbSession == null) { this.manager.replyItemNotFoundPacket(data); } }
protected PacketFilter getDataPacketFilter() { /* * filter all IQ stanzas having type 'SET' (represented by Data class), containing a * data packet extension, matching session ID and recipient */ return new AndFilter(new PacketTypeFilter(Data.class), new IBBDataPacketFilter()); }
protected PacketFilter getDataPacketFilter() { /* * filter all IQ stanzas having type 'SET' (represented by Data * class), containing a data packet extension, matching session ID * and recipient */ return new AndFilter(new PacketTypeFilter(Data.class), new IBBDataPacketFilter()); }
protected StanzaListener getDataPacketListener() { return new StanzaListener() { private long lastSequence = -1; public void processPacket(Stanza packet) throws NotConnectedException { // get data packet extension DataPacketExtension data = ((Data) packet).getDataPacketExtension(); /* * check if sequence was not used already (see XEP-0047 Section 2.2) */ if (data.getSeq() <= this.lastSequence) { IQ unexpectedRequest = IQ.createErrorResponse((IQ) packet, new XMPPError( XMPPError.Condition.unexpected_request)); connection.sendStanza(unexpectedRequest); return; } // check if encoded data is valid (see XEP-0047 Section 2.2) if (data.getDecodedData() == null) { // data is invalid; respond with bad-request error IQ badRequest = IQ.createErrorResponse((IQ) packet, new XMPPError( XMPPError.Condition.bad_request)); connection.sendStanza(badRequest); return; } // data is valid; add to data queue dataQueue.offer(data); // confirm IQ IQ confirmData = IQ.createResultIQ((IQ) packet); connection.sendStanza(confirmData); // set last seen sequence this.lastSequence = data.getSeq(); if (this.lastSequence == 65535) { this.lastSequence = -1; } } }; }
/** * Test the input stream read(byte[], int, int) method. * * @throws Exception should not happen */ @Test public void shouldReadAllReceivedData1() throws Exception { // create random data Random rand = new Random(); byte[] controlData = new byte[3 * blockSize]; rand.nextBytes(controlData); IQ resultIQ = IBBPacketUtils.createResultIQ(initiatorJID, targetJID); // get IBB sessions data packet listener InBandBytestreamSession session = new InBandBytestreamSession(connection, initBytestream, initiatorJID); InputStream inputStream = session.getInputStream(); StanzaListener listener = Whitebox.getInternalState(inputStream, StanzaListener.class); // set data packet acknowledgment and notify listener for (int i = 0; i < controlData.length / blockSize; i++) { protocol.addResponse(resultIQ); String base64Data = Base64.encodeToString(controlData, i * blockSize, blockSize); DataPacketExtension dpe = new DataPacketExtension(sessionID, i, base64Data); Data data = new Data(dpe); listener.processPacket(data); } byte[] bytes = new byte[3 * blockSize]; int read = 0; read = inputStream.read(bytes, 0, blockSize); assertEquals(blockSize, read); read = inputStream.read(bytes, 10, blockSize); assertEquals(blockSize, read); read = inputStream.read(bytes, 20, blockSize); assertEquals(blockSize, read); // verify data for (int i = 0; i < bytes.length; i++) { assertEquals(controlData[i], bytes[i]); } protocol.verifyAll(); }
public IQ parseIQ(XmlPullParser parser) throws Exception { DataPacketExtension data = (DataPacketExtension) parseExtension(parser); IQ iq = new Data(data); return iq; }
/** * @param data * @throws NotConnectedException */ public void processIQPacket(Data data) throws NotConnectedException { inputStream.dataPacketListener.processPacket(data); }