1
#include "source/common/http/mixed_conn_pool.h"
2

            
3
#include "source/common/http/codec_client.h"
4
#include "source/common/http/http1/conn_pool.h"
5
#include "source/common/http/http2/conn_pool.h"
6
#include "source/common/http/utility.h"
7
#include "source/common/runtime/runtime_features.h"
8
#include "source/common/tcp/conn_pool.h"
9

            
10
namespace Envoy {
11
namespace Http {
12

            
13
65
Envoy::ConnectionPool::ActiveClientPtr HttpConnPoolImplMixed::instantiateActiveClient() {
14
65
  uint32_t initial_streams = Http2::ActiveClient::calculateInitialStreamsLimit(
15
65
      http_server_properties_cache_, origin_, host());
16
65
  return std::make_unique<Tcp::ActiveTcpClient>(
17
65
      *this, Envoy::ConnectionPool::ConnPoolImplBase::host(), initial_streams, absl::nullopt);
18
65
}
19

            
20
CodecClientPtr
21
59
HttpConnPoolImplMixed::createCodecClient(Upstream::Host::CreateConnectionData& data) {
22
59
  auto protocol = protocol_ == Protocol::Http11 ? CodecType::HTTP1 : CodecType::HTTP2;
23
59
  CodecClientPtr codec{new CodecClientProd(protocol, std::move(data.connection_),
24
59
                                           data.host_description_, dispatcher_, random_generator_,
25
59
                                           transportSocketOptions())};
26
59
  return codec;
27
59
}
28

            
29
118
void HttpConnPoolImplMixed::onConnected(Envoy::ConnectionPool::ActiveClient& client) {
30
  // onConnected is called under the stack of the Network::Connection raising
31
  // the Connected event. The first time it is called, it's called for a TCP
32
  // client, the TCP client is detached from the connection and discarded, and an
33
  // HTTP client is associated with that connection. When the first call returns, the
34
  // Network::Connection will inform the new callback (the HTTP client) that it
35
  // is connected. The early return is to ignore that second call.
36
118
  if (client.protocol() != absl::nullopt) {
37
59
    return;
38
59
  }
39

            
40
  // If an old TLS stack does not negotiate alpn, it likely does not support
41
  // HTTP/2. Fail over to HTTP/1.
42
59
  protocol_ = Protocol::Http11;
43
59
  ASSERT(dynamic_cast<Tcp::ActiveTcpClient*>(&client) != nullptr);
44
59
  auto tcp_client = static_cast<Tcp::ActiveTcpClient*>(&client);
45
59
  std::string alpn = tcp_client->connection_->nextProtocol();
46
59
  if (!alpn.empty()) {
47
56
    if (alpn == Http::Utility::AlpnNames::get().Http11) {
48
22
      protocol_ = Http::Protocol::Http11;
49
49
    } else if (alpn == Http::Utility::AlpnNames::get().Http2) {
50
34
      protocol_ = Http::Protocol::Http2;
51
34
    }
52
56
  }
53

            
54
59
  uint32_t old_effective_limit = client.effectiveConcurrentStreamLimit();
55
59
  if (protocol_ == Http::Protocol::Http11 && client.concurrent_stream_limit_ != 1) {
56
    // The estimates were all based on assuming HTTP/2 would be negotiated. Adjust down.
57
23
    uint32_t delta = client.concurrent_stream_limit_ - 1;
58
23
    client.concurrent_stream_limit_ = 1;
59
23
    decrConnectingAndConnectedStreamCapacity(delta, client);
60
23
    if (http_server_properties_cache_ && origin_.has_value()) {
61
22
      http_server_properties_cache_->setConcurrentStreams(origin_.value(), 1);
62
22
    }
63
23
  }
64

            
65
59
  Upstream::Host::CreateConnectionData data{std::move(tcp_client->connection_),
66
59
                                            client.real_host_description_};
67
  // As this connection comes from the tcp connection pool, it will be
68
  // read-disabled to handle TCP traffic where upstream sends data first. Undo
69
  // this as it is not necessary for HTTP/HTTPS.
70
59
  data.connection_->readDisable(false);
71
59
  data.connection_->removeConnectionCallbacks(*tcp_client);
72
59
  data.connection_->removeReadFilter(tcp_client->read_filter_handle_);
73
59
  dispatcher_.deferredDelete(client.removeFromList(owningList(client.state())));
74

            
75
59
  std::unique_ptr<ActiveClient> new_client;
76
59
  if (protocol_ == Http::Protocol::Http11) {
77
25
    new_client = std::make_unique<Http1::ActiveClient>(*this, data);
78
51
  } else {
79
34
    new_client = std::make_unique<Http2::ActiveClient>(*this, data);
80
34
  }
81
  // When we switch from TCP to HTTP clients, the base class onConnectionEvent
82
  // will be called for both, so add to the connecting stream capacity to
83
  // balance it being decremented.
84
59
  connecting_stream_capacity_ += new_client->effectiveConcurrentStreamLimit();
85
  // The global capacity is not adjusted in onConnectionEvent, so simply update
86
  // it to reflect any difference between the TCP stream limits and HTTP/2
87
  // stream limits.
88
59
  if (new_client->effectiveConcurrentStreamLimit() > old_effective_limit) {
89
1
    incrConnectingAndConnectedStreamCapacity(
90
1
        new_client->effectiveConcurrentStreamLimit() - old_effective_limit, client);
91
1
  }
92
59
  new_client->setState(ActiveClient::State::Connecting);
93
59
  LinkedList::moveIntoList(std::move(new_client), owningList(new_client->state()));
94
59
}
95

            
96
} // namespace Http
97
} // namespace Envoy