proxygen
proxygen::RFC1867Codec Class Reference

#include <RFC1867.h>

Inheritance diagram for proxygen::RFC1867Codec:
proxygen::HTTPCodec::Callback

Classes

class  Callback
 

Public Member Functions

 RFC1867Codec (const std::string &boundary)
 
void setCallback (Callback *callback)
 
std::unique_ptr< folly::IOBufonIngress (std::unique_ptr< folly::IOBuf > data)
 
void onIngressEOM ()
 
uint64_t getBytesProcessed () const
 

Private Types

enum  ParserState {
  ParserState::START, ParserState::HEADERS_START, ParserState::HEADERS, ParserState::FIELD_DATA,
  ParserState::DONE, ParserState::ERROR
}
 

Private Member Functions

void onMessageBegin (HTTPCodec::StreamID, HTTPMessage *) override
 
void onHeadersComplete (HTTPCodec::StreamID stream, std::unique_ptr< HTTPMessage > msg) override
 
void onBody (HTTPCodec::StreamID, std::unique_ptr< folly::IOBuf >, uint16_t) override
 
void onTrailersComplete (HTTPCodec::StreamID, std::unique_ptr< HTTPHeaders >) override
 
void onMessageComplete (HTTPCodec::StreamID, bool) override
 
void onError (HTTPCodec::StreamID, const HTTPException &, bool) override
 
folly::IOBufQueue readToBoundary (bool &foundBoundary)
 
- Private Member Functions inherited from proxygen::HTTPCodec::Callback
virtual void onPushMessageBegin (StreamID, StreamID, HTTPMessage *)
 
virtual void onExMessageBegin (StreamID, StreamID, bool, HTTPMessage *)
 
virtual void onChunkHeader (StreamID, size_t)
 
virtual void onChunkComplete (StreamID)
 
virtual void onAbort (StreamID, ErrorCode)
 
virtual void onFrameHeader (StreamID, uint8_t, uint64_t, uint8_t, uint16_t=0)
 
virtual void onGoaway (uint64_t, ErrorCode, std::unique_ptr< folly::IOBuf >=nullptr)
 
virtual void onPingRequest (uint64_t)
 
virtual void onPingReply (uint64_t)
 
virtual void onWindowUpdate (StreamID, uint32_t)
 
virtual void onSettings (const SettingsList &)
 
virtual void onSettingsAck ()
 
virtual void onPriority (StreamID, const HTTPMessage::HTTPPriority &)
 
virtual bool onNativeProtocolUpgrade (StreamID, CodecProtocol, const std::string &, HTTPMessage &)
 
virtual void onGenerateFrameHeader (StreamID, uint8_t, uint64_t, uint16_t=0)
 
virtual void onCertificateRequest (uint16_t, std::unique_ptr< folly::IOBuf >)
 
virtual void onCertificate (uint16_t, std::unique_ptr< folly::IOBuf >)
 
virtual uint32_t numOutgoingStreams () const
 
virtual uint32_t numIncomingStreams () const
 
virtual ~Callback ()
 

Private Attributes

std::string boundary_
 
Callbackcallback_ {nullptr}
 
ParserState state_ {ParserState::START}
 
HTTP1xCodec headerParser_ {TransportDirection::DOWNSTREAM}
 
std::string field_
 
folly::IOBufQueue input_ {folly::IOBufQueue::cacheChainLength()}
 
folly::IOBufQueue value_
 
std::unique_ptr< folly::IOBufpendingCR_
 
uint64_t bytesProcessed_ {0}
 
bool parseError_ {false}
 

Detailed Description

Class for stream-parsing RFC 1867 style post data. At present it does not support nested multi-part content (multipart/mixed). Can parse multiple POST bodies unless one of them invokes the onError() callback. After that, the codec is no longer usable.

Definition at line 24 of file RFC1867.h.

Member Enumeration Documentation

Enumerator
START 
HEADERS_START 
HEADERS 
FIELD_DATA 
DONE 
ERROR 

Definition at line 71 of file RFC1867.h.

71  {
72  START,
73  HEADERS_START,
74  HEADERS,
75  FIELD_DATA, // part, or field, not only file
76  DONE,
77  ERROR
78  };

Constructor & Destructor Documentation

proxygen::RFC1867Codec::RFC1867Codec ( const std::string boundary)
inlineexplicit

Definition at line 48 of file RFC1867.h.

References boundary_, headerParser_, and proxygen::HTTP1xCodec::setCallback().

