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