proxygen
wangle::LengthFieldBasedFrameDecoder Class Reference

#include <LengthFieldBasedFrameDecoder.h>

Inheritance diagram for wangle::LengthFieldBasedFrameDecoder:
wangle::ByteToMessageDecoder< M > wangle::InboundHandler< folly::IOBufQueue &, M > wangle::HandlerBase< InboundHandlerContext< M > >

Public Member Functions

 LengthFieldBasedFrameDecoder (uint32_t lengthFieldLength=4, uint32_t maxFrameLength=UINT_MAX, uint32_t lengthFieldOffset=0, int32_t lengthAdjustment=0, uint32_t initialBytesToStrip=4, bool networkByteOrder=true)
 
bool decode (Context *ctx, folly::IOBufQueue &buf, std::unique_ptr< folly::IOBuf > &result, size_t &) override
 
- Public Member Functions inherited from wangle::ByteToMessageDecoder< M >
virtual bool decode (Context *ctx, folly::IOBufQueue &buf, M &result, size_t &)=0
 
void transportActive (Context *ctx) override
 
void transportInactive (Context *ctx) override
 
void read (Context *ctx, folly::IOBufQueue &q) override
 
- Public Member Functions inherited from wangle::InboundHandler< folly::IOBufQueue &, M >
 ~InboundHandler () override=default
 
virtual void read (Context *ctx, folly::IOBufQueue &msg)=0
 
virtual void readEOF (Context *ctx)
 
virtual void readException (Context *ctx, folly::exception_wrapper e)
 
virtual void transportActive (Context *ctx)
 
virtual void transportInactive (Context *ctx)
 
- Public Member Functions inherited from wangle::HandlerBase< InboundHandlerContext< M > >
virtual ~HandlerBase ()=default
 
virtual void attachPipeline (InboundHandlerContext< M > *)
 
virtual void detachPipeline (InboundHandlerContext< M > *)
 
InboundHandlerContext< M > * getContext ()
 

Private Member Functions

uint64_t getUnadjustedFrameLength (folly::IOBufQueue &buf, int offset, int length, bool networkByteOrder)
 

Private Attributes

uint32_t lengthFieldLength_
 
uint32_t maxFrameLength_
 
uint32_t lengthFieldOffset_
 
int32_t lengthAdjustment_
 
uint32_t initialBytesToStrip_
 
bool networkByteOrder_
 
uint32_t lengthFieldEndOffset_
 

Additional Inherited Members

- Public Types inherited from wangle::ByteToMessageDecoder< M >
typedef InboundHandler< folly::IOBufQueue &, M >::Context Context
 
- Public Types inherited from wangle::InboundHandler< folly::IOBufQueue &, M >
typedef folly::IOBufQueuerin
 
typedef M rout
 
typedef folly::Unit win
 
typedef folly::Unit wout
 
typedef InboundHandlerContext< MContext
 
- Static Public Attributes inherited from wangle::InboundHandler< folly::IOBufQueue &, M >
static const HandlerDir dir
 

Detailed Description

A decoder that splits the received IOBufs dynamically by the value of the length field in the message. It is particularly useful when you decode a binary message which has an integer header field that represents the length of the message body or the whole message.

LengthFieldBasedFrameDecoder has many configuration parameters so that it can decode any message with a length field, which is often seen in proprietary client-server protocols. Here are some example that will give you the basic idea on which option does what.

2 bytes length field at offset 0, do not strip header

The value of the length field in this example is 12 (0x0C) which represents the length of "HELLO, WORLD". By default, the decoder assumes that the length field represents the number of the bytes that follows the length field. Therefore, it can be decoded with the simplistic parameter combination.

lengthFieldOffset = 0 lengthFieldLength = 2 lengthAdjustment = 0 initialBytesToStrip = 0 (= do not strip header)

BEFORE DECODE (14 bytes) AFTER DECODE (14 bytes) +-----—+-------------—+ +-----—+-------------—+ | Length | Actual Content |--—>| Length | Actual Content | | 0x000C | "HELLO, WORLD" | | 0x000C | "HELLO, WORLD" | +-----—+-------------—+ +-----—+-------------—+

