1 /* 2 * Copyright (c) Facebook, Inc. and its affiliates. 3 * All rights reserved. 4 * 5 * This source code is licensed under the BSD-style license found in the 6 * LICENSE file in the root directory of this source tree. 7 */ 8 9 #pragma once 10 11 #include <folly/Conv.h> 12 #include <proxygen/lib/http/codec/HTTP1xCodec.h> 13 14 namespace proxygen { 15 16 /** 17 * Class for stream-parsing RFC 1867 style post data. At present it does 18 * not support nested multi-part content (multipart/mixed). 19 * Can parse multiple POST bodies 20 * unless one of them invokes the onError() callback. After that, the codec is 21 * no longer usable. 22 */ 23 class RFC1867Codec : HTTPCodec::Callback { 24 public: 25 class Callback { 26 public: ~Callback()27 virtual ~Callback() { 28 } 29 // return < 0 to skip remainder of field callbacks? 30 virtual int onFieldStart(const std::string& name, 31 folly::Optional<std::string> filename, 32 std::unique_ptr<HTTPMessage> msg, 33 uint64_t postBytesProcessed) = 0; 34 virtual int onFieldData(std::unique_ptr<folly::IOBuf>, 35 uint64_t postBytesProcessed) = 0; 36 /** On reading to end of a part indicated by boundary 37 * @param endedOnBoundary indicate successful part end 38 */ 39 virtual void onFieldEnd(bool endedOnBoundary, 40 uint64_t postBytesProcessed) = 0; 41 virtual void onError() = 0; 42 }; 43 44 // boundary is the parameter to Content-Type, eg: 45 // 46 // Content-type: multipart/form-data, boundary=AaB03x RFC1867Codec(const std::string & boundary)47 explicit RFC1867Codec(const std::string& boundary) { 48 CHECK(!boundary.empty()); 49 boundary_ = folly::to<std::string>("\n--", boundary); 50 headerParser_.setCallback(this); 51 } 52 setCallback(Callback * callback)53 void setCallback(Callback* callback) { 54 callback_ = callback; 55 } 56 57 // Pass the next piece of input data. Returns unparsed data that requires 58 // more input to continue 59 std::unique_ptr<folly::IOBuf> onIngress(std::unique_ptr<folly::IOBuf> data); 60 61 // The end of input has been seen. Validate the parser state and reset 62 // for more parsing. 63 void onIngressEOM(); 64 getBytesProcessed()65 uint64_t getBytesProcessed() const { 66 return bytesProcessed_; 67 } 68 69 private: 70 enum class ParserState { 71 START, 72 HEADERS_START, 73 HEADERS, 74 FIELD_DATA, // part, or field, not only file 75 DONE, 76 ERROR 77 }; 78 79 // HTTPCodec::Callback onMessageBegin(HTTPCodec::StreamID,HTTPMessage *)80 void onMessageBegin(HTTPCodec::StreamID /*stream*/, 81 HTTPMessage* /*msg*/) override { 82 } 83 void onHeadersComplete(HTTPCodec::StreamID stream, 84 std::unique_ptr<HTTPMessage> msg) override; onBody(HTTPCodec::StreamID,std::unique_ptr<folly::IOBuf>,uint16_t)85 void onBody(HTTPCodec::StreamID /*stream*/, 86 std::unique_ptr<folly::IOBuf> /*chain*/, 87 uint16_t /*padding*/) override { 88 parseError_ = true; 89 headerParser_.setParserPaused(true); 90 } onTrailersComplete(HTTPCodec::StreamID,std::unique_ptr<HTTPHeaders>)91 void onTrailersComplete(HTTPCodec::StreamID /*stream*/, 92 std::unique_ptr<HTTPHeaders> /*trailers*/) override { 93 parseError_ = true; 94 headerParser_.setParserPaused(true); 95 } onMessageComplete(HTTPCodec::StreamID,bool)96 void onMessageComplete(HTTPCodec::StreamID /*stream*/, 97 bool /*upgrade*/) override { 98 headerParser_.setParserPaused(true); 99 } 100 onError(HTTPCodec::StreamID,const HTTPException &,bool)101 void onError(HTTPCodec::StreamID /*stream*/, 102 const HTTPException& /*error*/, 103 bool /*newTxn*/) override { 104 parseError_ = true; 105 headerParser_.setParserPaused(true); 106 } 107 108 folly::IOBufQueue readToBoundary(bool& foundBoundary); 109 110 std::string boundary_; 111 Callback* callback_{nullptr}; 112 ParserState state_{ParserState::START}; 113 HTTP1xCodec headerParser_{TransportDirection::DOWNSTREAM}; 114 std::string field_; 115 folly::IOBufQueue input_{folly::IOBufQueue::cacheChainLength()}; 116 folly::IOBufQueue value_; 117 std::unique_ptr<folly::IOBuf> pendingCR_; 118 uint64_t bytesProcessed_{0}; 119 bool parseError_{false}; 120 }; 121 122 } // namespace proxygen 123