48  {
49  CHECK(!boundary.empty());
50  boundary_ = folly::to<std::string>("\n--", boundary);
52  }
std::string boundary_
Definition: RFC1867.h:110
void setCallback(Callback *callback) override
Definition: HTTP1xCodec.h:40
HTTP1xCodec headerParser_
Definition: RFC1867.h:113

Member Function Documentation

uint64_t proxygen::RFC1867Codec::getBytesProcessed ( ) const
inline

Definition at line 66 of file RFC1867.h.

References bytesProcessed_.

66  {
67  return bytesProcessed_;
68  }
uint64_t bytesProcessed_
Definition: RFC1867.h:118
void proxygen::RFC1867Codec::onBody ( HTTPCodec::StreamID  stream,
std::unique_ptr< folly::IOBuf chain,
uint16_t  padding 
)
inlineoverrideprivatevirtual

Called for each block of message body data

Parameters
streamThe stream ID
chainOne or more buffers of body data. The codec will remove any protocol framing, such as HTTP/1.1 chunk headers, from the buffers before calling this function.
paddingNumber of pad bytes that came with the data segment

Implements proxygen::HTTPCodec::Callback.

Definition at line 85 of file RFC1867.h.

References headerParser_, parseError_, and proxygen::HTTP1xCodec::setParserPaused().

87  {
88  parseError_ = true;
90  }
HTTP1xCodec headerParser_
Definition: RFC1867.h:113
void setParserPaused(bool paused) override
void proxygen::RFC1867Codec::onError ( HTTPCodec::StreamID  stream,
const HTTPException error,
bool  newTxn 
)
inlineoverrideprivatevirtual

Called when a parsing or protocol error has occurred

Parameters
streamThe stream ID
errorDescription of the error
newTxntrue if onMessageBegin has not been called for txn

Implements proxygen::HTTPCodec::Callback.

Definition at line 101 of file RFC1867.h.

References headerParser_, parseError_, readToBoundary(), and proxygen::HTTP1xCodec::setParserPaused().

103  {
104  parseError_ = true;
106  }
HTTP1xCodec headerParser_
Definition: RFC1867.h:113
void setParserPaused(bool paused) override
void proxygen::RFC1867Codec::onHeadersComplete ( HTTPCodec::StreamID  stream,
std::unique_ptr< HTTPMessage msg 
)
overrideprivatevirtual

Called when all the headers of an ingress message have been parsed

Parameters
streamThe stream ID
msgThe message
sizeSize of the ingress header

Implements proxygen::HTTPCodec::Callback.

Definition at line 178 of file RFC1867.cpp.

References callback_, folly::Range< Iter >::data(), proxygen::ERROR, proxygen::HTTP_HEADER_CONTENT_DISPOSITION, folly::gen::move, name, folly::Range< Iter >::reset(), folly::Range< Iter >::size(), folly::Range< Iter >::str(), and value.

Referenced by onMessageBegin().

179  {
180  static const StringPiece kName("name", 4);
181  static const StringPiece kFilename("filename", 8);
182  static const StringPiece kFormData("form-data", 9);
183 
184  const auto& contentDisp =
185  msg->getHeaders().getSingleOrEmpty(HTTP_HEADER_CONTENT_DISPOSITION);
186  string name;
187  folly::Optional<string> filename; // filename is optional
189  contentDisp, ';', '=',
190  [&] (folly::StringPiece parameter, folly::StringPiece value) {
191  // TODO: Trim whitespace first
192  // Strip quotes if present
193  if (value.size() >= 2 && value[0] == '\"' &&
194  value[value.size() - 1] == '\"') {
195  value.reset(value.data() + 1, value.size() - 2);
196  }
197  if (parameter == kName) {
198  name = value.str();
199  } else if (parameter == kFilename) {
200  filename = value.str();
201  } else if (parameter != kFormData) {
202  LOG(WARNING) << "Ignoring parameter " << parameter << " value \""
203  << value << '"';
204  }
205  });
206  if (name.empty()) {
207  if (callback_) {
208  LOG(ERROR) << "name empty";
209  callback_->onError();
210  }
212  return;
213  } else {
215  if (callback_ && callback_->onFieldStart(name, filename,
216  std::move(msg),
217  bytesProcessed_) < 0) {
218  field_ = name;
219  LOG(WARNING) << "Callback returned error";
221  }
222  }
223 }
Callback * callback_
Definition: RFC1867.h:111
static void splitNameValuePieces(const std::string &input, char pairDelim, char valueDelim, std::function< void(folly::StringPiece, folly::StringPiece)> callback)
std::string str() const
Definition: Range.h:591
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
constexpr size_type size() const
Definition: Range.h:431
const char * name
Definition: http_parser.c:437
constexpr Iter data() const
Definition: Range.h:446
virtual int onFieldStart(const std::string &name, folly::Optional< std::string > filename, std::unique_ptr< HTTPMessage > msg, uint64_t postBytesProcessed)=0
static const char *const value
Definition: Conv.cpp:50
std::string field_
Definition: RFC1867.h:114
ParserState state_
Definition: RFC1867.h:112
Range< const char * > StringPiece
uint64_t bytesProcessed_
Definition: RFC1867.h:118
void reset(Iter start, size_type size)
Definition: Range.h:421
std::unique_ptr< IOBuf > proxygen::RFC1867Codec::onIngress ( std::unique_ptr< folly::IOBuf data)

