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/codec/HQFramedCodec.h>
12 #include <proxygen/lib/http/codec/HQFramer.h>
13 #include <proxygen/lib/http/codec/HQUnidirectionalCodec.h>
14 #include <proxygen/lib/http/codec/HQUtils.h>
15 #include <proxygen/lib/http/codec/HTTPCodec.h>
16 #include <proxygen/lib/http/codec/compress/QPACKCodec.h>
17 
18 #include <folly/io/IOBuf.h>
19 #include <folly/lang/Assume.h>
20 
21 namespace proxygen { namespace hq {
22 
23 class HQControlCodec
24     : public HQUnidirectionalCodec
25     , public HQFramedCodec {
26 
27  public:
28   HQControlCodec(
29       HTTPCodec::StreamID streamId,
30       TransportDirection direction,
31       StreamDirection streamDir,
32       HTTPSettings& settings,
33       UnidirectionalStreamType streamType = UnidirectionalStreamType::CONTROL)
HQUnidirectionalCodec(streamType,streamDir)34       : HQUnidirectionalCodec(streamType, streamDir),
35         HQFramedCodec(streamId, direction),
36         settings_(settings) {
37     VLOG(4) << "creating " << getTransportDirectionString(direction)
38             << " HQ Control codec for stream " << streamId_;
39     egressGoawayAck_ = direction == TransportDirection::UPSTREAM
40                            ? kMaxPushId + 1
41                            : kMaxClientBidiStreamId;
42   }
43 
~HQControlCodec()44   ~HQControlCodec() override {
45   }
46 
47   // HQ Unidirectional Codec API
onUnidirectionalIngress(std::unique_ptr<folly::IOBuf> buf)48   std::unique_ptr<folly::IOBuf> onUnidirectionalIngress(
49       std::unique_ptr<folly::IOBuf> buf) override {
50     CHECK(buf);
51     auto consumed = onFramedIngress(*buf);
52     folly::IOBufQueue q;
53     q.append(std::move(buf));
54     q.trimStart(consumed);
55     return q.move();
56   }
57 
onUnidirectionalIngressEOF()58   void onUnidirectionalIngressEOF() override {
59     LOG(ERROR) << "Unexpected control stream EOF";
60     if (callback_) {
61       HTTPException ex(HTTPException::Direction::INGRESS_AND_EGRESS,
62                        "Control stream EOF");
63       ex.setHttp3ErrorCode(HTTP3::ErrorCode::HTTP_CLOSED_CRITICAL_STREAM);
64       callback_->onError(streamId_, ex, false);
65     }
66   }
67 
68   // HTTPCodec API
69   bool isWaitingToDrain() const override;
70 
getProtocol()71   CodecProtocol getProtocol() const override {
72     return CodecProtocol::HQ;
73   }
74 
onIngress(const folly::IOBuf &)75   size_t onIngress(const folly::IOBuf& /*buf*/) override {
76     LOG(FATAL) << __func__ << " not supported";
77     folly::assume_unreachable();
78   }
79 
onIngressEOF()80   void onIngressEOF() override {
81     // error
82   }
83 
84   size_t generateGoaway(
85       folly::IOBufQueue& writeBuf,
86       StreamID lastStream,
87       ErrorCode statusCode,
88       std::unique_ptr<folly::IOBuf> debugData = nullptr) override;
89 
90   size_t generateSettings(folly::IOBufQueue& writeBuf) override;
91 
92   size_t generatePriority(folly::IOBufQueue& writeBuf,
93                           StreamID stream,
94                           const HTTPMessage::HTTP2Priority& pri) override;
95 
96   size_t generatePriority(folly::IOBufQueue& writeBuf,
97                           StreamID stream,
98                           HTTPPriority priority) override;
99 
100   size_t generatePushPriority(folly::IOBufQueue& writeBuf,
101                               StreamID pushId,
102                               HTTPPriority priority) override;
103 
getIngressSettings()104   const HTTPSettings* getIngressSettings() const override {
105     CHECK(isIngress());
106     return &settings_;
107   }
108 
getEgressSettings()109   HTTPSettings* getEgressSettings() override {
110     CHECK(isEgress());
111     return &settings_;
112   }
113 
enableDoubleGoawayDrain()114   void enableDoubleGoawayDrain() override {
115     doubleGoaway_ = true;
116   }
117 
getDefaultWindowSize()118   uint32_t getDefaultWindowSize() const override {
119     CHECK(false) << __func__ << " not supported";
120     folly::assume_unreachable();
121   }
122 
setHeaderCodecStats(HeaderCodec::Stats *)123   void setHeaderCodecStats(HeaderCodec::Stats* /*hcStats*/) override {
124     CHECK(false) << __func__ << " not supported";
125   }
126 
getCompressionInfo()127   CompressionInfo getCompressionInfo() const override {
128     CHECK(false) << __func__ << " not supported";
129     folly::assume_unreachable();
130   }
131 
132   size_t addPriorityNodes(PriorityQueue& queue,
133                           folly::IOBufQueue& writeBuf,
134                           uint8_t maxLevel) override;
135 
136   HTTPCodec::StreamID mapPriorityToDependency(uint8_t priority) const override;
137 
138  protected:
139   ParseResult checkFrameAllowed(FrameType type) override;
140   ParseResult parseCancelPush(folly::io::Cursor& cursor,
141                               const FrameHeader& header) override;
142   ParseResult parseSettings(folly::io::Cursor& cursor,
143                             const FrameHeader& header) override;
144   ParseResult parseGoaway(folly::io::Cursor& cursor,
145                           const FrameHeader& header) override;
146   ParseResult parseMaxPushId(folly::io::Cursor& cursor,
147                              const FrameHeader& header) override;
148   ParseResult parsePriorityUpdate(folly::io::Cursor& cursor,
149                                   const FrameHeader& header) override;
150   ParseResult parsePushPriorityUpdate(folly::io::Cursor& cursor,
151                                       const FrameHeader& header) override;
152 
153   uint64_t finalGoawayId();
154 
155  protected:
156   bool doubleGoaway_{true};
157   bool sentGoaway_{false};
158   bool sentFinalGoaway_{false};
159   bool receivedSettings_{false};
160   bool sentSettings_{false};
161   quic::StreamId egressGoawayAck_;
162   quic::StreamId minUnseenStreamID_{kMaxClientBidiStreamId};
163   uint64_t minUnseenPushID_{kMaxPushId + 1};
164   HTTPSettings& settings_;
165 };
166 
167 }} // namespace proxygen::hq
168