@Override public void decode ( final IoSession session, final IoBuffer data, final ProtocolDecoderOutput output ) throws Exception { final short magic = data.getShort (); final byte version = data.get (); final int sequence = data.getInt (); final byte commandCode = data.get (); if ( magic != 1202 ) { throw new ProtocolCodecException ( String.format ( "Magic code should be 1202 but is %s", magic ) ); } if ( version != 1 ) { throw new ProtocolCodecException ( String.format ( "Version should be %s but is %s", 1, version ) ); } decodeMessage ( sequence, commandCode, data, output ); }
private Object decodeData ( final IoBuffer data ) throws ProtocolCodecException { data.order ( ByteOrder.LITTLE_ENDIAN ); final byte dataType = data.get (); switch ( dataType ) { case 0: return null; case 1: return data.get () != 0x00; case 2: return data.getInt (); case 3: return data.getLong (); case 4: return data.getFloat (); default: throw new ProtocolCodecException ( String.format ( "Data type %02x is unknown", dataType ) ); } }
private boolean processWriteCommand ( final IoSession session, final IoBuffer data, final ProtocolDecoderOutput out ) throws ProtocolCodecException { final int len = messageLength ( data ); if ( len < 0 ) { return false; } final int registerNumber = data.getUnsignedShort (); final int operationId = data.getInt (); final Variant value = decodeVariant ( session, data ); out.write ( new WriteCommand ( registerNumber, value, operationId ) ); return true; }
private boolean processWriteResult ( final IoSession session, final IoBuffer data, final ProtocolDecoderOutput out ) throws ProtocolCodecException { final int len = messageLength ( data ); if ( len < 0 ) { return false; } try { final int operationId = data.getInt (); final int errorCode = data.getUnsignedShort (); final String errorMessage = decodeString ( session, data ); out.write ( new WriteResult ( operationId, errorCode, errorMessage ) ); } catch ( final CharacterCodingException e ) { throw new ProtocolCodecException ( e ); } return true; }
private boolean processDataUpdate ( final IoSession session, final IoBuffer data, final ProtocolDecoderOutput out ) throws ProtocolCodecException { final int len = messageLength ( data ); if ( len < 0 ) { return false; } final int count = data.getUnsignedShort (); final List<DataUpdate.Entry> entries = new ArrayList<DataUpdate.Entry> ( count ); for ( int i = 0; i < count; i++ ) { entries.add ( decodeDataUpdateEntry ( data, session ) ); } out.write ( new DataUpdate ( entries ) ); return true; }
private boolean processBrowseAdded ( final IoSession session, final IoBuffer data, final ProtocolDecoderOutput out ) throws ProtocolCodecException { final int len = messageLength ( data ); if ( len < 0 ) { return false; } final int count = data.getUnsignedShort (); final List<BrowseAdded.Entry> entries = new ArrayList<BrowseAdded.Entry> ( count ); for ( int i = 0; i < count; i++ ) { entries.add ( decodeBrowserAddEntry ( data, session ) ); } out.write ( new BrowseAdded ( entries ) ); return true; }
private boolean processHello ( final IoSession session, final IoBuffer data, final ProtocolDecoderOutput out ) throws ProtocolCodecException { final int len = messageLength ( data ); if ( len < 0 ) { return false; } final byte version = data.get (); if ( version != 0x01 ) { throw new ProtocolCodecException ( String.format ( "Protocol version %s is unsupported", version ) ); } final short nodeId = data.getShort (); final EnumSet<Hello.Features> features = data.getEnumSetShort ( Hello.Features.class ); out.write ( new Hello ( nodeId, features ) ); return true; }
private void encodeBrowseUpdate ( final IoSession session, final Object message, final IoBuffer data ) throws ProtocolCodecException { // length data.putUnsignedShort ( ( (BrowseAdded)message ).getEntries ().size () ); final CharsetEncoder encoder = Sessions.getCharsetEncoder ( session ); // data for ( final BrowseAdded.Entry entry : ( (BrowseAdded)message ).getEntries () ) { data.putUnsignedShort ( entry.getRegister () ); data.put ( entry.getDataType ().getDataType () ); data.putEnumSet ( entry.getFlags () ); try { data.putPrefixedString ( entry.getName (), encoder ); data.putPrefixedString ( entry.getDescription (), encoder ); data.putPrefixedString ( entry.getUnit (), encoder ); } catch ( final CharacterCodingException e ) { throw new ProtocolCodecException ( e ); } } }
private void encodeProperties ( final IoBuffer data, final Map<String, String> properties ) throws ProtocolCodecException { final CharsetEncoder encoder = this.defaultCharset.newEncoder (); data.putUnsignedShort ( properties.size () ); for ( final Map.Entry<String, String> entry : properties.entrySet () ) { try { data.putPrefixedString ( entry.getKey (), encoder ); data.putPrefixedString ( entry.getValue (), encoder ); } catch ( final CharacterCodingException e ) { throw new ProtocolCodecException ( e ); } } }
private byte checkType ( final IoBuffer buffer, final byte expectedType, final boolean allowNull ) throws Exception { final byte type = buffer.get (); if ( allowNull && type == TYPE_NULL ) { return type; } if ( type != expectedType ) { if ( type == 0 && !allowNull ) { throw new ProtocolCodecException ( String.format ( "Failed to decode. Field is transmitted as null but defined as not-null." ) ); } throw new ProtocolCodecException ( String.format ( "Failed to decode string: Expected type %02x, found: %02x", expectedType, type ) ); } return type; }
public MessageDecoderResult decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws ProtocolCodecException { int messageCount = 0; while (parseMessage(in, out)) { messageCount++; } if (messageCount > 0) { // Mina will compact the buffer because we can't detect a header if (in.remaining() < HEADER_PATTERN.length) { position = 0; } return MessageDecoderResult.OK; } else { // Mina will compact the buffer position -= in.position(); return MessageDecoderResult.NEED_DATA; } }
/** * Utility to extract messages from a file. This method will return each * message found to a provided listener. The message file will also be * memory mapped rather than fully loaded into physical memory. Therefore, * a large message file can be processed without using excessive memory. * * @param file * @param listener * @throws IOException * @throws ProtocolCodecException */ public void extractMessages(File file, final MessageListener listener) throws IOException, ProtocolCodecException { // Set up a read-only memory-mapped file FileChannel readOnlyChannel = new RandomAccessFile(file, "r").getChannel(); MappedByteBuffer memoryMappedBuffer = readOnlyChannel.map(FileChannel.MapMode.READ_ONLY, 0, (int) readOnlyChannel.size()); decode(null, ByteBuffer.wrap(memoryMappedBuffer), new ProtocolDecoderOutput() { public void write(Object message) { listener.onMessage((String) message); } public void flush() { // ignored } }); }
/** * Read a leaf node of a given type and leaf type. * * @param type The node type. * @param leafType The type of value contained in the leaf: must be * one of the XdrCoding.TYPE_* values. * @return The node. * * @throws ProtocolCodecException if the node was not valid constant * node of the specified type. */ private Node leaf (int type, int leafType) throws ProtocolCodecException { switch (type) { case NAME: assertLeafType (leafType, TYPE_STRING); return new Field (getString (in)); case CONST_STRING: assertLeafType (leafType, TYPE_STRING); return new Const (getString (in)); case CONST_INT32: assertLeafType (leafType, TYPE_INT32); return new Const (in.getInt ()); case CONST_INT64: assertLeafType (leafType, TYPE_INT64); return new Const (in.getLong ()); case CONST_REAL64: assertLeafType (leafType, TYPE_REAL64); return new Const (in.getDouble ()); default: throw new Error (); } }
/** * Encode an AST in Elvin XDR format. * * @param out The buffer to encode to. * @param node The root of the AST. * * @see #decodeAST(ByteBuffer) */ public static void encodeAST (ByteBuffer out, Node node) throws ProtocolCodecException { if (node instanceof Const) { encodeConst (out, (Const)node); } else if (node instanceof Field) { out.putInt (NAME); out.putInt (TYPE_STRING); putString (out, ((Field)node).fieldName ()); } else { out.putInt (typeCodeFor (node)); out.putInt (0); // composite node base type is 0 Collection<Node> children = node.children (); out.putInt (children.size ()); for (Node child : children) encodeAST (out, child); } }
/** * Read a length-delimited 4-byte-aligned UTF-8 string. */ public static String getString (ByteBuffer in) throws BufferUnderflowException, ProtocolCodecException { try { int length = getPositiveInt (in); if (length == 0) { return ""; } else { String string = in.getString (length, UTF8_DECODER.get ()); in.skip (paddingFor (length)); return string; } } catch (CharacterCodingException ex) { throw new ProtocolCodecException ("Invalid UTF-8 string", ex); } }
/** * Read a name/value set. */ public static Map<String, Object> getNameValues (ByteBuffer in) throws ProtocolCodecException { int pairs = getPositiveInt (in); if (pairs == 0) return emptyMap (); HashMap<String, Object> nameValues = new HashMap<String, Object> (); for ( ; pairs > 0; pairs--) nameValues.put (getString (in), getObject (in)); return nameValues; }
/** * Read an object in type_id/value format. */ public static Object getObject (ByteBuffer in) throws ProtocolCodecException { int type = in.getInt (); switch (type) { case TYPE_INT32: return in.getInt (); case TYPE_INT64: return in.getLong (); case TYPE_REAL64: return in.getDouble (); case TYPE_STRING: return getString (in); case TYPE_OPAQUE: return getBytes (in); default: throw new ProtocolCodecException ("Unknown type code: " + type); } }
/** {@inheritDoc} */ public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws ProtocolCodecException { final ProtocolState state = (ProtocolState) session.getAttribute(ProtocolState.SESSION_KEY); // pass the connection to the encoder for its use encoder.setConnection((RTMPConnection) session.getAttribute(RTMPConnection.RTMP_CONNECTION_KEY)); try { // We need to synchronize on the output and flush the generated data to prevent two packages to the same channel // to be sent in different order thus resulting in wrong headers being generated. final IoBuffer buf = encoder.encode(state, message); if (buf != null) { out.write(buf); out.mergeAll(); out.flush(); } else { log.trace("Response buffer was null after encoding"); } } catch (Exception ex) { log.error("Exception during encode", ex); } }
/** * Decode a PDU as a slave * * @param message * the message PDU * @return the decoded messages * @throws IllegalStateException * if the function code is not supported */ public static Object decodeAsSlave ( final Pdu message ) throws ProtocolCodecException { final IoBuffer data = message.getData (); final byte functionCode = data.get (); switch ( functionCode ) { case Constants.FUNCTION_CODE_READ_COILS: case Constants.FUNCTION_CODE_READ_DISCRETE_INPUTS: case Constants.FUNCTION_CODE_READ_HOLDING_REGISTERS: case Constants.FUNCTION_CODE_READ_INPUT_REGISTERS: return new ReadRequest ( message.getTransactionId (), message.getUnitIdentifier (), functionCode, data.getUnsignedShort (), data.getUnsignedShort () ); case Constants.FUNCTION_CODE_WRITE_SINGLE_COIL: case Constants.FUNCTION_CODE_WRITE_SINGLE_REGISTER: return new WriteSingleDataRequest ( message.getTransactionId (), message.getUnitIdentifier (), functionCode, data.getUnsignedShort (), readBytes ( data, 2 ) ); case Constants.FUNCTION_CODE_WRITE_MULTIPLE_COILS: case Constants.FUNCTION_CODE_WRITE_MULTIPLE_REGISTERS: final int startAddress = data.getUnsignedShort (); final int numRegisters = data.getUnsignedShort (); /* number of registers */ final byte num = data.get (); if ( data.remaining () != num ) { throw new ProtocolCodecException ( String.format ( "Wrong amount of data. Announced %s (bytes), found %s (bytes)", num, data.remaining () ) ); } final byte[] b = new byte[data.remaining ()]; data.get ( b ); return new WriteMultiDataRequest ( message.getTransactionId (), message.getUnitIdentifier (), functionCode, startAddress, b, numRegisters ); default: throw new IllegalStateException ( String.format ( "Function code %02x is not supported", functionCode ) ); } }
private void decodeMessage ( final int sequence, final byte commandCode, final IoBuffer data, final ProtocolDecoderOutput output ) throws ProtocolCodecException { switch ( commandCode ) { case 3: decodeResponseConfiguration ( data, output, sequence ); break; case 5: decodeResponseData ( data, output, sequence ); break; } }
private void decodeResponseData ( final IoBuffer data, final ProtocolDecoderOutput output, final int sequence ) throws ProtocolCodecException { final byte nin = data.get (); final Object[] vars = new Object[nin]; for ( int i = 0; i < nin; i++ ) { vars[i] = decodeData ( data ); } final DataMessage msg = new DataMessage ( sequence, CommandCode.RESPOND_DATA, vars ); output.write ( msg ); }
/** * Decode a variant from the stream * * @return a decoded variant or <code>null</code> if the data type was * {@link DataType#DEAD} * @throws ProtocolCodecException */ private Variant decodeVariant ( final IoSession session, final IoBuffer data ) throws ProtocolCodecException { final byte b = data.get (); final DataType dataType = DataType.fromByte ( b ); if ( dataType == null ) { throw new ProtocolCodecException ( String.format ( "Data type %02x is unkown", b ) ); } switch ( dataType ) { case DEAD: return null; case NULL: return Variant.NULL; case BOOLEAN: return Variant.valueOf ( data.get () != 0x00 ); case INT32: return Variant.valueOf ( data.getInt () ); case INT64: return Variant.valueOf ( data.getInt () ); case DOUBLE: return Variant.valueOf ( data.getDouble () ); case STRING: try { return Variant.valueOf ( decodeString ( session, data ) ); } catch ( final CharacterCodingException e ) { throw new ProtocolCodecException ( e ); } default: throw new ProtocolCodecException ( String.format ( "Data type %s is unkown", b ) ); } }
private DataUpdate.Entry decodeDataUpdateEntry ( final IoBuffer data, final IoSession session ) throws ProtocolCodecException { final int register = data.getUnsignedShort (); final byte missedUpdates = data.get (); final long timestamp = data.getLong (); final Set<DataUpdate.State> states = data.getEnumSetShort ( DataUpdate.State.class ); final Variant value = decodeVariant ( session, data ); return new DataUpdate.Entry ( register, value, timestamp, states, missedUpdates ); }
private BrowseAdded.Entry decodeBrowserAddEntry ( final IoBuffer data, final IoSession session ) throws ProtocolCodecException { final short register = (short)data.getUnsignedShort (); // FIXME: validate if short works final byte b = data.get (); final DataType dataType = DataType.fromByte ( b ); if ( dataType == null ) { throw new ProtocolCodecException ( String.format ( "Data type %s is unkown", b ) ); } final Set<BrowseAdded.Entry.Flags> flags = data.getEnumSet ( BrowseAdded.Entry.Flags.class ); final CharsetDecoder decoder = Sessions.getCharsetDecoder ( session ); try { final String name = data.getPrefixedString ( decoder ); final String description = data.getPrefixedString ( decoder ); final String unit = data.getPrefixedString ( decoder ); return new BrowseAdded.Entry ( register, name, description, unit, dataType, flags ); } catch ( final CharacterCodingException e ) { throw new ProtocolCodecException ( e ); } }
private void encodeEntry ( final IoSession session, final IoBuffer data, final Entry entry ) throws ProtocolCodecException { data.putUnsignedShort ( entry.getRegister () ); data.put ( entry.getMissedUpdates () ); data.putLong ( entry.getTimestamp () ); data.putEnumSetShort ( entry.getStates () ); // put payload encodeVariant ( session, data, entry.getValue () ); }
private void handleError(ByteBuffer buffer, int recoveryPosition, String text, boolean disconnect) throws ProtocolCodecException { buffer.position(recoveryPosition); position = recoveryPosition; state = SEEKING_HEADER; text = appendDebugInformation(buffer, text); if (disconnect) { throw new CriticalProtocolCodecException(text); } else { log.error(text); } }
/** * Utility method to extract messages from files. This method loads all * extracted messages into memory so if the expected number of extracted * messages is large, do not use this method or your application may run * out of memory. Use the streaming version of the method instead. * * @param file * @return a list of extracted messages * @throws java.io.IOException * @throws ProtocolCodecException */ public List<String> extractMessages(File file) throws IOException, ProtocolCodecException { final List<String> messages = new ArrayList<String>(); extractMessages(file, new MessageListener() { public void onMessage(String message) { messages.add(message); } }); return messages; }
public void exceptionCaught(IoSession ioSession, Throwable cause) throws Exception { boolean disconnectNeeded = false; Session quickFixSession = findQFSession(ioSession); Throwable realCause = cause; if (cause instanceof ProtocolDecoderException && cause.getCause() != null) { realCause = cause.getCause(); } String reason; if (realCause instanceof IOException) { if (quickFixSession != null && quickFixSession.isEnabled()) { reason = "Socket exception (" + ioSession.getRemoteAddress() + "): " + cause; } else { reason = "Socket (" + ioSession.getRemoteAddress() + "): " + cause; } disconnectNeeded = true; } else if (realCause instanceof CriticalProtocolCodecException) { reason = "Critical protocol codec error: " + cause; disconnectNeeded = true; } else if (realCause instanceof ProtocolCodecException) { reason = "Protocol handler exception: " + cause; } else { reason = cause.toString(); } if (disconnectNeeded) { if (quickFixSession != null) { quickFixSession.disconnect(reason, true); } else { log.error(reason, cause); ioSession.close(); } } else { log.error(reason, cause); } }
@Override public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws ProtocolCodecException { // get the connection from the session String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID); log.trace("Session id: {}", sessionId); RTMPConnection conn = (RTMPConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId); if (conn != null) { Red5.setConnectionLocal(conn); final Semaphore lock = conn.getEncoderLock(); try { // acquire the encoder lock lock.acquire(); // get the buffer final IoBuffer buf = message instanceof IoBuffer ? (IoBuffer) message : getEncoder().encode(message); if (buf != null) { if (log.isTraceEnabled()) { log.trace("Writing output data: {}", Hex.encodeHexString(buf.array())); } out.write(buf); } else { log.trace("Response buffer was null after encoding"); } } catch (Exception ex) { log.error("Exception during encode", ex); } finally { lock.release(); Red5.setConnectionLocal(null); } } else { log.debug("Connection is no longer available for encoding, may have been closed already"); } }
@Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); putString (out, serverDomain); }
@Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); serverDomain = getString (in); }
@Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); encodeAST (out, incomingFilter); }
@Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); incomingFilter = decodeAST (in); }
@Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); out.putInt (versionMajor); out.putInt (versionMinor); putString (out, serverDomain); }
@Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); versionMajor = in.getInt (); versionMinor = in.getInt (); serverDomain = getString (in); }
@Override public void decode (ByteBuffer in) throws ProtocolCodecException { super.decode (in); routing = getStringArray (in); }
@Override public void encode (ByteBuffer out) throws ProtocolCodecException { super.encode (out); putStringArray (out, routing); }
@Override protected Message newMessage (int messageType, int frameSize) throws ProtocolCodecException { switch (messageType) { case Nack.ID: return new Nack (); case Disconn.ID: return new Disconn (); case Ack.ID: return new Ack (); case FedConnRply.ID: return new FedConnRply (); case FedConnRqst.ID: return new FedConnRqst (); case FedSubReplace.ID: return new FedSubReplace (); case FedNotify.ID: return new FedNotify (); case TestConn.ID: return TestConn.INSTANCE; case ConfConn.ID: return ConfConn.INSTANCE; case DropWarn.ID: return new DropWarn (); default: throw new ProtocolCodecException ("Unknown message type: ID = " + messageType); } }
/** * Assert that a single child is found and return this, otherwise * throw an exception. Used as a predicate for single-child nodes. */ private XdrAstParser single () throws ProtocolCodecException { int count = in.getInt (); if (count == 1) return this; else throw new ProtocolCodecException ("Expected single child, found " + count); }
/** * Assert that two children are found and return this, otherwise * throw an exception. Used as a predicate for binary nodes. */ private XdrAstParser binary () throws ProtocolCodecException { int count = in.getInt (); if (count == 2) return this; else throw new ProtocolCodecException ("Expected two children, found " + count); }