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 <proxygen/lib/http/HTTPMessage.h>
12 #include <proxygen/lib/http/codec/HTTPCodec.h>
13 #include <proxygen/lib/http/codec/TransportDirection.h>
14 #include <string>
15 
16 #include <proxygen/external/http_parser/http_parser.h>
17 
18 namespace proxygen {
19 
20 class HTTP1xCodec : public HTTPCodec {
21  public:
22   // Default strictValidation to false for now to match existing behavior
23   explicit HTTP1xCodec(TransportDirection direction,
24                        bool force1_1 = false,
25                        bool strictValidation = false);
26   ~HTTP1xCodec() override;
27 
28   HTTP1xCodec(HTTP1xCodec&&) = default;
29 
30   // Returns codec for response generation, allowing to set flags that are
31   // normally set during request processing.
32   // Normally codec processes request/response pair, but is also used for
33   // serialization and processes single message.
34   static HTTP1xCodec makeResponseCodec(bool mayChunkEgress);
35 
36   // HTTPCodec API
getProtocol()37   CodecProtocol getProtocol() const override {
38     return CodecProtocol::HTTP_1_1;
39   }
40 
getUserAgent()41   const std::string& getUserAgent() const override {
42     return userAgent_;
43   }
44 
getTransportDirection()45   TransportDirection getTransportDirection() const override {
46     return transportDirection_;
47   }
48   StreamID createStream() override;
setCallback(Callback * callback)49   void setCallback(Callback* callback) override {
50     callback_ = callback;
51   }
52   bool isBusy() const override;
53   void setParserPaused(bool paused) override;
isParserPaused()54   bool isParserPaused() const override {
55     return parserPaused_;
56   }
57   size_t onIngress(const folly::IOBuf& buf) override;
58   void onIngressEOF() override;
59   bool isReusable() const override;
isWaitingToDrain()60   bool isWaitingToDrain() const override {
61     return disableKeepalivePending_ && keepalive_;
62   }
isEgressBusy()63   bool isEgressBusy() const {
64     return ((transportDirection_ == TransportDirection::DOWNSTREAM &&
65              responsePending_) ||
66             // count egress busy for non-upgraded upstream codecs with a
67             // pending response.  HTTP/1.x servers are inconsistent in how they
68             // interpret an EOF with a pending response, so don't trigger one
69             // unless the connection was upgraded.
70             (transportDirection_ == TransportDirection::UPSTREAM &&
71              (requestPending_ || (!egressUpgrade_ && responsePending_))));
72   }
73   // True if the session requires an EOF (or RST) to terminate the message
closeOnEgressComplete()74   bool closeOnEgressComplete() const override {
75     return !isEgressBusy() && !isReusable();
76   }
supportsParallelRequests()77   bool supportsParallelRequests() const override {
78     return false;
79   }
supportsPushTransactions()80   bool supportsPushTransactions() const override {
81     return false;
82   }
83   void generateHeader(
84       folly::IOBufQueue& writeBuf,
85       StreamID txn,
86       const HTTPMessage& msg,
87       bool eom = false,
88       HTTPHeaderSize* size = nullptr,
89       const folly::Optional<HTTPHeaders>& extraHeaders = folly::none) override;
90   size_t generateBody(folly::IOBufQueue& writeBuf,
91                       StreamID txn,
92                       std::unique_ptr<folly::IOBuf> chain,
93                       folly::Optional<uint8_t> padding,
94                       bool eom) override;
95   size_t generateChunkHeader(folly::IOBufQueue& writeBuf,
96                              StreamID txn,
97                              size_t length) override;
98   size_t generateChunkTerminator(folly::IOBufQueue& writeBuf,
99                                  StreamID txn) override;
100   size_t generateTrailers(folly::IOBufQueue& writeBuf,
101                           StreamID txn,
102                           const HTTPHeaders& trailers) override;
103   size_t generateEOM(folly::IOBufQueue& writeBuf, StreamID txn) override;
104   size_t generateRstStream(folly::IOBufQueue& writeBuf,
105                            StreamID txn,
106                            ErrorCode statusCode) override;
107   size_t generateGoaway(
108       folly::IOBufQueue& writeBuf,
109       StreamID lastStream,
110       ErrorCode statusCode,
111       std::unique_ptr<folly::IOBuf> debugData = nullptr) override;
112 
generateImmediateGoaway(folly::IOBufQueue &,ErrorCode,std::unique_ptr<folly::IOBuf>)113   size_t generateImmediateGoaway(folly::IOBufQueue&,
114                                  ErrorCode,
115                                  std::unique_ptr<folly::IOBuf>) override {
116     keepalive_ = false;
117     return 0;
118   }
119 
120   void setAllowedUpgradeProtocols(std::list<std::string> protocols);
121   const std::string& getAllowedUpgradeProtocols();
122 
setStrictValidation(bool strict)123   void setStrictValidation(bool strict) {
124     strictValidation_ = strict;
125   }
126 
127   /**
128    * @returns true if the codec supports the given NPN protocol.
129    */
130   static bool supportsNextProtocol(const std::string& npn);
131 
132  private:
133   /** Simple state model used to track the parsing of HTTP headers */
134   enum class HeaderParseState : uint8_t {
135     kParsingHeaderIdle,
136     kParsingHeaderStart,
137     kParsingHeaderName,
138     kParsingHeaderValue,
139     kParsingHeadersComplete,
140     kParsingTrailerName,
141     kParsingTrailerValue
142   };
143 
144   std::string generateWebsocketKey() const;
145   std::string generateWebsocketAccept(const std::string& acceptKey) const;
146   mutable std::string websockAcceptKey_;
147 
148   /** Used to keep track of whether a client requested keep-alive. This is
149    * only useful to support HTTP 1.0 keep-alive for a downstream connection
150    * where keep-alive is disabled unless the client requested it. */
151   enum class KeepaliveRequested : uint8_t {
152     UNSET,
153     ENABLED,  // incoming message requested keepalive
154     DISABLED, // incoming message disabled keepalive
155   };
156 
157   void addDateHeader(folly::IOBufQueue& writeBuf, size_t& len);
158 
159   /** Check whether we're currently parsing ingress message headers */
isParsingHeaders()160   bool isParsingHeaders() const {
161     return (headerParseState_ > HeaderParseState::kParsingHeaderIdle) &&
162            (headerParseState_ < HeaderParseState::kParsingHeadersComplete);
163   }
164 
165   /** Check whether we're currently parsing ingress header-or-trailer name */
isParsingHeaderOrTrailerName()166   bool isParsingHeaderOrTrailerName() const {
167     return (headerParseState_ == HeaderParseState::kParsingHeaderName) ||
168            (headerParseState_ == HeaderParseState::kParsingTrailerName);
169   }
170 
171   /** Invoked when a parsing error occurs. It will send an exception to
172       the callback object to report the error and do any other cleanup
173       needed. It optionally takes a message to pass to the generated
174       HTTPException passed to callback_. */
175   void onParserError(const char* what = nullptr);
176 
177   /** Push out header name-value pair to hdrs and clear currentHeader*_ */
178   bool pushHeaderNameAndValue(HTTPHeaders& hdrs);
179 
180   /** Serialize websocket headers into a buffer **/
181   void serializeWebsocketHeader(folly::IOBufQueue& writeBuf,
182                                 size_t& len,
183                                 bool upstream);
184 
185   // Parser callbacks
186   int onMessageBegin();
187   int onURL(const char* buf, size_t len);
188   int onReason(const char* buf, size_t len);
189   int onHeaderField(const char* buf, size_t len);
190   int onHeaderValue(const char* buf, size_t len);
191   int onHeadersComplete(size_t len);
192   int onBody(const char* buf, size_t len);
193   int onChunkHeader(size_t len);
194   int onChunkComplete();
195   int onMessageComplete();
196 
197   HTTPCodec::Callback* callback_;
198   StreamID ingressTxnID_;
199   StreamID egressTxnID_;
200   http_parser parser_;
201   const folly::IOBuf* currentIngressBuf_;
202   std::unique_ptr<HTTPMessage> msg_;
203   std::unique_ptr<HTTPMessage> upgradeRequest_;
204   std::unique_ptr<HTTPHeaders> trailers_;
205   std::string currentHeaderName_;
206   folly::StringPiece currentHeaderNameStringPiece_;
207   std::string currentHeaderValue_;
208   std::string url_;
209   std::string userAgent_;
210   std::string reason_;
211   std::string upgradeHeader_; // last sent/received client upgrade header
212   std::string allowedNativeUpgrades_; // DOWNSTREAM only
213   HTTPHeaderSize headerSize_;
214   HeaderParseState headerParseState_;
215   TransportDirection transportDirection_;
216   KeepaliveRequested keepaliveRequested_; // only used in DOWNSTREAM mode
217   std::pair<CodecProtocol, std::string> upgradeResult_; // DOWNSTREAM only
218   bool force1_1_ : 1; // Use HTTP/1.1 even if msg is 1.0
219   bool strictValidation_ : 1;
220   bool parserActive_ : 1;
221   bool pendingEOF_ : 1;
222   bool parserPaused_ : 1;
223   bool parserError_ : 1;
224   bool requestPending_ : 1;
225   bool responsePending_ : 1;
226   bool egressChunked_ : 1;
227   bool inChunk_ : 1;
228   bool lastChunkWritten_ : 1;
229   bool keepalive_ : 1;
230   bool disableKeepalivePending_ : 1;
231   // TODO: replace the 2 booleans below with an enum "request method"
232   bool connectRequest_ : 1;
233   bool headRequest_ : 1;
234   bool expectNoResponseBody_ : 1;
235   bool mayChunkEgress_ : 1;
236   bool is1xxResponse_ : 1;
237   bool inRecvLastChunk_ : 1;
238   bool ingressUpgrade_ : 1;
239   bool ingressUpgradeComplete_ : 1;
240   bool egressUpgrade_ : 1;
241   bool nativeUpgrade_ : 1;
242   bool headersComplete_ : 1;
243 
244   // C-callable wrappers for the http_parser callbacks
245   static int onMessageBeginCB(http_parser* parser);
246   static int onPathCB(http_parser* parser, const char* buf, size_t len);
247   static int onQueryStringCB(http_parser* parser, const char* buf, size_t len);
248   static int onUrlCB(http_parser* parser, const char* buf, size_t len);
249   static int onReasonCB(http_parser* parser, const char* buf, size_t len);
250   static int onHeaderFieldCB(http_parser* parser, const char* buf, size_t len);
251   static int onHeaderValueCB(http_parser* parser, const char* buf, size_t len);
252   static int onHeadersCompleteCB(http_parser* parser,
253                                  const char* buf,
254                                  size_t len);
255   static int onBodyCB(http_parser* parser, const char* buf, size_t len);
256   static int onChunkHeaderCB(http_parser* parser);
257   static int onChunkCompleteCB(http_parser* parser);
258   static int onMessageCompleteCB(http_parser* parser);
259 
260   static const http_parser_settings* getParserSettings();
261 };
262 
263 } // namespace proxygen
264