Definition at line 61 of file RFC1867.cpp.

References folly::IOBufQueue::append(), c, callback_, ch, proxygen::ERROR, proxygen::spdy::HEADERS, folly::gen::move, and uint8_t.

Referenced by setCallback().

61  {
62  static auto dummyBuf = IOBuf::wrapBuffer(kDummyGet.data(),
63  kDummyGet.length());
64  IOBufQueue result{IOBufQueue::cacheChainLength()};
65  bool foundBoundary = false;
66  BoundaryResult br = BoundaryResult::NO;
67 
68  input_.append(std::move(data));
69  while (!input_.empty()) {
70  switch (state_) {
71  case ParserState::START:
72  // first time, must start with boundary without leading \n
73  br = isBoundary(*input_.front(), 0, boundary_.data() + 1,
74  boundary_.length() - 1);
75  if (br == BoundaryResult::NO) {
76  if (callback_) {
77  LOG(ERROR) << "Invalid starting sequence";
78  callback_->onError();
79  }
81  return nullptr;
82  } else if (br == BoundaryResult::PARTIAL) {
83  return input_.move();
84  }
85  input_.trimStart(boundary_.length() - 1);
86  bytesProcessed_ += boundary_.length() - 1;
88  // fall through
89 
91  {
92  if (input_.chainLength() < 3) {
93  return input_.move();
94  }
95  Cursor c(input_.front());
96  char firstTwo[2];
97  c.pull(firstTwo, 2);
98  // We have at least 3 chars available to read
99  uint8_t toTrim = 3;
100  if (memcmp(firstTwo, "--", 2) == 0) {
101  do {
102  auto ch = c.read<char>();
103  if (ch == '\n') {
104  input_.trimStart(toTrim);
106  } else if (ch == '\r') {
107  // Every \r we encounter is a char we must trim but we must
108  // make sure we have sufficient data available in input_ to
109  // keep reading (toTrim is always one pos ahead to handle the
110  // expected \n)
111  ++toTrim;
112  if (input_.chainLength() < toTrim) {
113  return input_.move();
114  }
115  } else {
117  }
118  } while (state_ == ParserState::HEADERS_START);
119  break;
120  }
121  }
123  headerParser_.onIngress(*dummyBuf);
124  CHECK(!parseError_);
126  // fall through
127 
129  while (!parseError_ && input_.front() &&
131  size_t bytesParsed = headerParser_.onIngress(*input_.front());
132  input_.trimStart(bytesParsed);
133  bytesProcessed_ += bytesParsed;
134  }
135  if (parseError_) {
136  if (callback_) {
137  LOG(ERROR) << "Error parsing header data: ";
139  callback_->onError();
140  }
142  return nullptr;
143  }
144  break;
145 
147  result = readToBoundary(foundBoundary);
148  value_.append(result.move());
149  if (!value_.empty() && callback_) {
151  LOG(ERROR) << "Callback returned error";
153  return nullptr;
154  }
155  }
156  if (foundBoundary) {
157  if (callback_) {
159  }
161  } else {
162  if (input_.chainLength() > 0) {
163  VLOG(5) << "Trailing input="
165  }
166  return input_.move();
167  }
168  break;
169  case ParserState::DONE:
170  case ParserState::ERROR:
171  // abort, consume all input
172  return nullptr;
173  }
174  }
175  return nullptr;
176 }
BoundaryResult
Definition: RFC1867.cpp:23
Callback * callback_
Definition: RFC1867.h:111
const folly::IOBuf * front() const
Definition: IOBufQueue.h:476
void append(std::unique_ptr< folly::IOBuf > &&buf, bool pack=false)
Definition: IOBufQueue.cpp:143
size_t chainLength() const
Definition: IOBufQueue.h:492
folly::IOBufQueue readToBoundary(bool &foundBoundary)
Definition: RFC1867.cpp:225
static std::string printHexFolly(const folly::IOBuf *buf, bool coalesce=false)
Definition: Logging.h:127
bool empty() const
Definition: IOBufQueue.h:503
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
std::unique_ptr< folly::IOBuf > move()
Definition: IOBufQueue.h:459
std::string boundary_
Definition: RFC1867.h:110
auto ch
folly::IOBufQueue value_
Definition: RFC1867.h:116
virtual int onFieldData(std::unique_ptr< folly::IOBuf >, uint64_t postBytesProcessed)=0
virtual void onFieldEnd(bool endedOnBoundary, uint64_t postBytesProcessed)=0
folly::IOBufQueue input_
Definition: RFC1867.h:115
size_t onIngress(const folly::IOBuf &buf) override
HTTP1xCodec headerParser_
Definition: RFC1867.h:113
void setParserPaused(bool paused) override
void trimStart(size_t amount)
Definition: IOBufQueue.cpp:255
ParserState state_
Definition: RFC1867.h:112
char c
uint64_t bytesProcessed_
Definition: RFC1867.h:118
void proxygen::RFC1867Codec::onIngressEOM ( )