2 bytes length field at offset 0, strip header

Because we can get the length of the content by calling ioBuf->computeChainDataLength(), you might want to strip the length field by specifying initialBytesToStrip. In this example, we specified 2, that is same with the length of the length field, to strip the first two bytes.

lengthFieldOffset = 0 lengthFieldLength = 2 lengthAdjustment = 0 initialBytesToStrip = 2 (= the length of the Length field)

BEFORE DECODE (14 bytes) AFTER DECODE (12 bytes) +-----—+-------------—+ +-------------—+ | Length | Actual Content |--—>| Actual Content | | 0x000C | "HELLO, WORLD" | | "HELLO, WORLD" | +-----—+-------------—+ +-------------—+

2 bytes length field at offset 0, do not strip header, the length field represents the length of the whole message

In most cases, the length field represents the length of the message body only, as shown in the previous examples. However, in some protocols, the length field represents the length of the whole message, including the message header. In such a case, we specify a non-zero lengthAdjustment. Because the length value in this example message is always greater than the body length by 2, we specify -2 as lengthAdjustment for compensation.

lengthFieldOffset = 0 lengthFieldLength = 2 lengthAdjustment = -2 (= the length of the Length field) initialBytesToStrip = 0

BEFORE DECODE (14 bytes) AFTER DECODE (14 bytes) +-----—+-------------—+ +-----—+-------------—+ | Length | Actual Content |--—>| Length | Actual Content | | 0x000E | "HELLO, WORLD" | | 0x000E | "HELLO, WORLD" | +-----—+-------------—+ +-----—+-------------—+

3 bytes length field at the end of 5 bytes header, do not strip header

The following message is a simple variation of the first example. An extra header value is prepended to the message. lengthAdjustment is zero again because the decoder always takes the length of the prepended data into account during frame length calculation.

lengthFieldOffset = 2 (= the length of Header 1) lengthFieldLength = 3 lengthAdjustment = 0 initialBytesToStrip = 0

BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes) +-------—+-------—+-------------—+ +-------—+-------—+-------------—+ | Header 1 | Length | Actual Content |--—>| Header 1 | Length | Actual Content | | 0xCAFE | 0x00000C | "HELLO, WORLD" | | 0xCAFE | 0x00000C | "HELLO, WORLD" | +-------—+-------—+-------------—+ +-------—+-------—+-------------—+

3 bytes length field at the beginning of 5 bytes header, do not strip header

This is an advanced example that shows the case where there is an extra header between the length field and the message body. You have to specify a positive lengthAdjustment so that the decoder counts the extra header into the frame length calculation.

lengthFieldOffset = 0 lengthFieldLength = 3 lengthAdjustment = 2 (= the length of Header 1) initialBytesToStrip = 0

BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes) +-------—+-------—+-------------—+ +-------—+-------—+-------------—+ | Length | Header 1 | Actual Content |--—>| Length | Header 1 | Actual Content | | 0x00000C | 0xCAFE | "HELLO, WORLD" | | 0x00000C | 0xCAFE | "HELLO, WORLD" | +-------—+-------—+-------------—+ +-------—+-------—+-------------—+

2 bytes length field at offset 1 in the middle of 4 bytes header, strip the first header field and the length field

This is a combination of all the examples above. There are the prepended header before the length field and the extra header after the length field. The prepended header affects the lengthFieldOffset and the extra header affects the lengthAdjustment. We also specified a non-zero initialBytesToStrip to strip the length field and the prepended header from the frame. If you don't want to strip the prepended header, you could specify 0 for initialBytesToSkip.

lengthFieldOffset = 1 (= the length of HDR1) lengthFieldLength = 2 lengthAdjustment = 1 (= the length of HDR2) initialBytesToStrip = 3 (= the length of HDR1 + LEN)

