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