Definition at line 299 of file RFC1867.cpp.

References callback_, proxygen::ERROR, and uint8_t.

Referenced by setCallback().

299  {
301  LOG(WARNING) << "Field not terminated by boundary";
302  if (callback_) {
304  }
305  }
308  if (callback_) {
309  LOG(ERROR) << "onIngressEOM with state_=" << (uint8_t)state_;
310  callback_->onError();
311  }
312  }
314 }
Callback * callback_
Definition: RFC1867.h:111
virtual void onFieldEnd(bool endedOnBoundary, uint64_t postBytesProcessed)=0
ParserState state_
Definition: RFC1867.h:112
uint64_t bytesProcessed_
Definition: RFC1867.h:118
void proxygen::RFC1867Codec::onMessageBegin ( HTTPCodec::StreamID  stream,
HTTPMessage msg 
)
inlineoverrideprivatevirtual

Called when a new message is seen while parsing the ingress

Parameters
streamThe stream ID
msgA newly allocated HTTPMessage

Implements proxygen::HTTPCodec::Callback.

Definition at line 81 of file RFC1867.h.

References onHeadersComplete().

82  {}
void proxygen::RFC1867Codec::onMessageComplete ( HTTPCodec::StreamID  stream,
bool  upgrade 
)
inlineoverrideprivatevirtual

Called at end of a message (including body and trailers, if applicable)

Parameters
streamThe stream ID
upgradeWhether the connection has been upgraded to another protocol.

Implements proxygen::HTTPCodec::Callback.

Definition at line 96 of file RFC1867.h.

References headerParser_, and proxygen::HTTP1xCodec::setParserPaused().

97  {
99  }
HTTP1xCodec headerParser_
Definition: RFC1867.h:113
void setParserPaused(bool paused) override
void proxygen::RFC1867Codec::onTrailersComplete ( HTTPCodec::StreamID  stream,
std::unique_ptr< HTTPHeaders trailers 
)
inlineoverrideprivatevirtual

Called when all the trailers of an ingress message have been parsed, but only if the number of trailers is nonzero.

Parameters
streamThe stream ID
trailersThe message trailers

Implements proxygen::HTTPCodec::Callback.

Definition at line 91 of file RFC1867.h.

References headerParser_, parseError_, and proxygen::HTTP1xCodec::setParserPaused().

92  {
93  parseError_ = true;
95  }
HTTP1xCodec headerParser_
Definition: RFC1867.h:113
void setParserPaused(bool paused) override
IOBufQueue proxygen::RFC1867Codec::readToBoundary ( bool &  foundBoundary)
private

Definition at line 225 of file RFC1867.cpp.

References c, ch, folly::IOBuf::data(), folly::IOBuf::length(), folly::gen::move, ptr, folly::io::detail::CursorBase< Derived, BufType >::read(), folly::io::detail::CursorBase< Derived, BufType >::skip(), uint32_t, uint64_t, and uint8_t.

Referenced by onError().

