21 const string kDummyGet(
"GET / HTTP/1.0");
33 assert(offset <= buf.
length());
34 const IOBuf *crtBuf = &buf;
36 size_t crtLen = crtBuf->
length() - offset;
37 const uint8_t *crtData = crtBuf->data() + offset;
38 size_t cmplen =
std::min(crtLen, boundarylen);
39 if (memcmp(crtData, boundary, cmplen) == 0) {
40 if (cmplen == boundarylen) {
41 return BoundaryResult::YES;
45 boundarylen -= cmplen;
48 return BoundaryResult::NO;
51 crtBuf = crtBuf->next();
52 }
while (crtBuf != &buf);
54 return BoundaryResult::PARTIAL;
61 std::unique_ptr<IOBuf> RFC1867Codec::onIngress(std::unique_ptr<IOBuf>
data) {
62 static auto dummyBuf = IOBuf::wrapBuffer(kDummyGet.data(),
64 IOBufQueue result{IOBufQueue::cacheChainLength()};
65 bool foundBoundary =
false;
69 while (!input_.empty()) {
71 case ParserState::START:
73 br = isBoundary(*input_.front(), 0, boundary_.data() + 1,
74 boundary_.length() - 1);
75 if (br == BoundaryResult::NO) {
77 LOG(
ERROR) <<
"Invalid starting sequence";
80 state_ = ParserState::ERROR;
82 }
else if (br == BoundaryResult::PARTIAL) {
85 input_.trimStart(boundary_.length() - 1);
86 bytesProcessed_ += boundary_.length() - 1;
87 state_ = ParserState::HEADERS_START;
90 case ParserState::HEADERS_START:
92 if (input_.chainLength() < 3) {
100 if (memcmp(firstTwo,
"--", 2) == 0) {
102 auto ch =
c.read<
char>();
104 input_.trimStart(toTrim);
105 state_ = ParserState::DONE;
106 }
else if (
ch ==
'\r') {
112 if (input_.chainLength() < toTrim) {
113 return input_.move();
116 state_ = ParserState::ERROR;
118 }
while (state_ == ParserState::HEADERS_START);
122 headerParser_.setParserPaused(
false);
123 headerParser_.onIngress(*dummyBuf);
129 while (!parseError_ && input_.front() &&
131 size_t bytesParsed = headerParser_.onIngress(*input_.front());
132 input_.trimStart(bytesParsed);
133 bytesProcessed_ += bytesParsed;
137 LOG(
ERROR) <<
"Error parsing header data: ";
138 VLOG(3) << IOBufPrinter::printHexFolly(input_.front());
141 state_ = ParserState::ERROR;
146 case ParserState::FIELD_DATA:
147 result = readToBoundary(foundBoundary);
148 value_.append(result.move());
150 if (
callback_->onFieldData(value_.move(), bytesProcessed_) < 0) {
151 LOG(
ERROR) <<
"Callback returned error";
152 state_ = ParserState::ERROR;
158 callback_->onFieldEnd(
true, bytesProcessed_);
160 state_ = ParserState::HEADERS_START;
162 if (input_.chainLength() > 0) {
163 VLOG(5) <<
"Trailing input=" 164 << IOBufPrinter::printHexFolly(input_.front());
166 return input_.move();
169 case ParserState::DONE:
170 case ParserState::ERROR:
179 std::unique_ptr<HTTPMessage> msg) {
182 static const StringPiece kFormData(
"form-data", 9);
184 const auto& contentDisp =
188 HTTPMessage::splitNameValuePieces(
189 contentDisp,
';',
'=',
193 if (value.
size() >= 2 && value[0] ==
'\"' &&
194 value[value.
size() - 1] ==
'\"') {
197 if (parameter == kName) {
199 }
else if (parameter == kFilename) {
200 filename = value.
str();
201 }
else if (parameter != kFormData) {
202 LOG(WARNING) <<
"Ignoring parameter " << parameter <<
" value \"" 208 LOG(
ERROR) <<
"name empty";
211 state_ = ParserState::ERROR;
214 state_ = ParserState::FIELD_DATA;
217 bytesProcessed_) < 0) {
219 LOG(WARNING) <<
"Callback returned error";
220 state_ = ParserState::ERROR;
225 IOBufQueue RFC1867Codec::readToBoundary(
bool& foundBoundary) {
226 IOBufQueue result{IOBufQueue::cacheChainLength()};
229 while (!input_.empty() && boundaryResult != BoundaryResult::PARTIAL) {
230 const IOBuf* head = input_.front();
235 while (len > 0 && (ptr = (
const uint8_t*)memchr(ptr, boundary_[0], len))) {
238 len = head->
length() - readlen;
240 isBoundary(*head, readlen, boundary_.
data(), boundary_.length());
241 if (boundaryResult == BoundaryResult::YES) {
242 CHECK(readlen < head->length());
244 if (readlen == 0 && pendingCR_) {
258 result.append(input_.split(readlen));
259 uint32_t trimLen = boundary_.length() + (hasCr ? 1 : 0);
260 input_.trimStart(trimLen);
261 bytesProcessed_ += readlen + trimLen;
262 foundBoundary =
true;
264 }
else if (boundaryResult == BoundaryResult::PARTIAL) {
266 }
else if (pendingCR_) {
277 if ((boundaryResult == BoundaryResult::NO || resultLen > 0) &&
283 if (resultLen > 0 && head->
data()[resultLen - 1] ==
'\r') {
284 result.append(input_.split(resultLen - 1));
286 pendingCR_ = input_.split(1);
288 result.append(input_.split(resultLen));
290 bytesProcessed_ += resultLen;
294 foundBoundary =
false;
299 void RFC1867Codec::onIngressEOM() {
300 if (state_ == ParserState::FIELD_DATA) {
301 LOG(WARNING) <<
"Field not terminated by boundary";
303 callback_->onFieldEnd(
false, bytesProcessed_);
306 if (state_ != ParserState::HEADERS_START && state_ != ParserState::ERROR &&
307 state_ != ParserState::DONE) {
309 LOG(
ERROR) <<
"onIngressEOM with state_=" << (
uint8_t)state_;
313 state_ = ParserState::START;
void append(std::unique_ptr< folly::IOBuf > &&buf, bool pack=false)
constexpr detail::Map< Move > move
constexpr size_type size() const
const uint8_t * data() const
constexpr Iter data() const
std::size_t length() const
static const char *const value
folly::Function< void()> callback_
Range< const char * > StringPiece
static constexpr uint64_t data[1]
void reset(Iter start, size_type size)