copy wangle back into folly
[folly.git] / folly / wangle / codec / LineBasedFrameDecoder.cpp
1 /*
2  * Copyright 2015 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <folly/wangle/codec/LineBasedFrameDecoder.h>
17
18 namespace folly { namespace wangle {
19
20 using folly::io::Cursor;
21
22 LineBasedFrameDecoder::LineBasedFrameDecoder(uint32_t maxLength,
23                                              bool stripDelimiter,
24                                              TerminatorType terminatorType)
25     : maxLength_(maxLength)
26     , stripDelimiter_(stripDelimiter)
27     , terminatorType_(terminatorType) {}
28
29 std::unique_ptr<IOBuf> LineBasedFrameDecoder::decode(
30   Context* ctx, IOBufQueue& buf, size_t&) {
31   int64_t eol = findEndOfLine(buf);
32
33   if (!discarding_) {
34     if (eol >= 0) {
35       Cursor c(buf.front());
36       c += eol;
37       auto delimLength = c.read<char>() == '\r' ? 2 : 1;
38       if (eol > maxLength_) {
39         buf.split(eol + delimLength);
40         fail(ctx, folly::to<std::string>(eol));
41         return nullptr;
42       }
43
44       std::unique_ptr<folly::IOBuf> frame;
45
46       if (stripDelimiter_) {
47         frame = buf.split(eol);
48         buf.trimStart(delimLength);
49       } else {
50         frame = buf.split(eol + delimLength);
51       }
52
53       return std::move(frame);
54     } else {
55       auto len = buf.chainLength();
56       if (len > maxLength_) {
57         discardedBytes_ = len;
58         buf.trimStart(len);
59         discarding_ = true;
60         fail(ctx, "over " + folly::to<std::string>(len));
61       }
62       return nullptr;
63     }
64   } else {
65     if (eol >= 0) {
66       Cursor c(buf.front());
67       c += eol;
68       auto delimLength = c.read<char>() == '\r' ? 2 : 1;
69       buf.trimStart(eol + delimLength);
70       discardedBytes_ = 0;
71       discarding_ = false;
72     } else {
73       discardedBytes_ = buf.chainLength();
74       buf.move();
75     }
76
77     return nullptr;
78   }
79 }
80
81 void LineBasedFrameDecoder::fail(Context* ctx, std::string len) {
82   ctx->fireReadException(
83     folly::make_exception_wrapper<std::runtime_error>(
84       "frame length" + len +
85       " exeeds max " + folly::to<std::string>(maxLength_)));
86 }
87
88 int64_t LineBasedFrameDecoder::findEndOfLine(IOBufQueue& buf) {
89   Cursor c(buf.front());
90   for (uint32_t i = 0; i < maxLength_ && i < buf.chainLength(); i++) {
91     auto b = c.read<char>();
92     if (b == '\n' && terminatorType_ != TerminatorType::CARRIAGENEWLINE) {
93       return i;
94     } else if (terminatorType_ != TerminatorType::NEWLINE &&
95                b == '\r' && !c.isAtEnd() && c.read<char>() == '\n') {
96       return i;
97     }
98   }
99
100   return -1;
101 }
102
103 }} // namespace