BEFORE DECODE (16 bytes) AFTER DECODE (13 bytes) +---—+-----—+---—+-------------—+ +---—+-------------—+ | HDR1 | Length | HDR2 | Actual Content |--—>| HDR2 | Actual Content | | 0xCA | 0x000C | 0xFE | "HELLO, WORLD" | | 0xFE | "HELLO, WORLD" | +---—+-----—+---—+-------------—+ +---—+-------------—+

2 bytes length field at offset 1 in the middle of 4 bytes header, strip the first header field and the length field, the length field represents the length of the whole message

Let's give another twist to the previous example. The only difference from the previous example is that the length field represents the length of the whole message instead of the message body, just like the third example. We have to count the length of HDR1 and Length into lengthAdjustment. Please note that we don't need to take the length of HDR2 into account because the length field already includes the whole header length.

lengthFieldOffset = 1 lengthFieldLength = 2 lengthAdjustment = -3 (= the length of HDR1 + LEN, negative) initialBytesToStrip = 3

BEFORE DECODE (16 bytes) AFTER DECODE (13 bytes) +---—+-----—+---—+-------------—+ +---—+-------------—+ | HDR1 | Length | HDR2 | Actual Content |--—>| HDR2 | Actual Content | | 0xCA | 0x0010 | 0xFE | "HELLO, WORLD" | | 0xFE | "HELLO, WORLD" | +---—+-----—+---—+-------------—+ +---—+-------------—+

See also
LengthFieldPrepender

Definition at line 183 of file LengthFieldBasedFrameDecoder.h.

Constructor & Destructor Documentation

wangle::LengthFieldBasedFrameDecoder::LengthFieldBasedFrameDecoder ( uint32_t  lengthFieldLength = 4,
uint32_t  maxFrameLength = UINT_MAX,
uint32_t  lengthFieldOffset = 0,
int32_t  lengthAdjustment = 0,
uint32_t  initialBytesToStrip = 4,
bool  networkByteOrder = true 
)
explicit

Definition at line 24 of file LengthFieldBasedFrameDecoder.cpp.

31  : lengthFieldLength_(lengthFieldLength)
32  , maxFrameLength_(maxFrameLength)
33  , lengthFieldOffset_(lengthFieldOffset)
34  , lengthAdjustment_(lengthAdjustment)
35  , initialBytesToStrip_(initialBytesToStrip)
36  , networkByteOrder_(networkByteOrder)
37  , lengthFieldEndOffset_(lengthFieldOffset + lengthFieldLength) {
38  CHECK(maxFrameLength > 0);
39  CHECK(lengthFieldOffset <= maxFrameLength - lengthFieldLength);
40 }

Member Function Documentation

bool wangle::LengthFieldBasedFrameDecoder::decode ( Context ctx,
folly::IOBufQueue buf,
std::unique_ptr< folly::IOBuf > &  result,
size_t &   
)
override

Definition at line 42 of file LengthFieldBasedFrameDecoder.cpp.

References folly::IOBufQueue::chainLength(), getUnadjustedFrameLength(), initialBytesToStrip_, lengthAdjustment_, lengthFieldEndOffset_, lengthFieldLength_, lengthFieldOffset_, maxFrameLength_, networkByteOrder_, folly::IOBufQueue::split(), folly::IOBufQueue::trimStart(), folly::IOBufQueue::trimStartAtMost(), and uint64_t.

