/* * Copyright (c) 2012-2015 Spotify AB * * Licensed 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 com.spotify.netty4.handler.codec.zmtp; import java.nio.ByteBuffer; import io.netty.buffer.ByteBuf; class ZMTP10WireFormat implements ZMTPWireFormat { private static final byte FINAL_FLAG = 0x0; private static final byte MORE_FLAG = 0x1; /** * Read the remote identity octets from a ZMTP/1.0 greeting. */ static ByteBuffer readIdentity(final ByteBuf buffer) throws ZMTPParsingException { final long length = readLength(buffer); if (length == -1) { return null; } final long identityLength = length - 1; if (identityLength < 0 || identityLength > 255) { throw new ZMTPParsingException("Bad remote identity length: " + length); } // skip the flags byte buffer.skipBytes(1); final byte[] identity = new byte[(int) identityLength]; buffer.readBytes(identity); return ByteBuffer.wrap(identity); } /** * Read a ZMTP/1.0 frame length. */ static long readLength(final ByteBuf in) { if (in.readableBytes() < 1) { return -1; } long size = in.readByte() & 0xFF; if (size == 0xFF) { if (in.readableBytes() < 8) { return -1; } size = in.readLong(); } return size; } /** * Write a ZMTP/1.0 frame length. * * @param length The length. * @param out Target buffer. */ static void writeLength(final long length, final ByteBuf out) { writeLength(out, length, length); } /** * Write a ZMTP/1.0 frame length. * * @param out Target buffer. * @param maxLength The maximum length of the field. * @param length The length. */ static void writeLength(final ByteBuf out, final long maxLength, final long length) { if (maxLength < 255) { out.writeByte((byte) length); } else { out.writeByte(0xFF); out.writeLong(length); } } /** * Write a ZMTP/1.0 greeting. * * @param out Target buffer. * @param identity Socket identity. */ static void writeGreeting(final ByteBuf out, final ByteBuffer identity) { writeLength(identity.remaining() + 1, out); out.writeByte(0x00); out.writeBytes(identity.duplicate()); } @Override public Header header() { return new ZMTP10Header(); } @Override public int frameLength(final int content) { if (content + 1 < 255) { return 1 + 1 + content; } else { return 1 + 8 + 1 + content; } } static class ZMTP10Header implements Header { int maxLength; int length; boolean more; @Override public void set(final int maxLength, final int length, final boolean more) { this.maxLength = maxLength; this.length = length; this.more = more; } @Override public void write(final ByteBuf out) { writeLength(out, maxLength + 1, length + 1); out.writeByte(more ? MORE_FLAG : FINAL_FLAG); } @Override public boolean read(final ByteBuf in) throws ZMTPParsingException { final long len = readLength(in); if (len == -1) { // Wait for more data return false; } if (len == 0) { throw new ZMTPParsingException("Received frame with zero length"); } if (in.readableBytes() < 1) { // Wait for more data return false; } length = (int) len - 1; more = (in.readByte() & MORE_FLAG) == MORE_FLAG; return true; } @Override public long length() { return length; } @Override public boolean more() { return more; } } }