com.datastax.driver.core.Frame Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dse-java-driver-core Show documentation
Show all versions of dse-java-driver-core Show documentation
A driver for DataStax Enterprise (DSE)
and Apache Cassandra 1.2+ clusters that works exclusively with the
Cassandra Query Language version 3 (CQL3) and Cassandra's binary protocol,
supporting DSE-specific features such as geospatial types, DSE Graph and DSE authentication.
/*
* Copyright DataStax, Inc.
*
* This software can be used solely with DataStax Enterprise. Please consult the license at
* http://www.datastax.com/terms/datastax-dse-driver-license-terms
*/
package com.datastax.driver.core;
import com.datastax.driver.core.exceptions.DriverInternalError;
import com.datastax.driver.core.exceptions.FrameTooLongException;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.handler.codec.TooLongFrameException;
import java.util.EnumSet;
import java.util.List;
/**
* A frame for the CQL binary protocol.
*
* Each frame contains a fixed size header (8 bytes for V1 and V2, 9 bytes for V3 and V4)
* followed by a variable size body. The content of the body depends on the header opcode value (the
* body can in particular be empty for some opcode values).
*
*
The protocol distinguishes 2 types of frames: requests and responses. Requests are those
* frames sent by the clients to the server, response are the ones sent by the server. Note however
* that the protocol supports server pushes (events) so responses does not necessarily come right
* after a client request.
*
*
Frames for protocol versions 1+2 are defined as:
*
*
*
*
* 0 8 16 24 32
* +---------+---------+---------+---------+
* | version | flags | stream | opcode |
* +---------+---------+---------+---------+
* | length |
* +---------+---------+---------+---------+
* | |
* . ... body ... .
* . .
* . .
* +---------------------------------------- *
*
*
* Frames for protocol versions 3+4 are defined as:
*
*
*
*
* 0 8 16 24 32 40
* +---------+---------+---------+---------+---------+
* | version | flags | stream | opcode |
* +---------+---------+---------+---------+---------+
* | length |
* +---------+---------+---------+---------+
* | |
* . ... body ... .
* . .
* . .
* +----------------------------------------
*
*
* @see "https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v1.spec"
* @see "https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v2.spec"
* @see "https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v3.spec"
* @see "https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec"
*/
class Frame {
final Header header;
final ByteBuf body;
private Frame(Header header, ByteBuf body) {
this.header = header;
this.body = body;
}
private static Frame create(ByteBuf fullFrame) {
assert fullFrame.readableBytes() >= 1
: String.format("Frame too short (%d bytes)", fullFrame.readableBytes());
int versionBytes = fullFrame.readByte();
// version first byte is the "direction" of the frame (request or response)
ProtocolVersion version = ProtocolVersion.fromInt(versionBytes & 0x7F);
int hdrLen = Header.lengthFor(version);
assert fullFrame.readableBytes() >= (hdrLen - 1)
: String.format("Frame too short (%d bytes)", fullFrame.readableBytes());
int flags = fullFrame.readByte();
int streamId = readStreamid(fullFrame, version);
int opcode = fullFrame.readByte();
int length = fullFrame.readInt();
assert length == fullFrame.readableBytes();
Header header = new Header(version, flags, streamId, opcode);
return new Frame(header, fullFrame);
}
private static int readStreamid(ByteBuf fullFrame, ProtocolVersion version) {
switch (version) {
case V1:
case V2:
return fullFrame.readByte();
case V3:
case V4:
case V5:
case DSE_V1:
case DSE_V2:
return fullFrame.readShort();
default:
throw version.unsupported();
}
}
static Frame create(
ProtocolVersion version, int opcode, int streamId, EnumSet flags, ByteBuf body) {
Header header = new Header(version, flags, streamId, opcode);
return new Frame(header, body);
}
static class Header {
final ProtocolVersion version;
final EnumSet flags;
final int streamId;
final int opcode;
private Header(ProtocolVersion version, int flags, int streamId, int opcode) {
this(version, Flag.deserialize(flags), streamId, opcode);
}
private Header(ProtocolVersion version, EnumSet flags, int streamId, int opcode) {
this.version = version;
this.flags = flags;
this.streamId = streamId;
this.opcode = opcode;
}
/**
* Return the expected frame header length in bytes according to the protocol version in use.
*
* @param version the protocol version in use
* @return the expected frame header length in bytes
*/
static int lengthFor(ProtocolVersion version) {
switch (version) {
case V1:
case V2:
return 8;
case V3:
case V4:
case V5:
case DSE_V1:
case DSE_V2:
return 9;
default:
throw version.unsupported();
}
}
enum Flag {
// The order of that enum matters!!
COMPRESSED,
TRACING,
CUSTOM_PAYLOAD,
WARNING,
USE_BETA;
static EnumSet deserialize(int flags) {
EnumSet set = EnumSet.noneOf(Flag.class);
Flag[] values = Flag.values();
for (int n = 0; n < 8; n++) {
if ((flags & (1 << n)) != 0) set.add(values[n]);
}
return set;
}
static int serialize(EnumSet flags) {
int i = 0;
for (Flag flag : flags) i |= 1 << flag.ordinal();
return i;
}
}
}
Frame with(ByteBuf newBody) {
return new Frame(header, newBody);
}
static final class Decoder extends ByteToMessageDecoder {
private DecoderForStreamIdSize decoder;
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List