45  {
46  // discarding too long frame
47  if (buf.chainLength() < lengthFieldEndOffset_) {
48  return false;
49  }
50 
51  uint64_t frameLength = getUnadjustedFrameLength(
53 
55 
56  if (frameLength < lengthFieldEndOffset_) {
57  buf.trimStart(lengthFieldEndOffset_);
58  ctx->fireReadException(folly::make_exception_wrapper<std::runtime_error>(
59  "Frame too small"));
60  return false;
61  }
62 
63  if (frameLength > maxFrameLength_) {
64  buf.trimStartAtMost(frameLength);
65  ctx->fireReadException(folly::make_exception_wrapper<std::runtime_error>(
66  "Frame larger than " +
67  folly::to<std::string>(maxFrameLength_)));
68  return false;
69  }
70 
71  if (buf.chainLength() < frameLength) {
72  return false;
73  }
74 
75  if (initialBytesToStrip_ > frameLength) {
76  buf.trimStart(frameLength);
77  ctx->fireReadException(folly::make_exception_wrapper<std::runtime_error>(
78  "InitialBytesToSkip larger than frame"));
79  return false;
80  }
81 
83  int actualFrameLength = frameLength - initialBytesToStrip_;
84  result = buf.split(actualFrameLength);
85  return true;
86 }
std::unique_ptr< folly::IOBuf > split(size_t n)
Definition: IOBufQueue.h:420
size_t chainLength() const
Definition: IOBufQueue.h:492
size_t trimStartAtMost(size_t amount)
Definition: IOBufQueue.cpp:263
uint64_t getUnadjustedFrameLength(folly::IOBufQueue &buf, int offset, int length, bool networkByteOrder)
void trimStart(size_t amount)
Definition: IOBufQueue.cpp:255
uint64_t wangle::LengthFieldBasedFrameDecoder::getUnadjustedFrameLength ( folly::IOBufQueue buf,
int  offset,
int  length,
bool  networkByteOrder 
)
private

Definition at line 88 of file LengthFieldBasedFrameDecoder.cpp.

References c, folly::IOBufQueue::front(), uint16_t, uint32_t, uint64_t, and uint8_t.

Referenced by decode().

89  {
90  folly::io::Cursor c(buf.front());
91  uint64_t frameLength;
92 
93  c.skip(offset);
94 
95  switch(length) {
96  case 1:{
97  if (networkByteOrder) {
98  frameLength = c.readBE<uint8_t>();
99  } else {
100  frameLength = c.readLE<uint8_t>();
101  }
102  break;
103  }
104  case 2:{
105  if (networkByteOrder) {
106  frameLength = c.readBE<uint16_t>();
107  } else {
108  frameLength = c.readLE<uint16_t>();
109  }
110  break;
111  }
112  case 4:{
113  if (networkByteOrder) {
114  frameLength = c.readBE<uint32_t>();
115  } else {
116  frameLength = c.readLE<uint32_t>();
117  }
118  break;
119  }
120  case 8:{
121  if (networkByteOrder) {
122  frameLength = c.readBE<uint64_t>();
123  } else {
124  frameLength = c.readLE<uint64_t>();
125  }
126  break;
127  }
128  }
129 
130  return frameLength;
131 }
const folly::IOBuf * front() const
Definition: IOBufQueue.h:476
char c

Member Data Documentation

uint32_t wangle::LengthFieldBasedFrameDecoder::initialBytesToStrip_
private

Definition at line 206 of file LengthFieldBasedFrameDecoder.h.

Referenced by decode().

int32_t wangle::LengthFieldBasedFrameDecoder::lengthAdjustment_
private

Definition at line 205 of file LengthFieldBasedFrameDecoder.h.

Referenced by decode().

uint32_t wangle::LengthFieldBasedFrameDecoder::lengthFieldEndOffset_
private

Definition at line 209 of file LengthFieldBasedFrameDecoder.h.

Referenced by decode().

uint32_t wangle::LengthFieldBasedFrameDecoder::lengthFieldLength_
private

Definition at line 202 of file LengthFieldBasedFrameDecoder.h.

Referenced by decode().

uint32_t wangle::LengthFieldBasedFrameDecoder::lengthFieldOffset_
private

Definition at line 204 of file LengthFieldBasedFrameDecoder.h.

Referenced by decode().

uint32_t wangle::LengthFieldBasedFrameDecoder::maxFrameLength_
private

Definition at line 203 of file LengthFieldBasedFrameDecoder.h.

Referenced by decode().

bool wangle::LengthFieldBasedFrameDecoder::networkByteOrder_
private

Definition at line 207 of file LengthFieldBasedFrameDecoder.h.

Referenced by decode().


The documentation for this class was generated from the following files: