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 <bitset>
12 #include <deque>
13 #include <folly/Optional.h>
14 #include <proxygen/lib/http/HTTPHeaders.h>
15 #include <proxygen/lib/http/codec/HTTPCodec.h>
16 #include <proxygen/lib/http/codec/HTTPSettings.h>
17 #include <proxygen/lib/http/codec/compress/HeaderCodec.h>
18 #include <zlib.h>
19 
20 namespace folly { namespace io {
21 class Cursor;
22 }} // namespace folly::io
23 
24 namespace proxygen {
25 
26 /**
27  * An implementation of common codec functionality used for multiple
28  * parallel stream downloads. Currently shared by SPDY and HTTP/2
29  */
30 class HTTPParallelCodec : public HTTPCodec {
31  public:
32   explicit HTTPParallelCodec(TransportDirection direction);
33 
getTransportDirection()34   TransportDirection getTransportDirection() const override {
35     return transportDirection_;
36   }
37 
38   StreamID createStream() override;
isBusy()39   bool isBusy() const override {
40     return false;
41   }
supportsStreamFlowControl()42   bool supportsStreamFlowControl() const override {
43     return true;
44   }
supportsSessionFlowControl()45   bool supportsSessionFlowControl() const override {
46     return true;
47   }
supportsParallelRequests()48   bool supportsParallelRequests() const override {
49     return true;
50   }
closeOnEgressComplete()51   bool closeOnEgressComplete() const override {
52     return false;
53   }
setCallback(Callback * callback)54   void setCallback(Callback* callback) override {
55     callback_ = callback;
56   }
setParserPaused(bool)57   void setParserPaused(bool /* paused */) override {
58   }
isParserPaused()59   bool isParserPaused() const override {
60     return false;
61   }
onIngressEOF()62   void onIngressEOF() override {
63   }
64   bool isReusable() const override;
65   bool isWaitingToDrain() const override;
getLastIncomingStreamID()66   StreamID getLastIncomingStreamID() const override {
67     return lastStreamID_;
68   }
69   void enableDoubleGoawayDrain() override;
70 
71   bool onIngressUpgradeMessage(const HTTPMessage& msg) override;
72 
setNextEgressStreamId(StreamID nextEgressStreamID)73   void setNextEgressStreamId(StreamID nextEgressStreamID) {
74     if (nextEgressStreamID > nextEgressStreamID_ &&
75         (nextEgressStreamID & 0x1) == (nextEgressStreamID_ & 0x1) &&
76         nextEgressStreamID_ < std::numeric_limits<int32_t>::max()) {
77       nextEgressStreamID_ = nextEgressStreamID;
78     }
79   }
80 
isInitiatedStream(StreamID stream)81   bool isInitiatedStream(StreamID stream) const {
82     return isInitiatedStream(transportDirection_, stream);
83   }
84 
isInitiatedStream(TransportDirection direction,StreamID stream)85   static bool isInitiatedStream(TransportDirection direction, StreamID stream) {
86     bool odd = stream & 0x01;
87     bool upstream = (direction == TransportDirection::UPSTREAM);
88     return (odd && upstream) || (!odd && !upstream);
89   }
90 
isStreamIngressEgressAllowed(StreamID stream)91   bool isStreamIngressEgressAllowed(StreamID stream) const {
92     bool isInitiated = isInitiatedStream(stream);
93     return (isInitiated && stream <= ingressGoawayAck_) ||
94            (!isInitiated && stream <= egressGoawayAck_);
95   }
96 
97  protected:
98   TransportDirection transportDirection_;
99   StreamID nextEgressStreamID_;
100   StreamID lastStreamID_{0};
101   HTTPCodec::Callback* callback_{nullptr};
102   StreamID ingressGoawayAck_{std::numeric_limits<uint32_t>::max()};
103   StreamID egressGoawayAck_{std::numeric_limits<uint32_t>::max()};
104   std::string goawayErrorMessage_;
105 
106   enum ClosingState {
107     OPEN = 0,
108     OPEN_WITH_GRACEFUL_DRAIN_ENABLED = 1,
109     FIRST_GOAWAY_SENT = 2,
110     CLOSING = 3, // SPDY only
111     CLOSED = 4   // HTTP2 only
112   } sessionClosing_;
113 
114   template <typename T, typename... Args>
deliverCallbackIfAllowed(T callbackFn,char const * cbName,StreamID stream,Args &&...args)115   bool deliverCallbackIfAllowed(T callbackFn,
116                                 char const* cbName,
117                                 StreamID stream,
118                                 Args&&... args) {
119     if (isStreamIngressEgressAllowed(stream)) {
120       if (callback_) {
121         (*callback_.*callbackFn)(stream, std::forward<Args>(args)...);
122       }
123       return true;
124     } else {
125       VLOG(2) << "Suppressing " << cbName << " for stream=" << stream
126               << " egressGoawayAck_=" << egressGoawayAck_;
127     }
128     return false;
129   }
130 };
131 } // namespace proxygen
132