225  {
226  IOBufQueue result{IOBufQueue::cacheChainLength()};
227  BoundaryResult boundaryResult = BoundaryResult::NO;
228 
229  while (!input_.empty() && boundaryResult != BoundaryResult::PARTIAL) {
230  const IOBuf* head = input_.front();
231  uint64_t len = head->length();
232  const uint8_t *ptr = head->data();
233 
234  /* iterate through first character matches */
235  while (len > 0 && (ptr = (const uint8_t*)memchr(ptr, boundary_[0], len))) {
236  /* calculate length after match */
237  uint64_t readlen = (ptr - head->data());
238  len = head->length() - readlen;
239  boundaryResult =
240  isBoundary(*head, readlen, boundary_.data(), boundary_.length());
241  if (boundaryResult == BoundaryResult::YES) {
242  CHECK(readlen < head->length());
243  bool hasCr = false;
244  if (readlen == 0 && pendingCR_) {
245  pendingCR_.reset();
246  }
247  if (readlen > 0) {
248  // If the last read char is a CR omit from result
249  Cursor c(head);
250  c.skip(readlen - 1);
251  uint8_t ch = c.read<uint8_t>();
252  if (ch == '\r') {
253  --readlen;
254  hasCr = true;
255  }
256  }
257  result.append(std::move(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;
263  return result;
264  } else if (boundaryResult == BoundaryResult::PARTIAL) {
265  break;
266  } else if (pendingCR_) {
267  // not a match, append pending CR to result
268  result.append(std::move(pendingCR_));
269  }
270 
271  /* next character */
272  ptr++; len--;
273  }
274  uint64_t resultLen = ptr ? ptr - head->data() : head->length();
275  // Put pendingCR_ in result if there was no partial match in head, or a
276  // partial match starting after the first character
277  if ((boundaryResult == BoundaryResult::NO || resultLen > 0) &&
278  pendingCR_) {
279  result.append(std::move(pendingCR_));
280  }
281  // the boundary does not start through resultLen, append it
282  // to result, except maybe the last char if it's a CR.
283  if (resultLen > 0 && head->data()[resultLen - 1] == '\r') {
284  result.append(input_.split(resultLen - 1));
285  CHECK(!pendingCR_);
286  pendingCR_ = input_.split(1);
287  } else {
288  result.append(input_.split(resultLen));
289  }
290  bytesProcessed_ += resultLen;
291  }
292 
293  // reached the end but no boundary found
294  foundBoundary = false;
295 
296  return result;
297 }
BoundaryResult
Definition: RFC1867.cpp:23
void * ptr
std::unique_ptr< folly::IOBuf > split(size_t n)
Definition: IOBufQueue.h:420
const folly::IOBuf * front() const
Definition: IOBufQueue.h:476
bool empty() const
Definition: IOBufQueue.h:503
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
const uint8_t * data() const
Definition: IOBuf.h:499
std::string boundary_
Definition: RFC1867.h:110
auto ch
folly::IOBufQueue input_
Definition: RFC1867.h:115
std::size_t length() const
Definition: IOBuf.h:533
std::unique_ptr< folly::IOBuf > pendingCR_
Definition: RFC1867.h:117
void trimStart(size_t amount)
Definition: IOBufQueue.cpp:255
char c
uint64_t bytesProcessed_
Definition: RFC1867.h:118
void proxygen::RFC1867Codec::setCallback ( Callback callback)
inline

Definition at line 54 of file RFC1867.h.

References callback_, data, onIngress(), and onIngressEOM().

54  {
55  callback_ = callback;
56  }
Callback * callback_
Definition: RFC1867.h:111

Member Data Documentation

std::string proxygen::RFC1867Codec::boundary_
private

Definition at line 110 of file RFC1867.h.

Referenced by RFC1867Codec().

uint64_t proxygen::RFC1867Codec::bytesProcessed_ {0}
private

Definition at line 118 of file RFC1867.h.

Referenced by getBytesProcessed().

Callback* proxygen::RFC1867Codec::callback_ {nullptr}
private

Definition at line 111 of file RFC1867.h.

Referenced by setCallback().

std::string proxygen::RFC1867Codec::field_
private

Definition at line 114 of file RFC1867.h.

HTTP1xCodec proxygen::RFC1867Codec::headerParser_ {TransportDirection::DOWNSTREAM}
private

Definition at line 113 of file RFC1867.h.

Referenced by onBody(), onError(), onMessageComplete(), onTrailersComplete(), and RFC1867Codec().

folly::IOBufQueue proxygen::RFC1867Codec::input_ {folly::IOBufQueue::cacheChainLength()}
private

Definition at line 115 of file RFC1867.h.

bool proxygen::RFC1867Codec::parseError_ {false}
private

Definition at line 119 of file RFC1867.h.

Referenced by onBody(), onError(), and onTrailersComplete().

std::unique_ptr<folly::IOBuf> proxygen::RFC1867Codec::pendingCR_
private

Definition at line 117 of file RFC1867.h.

ParserState proxygen::RFC1867Codec::state_ {ParserState::START}
private

Definition at line 112 of file RFC1867.h.

folly::IOBufQueue proxygen::RFC1867Codec::value_
private

Definition at line 116 of file RFC1867.h.


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