forked from envoyproxy/envoy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
http_integration.h
280 lines (237 loc) · 13.5 KB
/
http_integration.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
#pragma once
#include <cstdint>
#include <memory>
#include <string>
#include "common/http/codec_client.h"
#include "common/network/filter_impl.h"
#include "test/common/http/http2/http2_frame.h"
#include "test/integration/integration.h"
#include "test/integration/utility.h"
#include "test/test_common/printers.h"
namespace Envoy {
using ::Envoy::Http::Http2::Http2Frame;
/**
* HTTP codec client used during integration testing.
*/
class IntegrationCodecClient : public Http::CodecClientProd {
public:
IntegrationCodecClient(Event::Dispatcher& dispatcher, Random::RandomGenerator& random,
Network::ClientConnectionPtr&& conn,
Upstream::HostDescriptionConstSharedPtr host_description,
Http::CodecClient::Type type);
IntegrationStreamDecoderPtr makeHeaderOnlyRequest(const Http::RequestHeaderMap& headers);
IntegrationStreamDecoderPtr makeRequestWithBody(const Http::RequestHeaderMap& headers,
uint64_t body_size);
IntegrationStreamDecoderPtr makeRequestWithBody(const Http::RequestHeaderMap& headers,
const std::string& body);
bool sawGoAway() const { return saw_goaway_; }
bool connected() const { return connected_; }
void sendData(Http::RequestEncoder& encoder, absl::string_view data, bool end_stream);
void sendData(Http::RequestEncoder& encoder, Buffer::Instance& data, bool end_stream);
void sendData(Http::RequestEncoder& encoder, uint64_t size, bool end_stream);
void sendTrailers(Http::RequestEncoder& encoder, const Http::RequestTrailerMap& trailers);
void sendReset(Http::RequestEncoder& encoder);
// Intentionally makes a copy of metadata_map.
void sendMetadata(Http::RequestEncoder& encoder, Http::MetadataMap metadata_map);
std::pair<Http::RequestEncoder&, IntegrationStreamDecoderPtr>
startRequest(const Http::RequestHeaderMap& headers);
ABSL_MUST_USE_RESULT AssertionResult
waitForDisconnect(std::chrono::milliseconds time_to_wait = TestUtility::DefaultTimeout);
Network::ClientConnection* connection() const { return connection_.get(); }
Network::ConnectionEvent lastConnectionEvent() const { return last_connection_event_; }
Network::Connection& rawConnection() { return *connection_; }
bool disconnected() { return disconnected_; }
private:
struct ConnectionCallbacks : public Network::ConnectionCallbacks {
ConnectionCallbacks(IntegrationCodecClient& parent) : parent_(parent) {}
// Network::ConnectionCallbacks
void onEvent(Network::ConnectionEvent event) override;
void onAboveWriteBufferHighWatermark() override {}
void onBelowWriteBufferLowWatermark() override {}
IntegrationCodecClient& parent_;
};
struct CodecCallbacks : public Http::ConnectionCallbacks {
CodecCallbacks(IntegrationCodecClient& parent) : parent_(parent) {}
// Http::ConnectionCallbacks
void onGoAway(Http::GoAwayErrorCode) override { parent_.saw_goaway_ = true; }
IntegrationCodecClient& parent_;
};
void flushWrite();
Event::Dispatcher& dispatcher_;
ConnectionCallbacks callbacks_;
CodecCallbacks codec_callbacks_;
bool connected_{};
bool disconnected_{};
bool saw_goaway_{};
Network::ConnectionEvent last_connection_event_;
};
using IntegrationCodecClientPtr = std::unique_ptr<IntegrationCodecClient>;
/**
* Test fixture for HTTP and HTTP/2 integration tests.
*/
class HttpIntegrationTest : public BaseIntegrationTest {
public:
// TODO(jmarantz): Remove this once
// https://github.com/envoyproxy/envoy-filter-example/pull/69 is reverted.
HttpIntegrationTest(Http::CodecClient::Type downstream_protocol,
Network::Address::IpVersion version, TestTimeSystemPtr,
const std::string& config = ConfigHelper::httpProxyConfig())
: HttpIntegrationTest(downstream_protocol, version, config) {}
HttpIntegrationTest(Http::CodecClient::Type downstream_protocol,
Network::Address::IpVersion version,
const std::string& config = ConfigHelper::httpProxyConfig());
HttpIntegrationTest(Http::CodecClient::Type downstream_protocol,
const InstanceConstSharedPtrFn& upstream_address_fn,
Network::Address::IpVersion version,
const std::string& config = ConfigHelper::httpProxyConfig());
~HttpIntegrationTest() override;
protected:
void useAccessLog(absl::string_view format = "",
std::vector<envoy::config::core::v3::TypedExtensionConfig> formatters = {});
IntegrationCodecClientPtr makeHttpConnection(uint32_t port);
// Makes a http connection object without checking its connected state.
virtual IntegrationCodecClientPtr makeRawHttpConnection(
Network::ClientConnectionPtr&& conn,
absl::optional<envoy::config::core::v3::Http2ProtocolOptions> http2_options);
// Makes a http connection object with asserting a connected state.
IntegrationCodecClientPtr makeHttpConnection(Network::ClientConnectionPtr&& conn);
// Sets downstream_protocol_ and alters the HTTP connection manager codec type in the
// config_helper_.
void setDownstreamProtocol(Http::CodecClient::Type type);
// Enable the encoding/decoding of Http1 trailers downstream
ConfigHelper::HttpModifierFunction setEnableDownstreamTrailersHttp1();
// Enable the encoding/decoding of Http1 trailers upstream
ConfigHelper::ConfigModifierFunction setEnableUpstreamTrailersHttp1();
// Sends |request_headers| and |request_body_size| bytes of body upstream.
// Configured upstream to send |response_headers| and |response_body_size|
// bytes of body downstream.
//
// Waits for the complete downstream response before returning.
// Requires |codec_client_| to be initialized.
IntegrationStreamDecoderPtr sendRequestAndWaitForResponse(
const Http::TestRequestHeaderMapImpl& request_headers, uint32_t request_body_size,
const Http::TestResponseHeaderMapImpl& response_headers, uint32_t response_body_size,
int upstream_index = 0, std::chrono::milliseconds time = TestUtility::DefaultTimeout);
// Wait for the end of stream on the next upstream stream on any of the provided fake upstreams.
// Sets fake_upstream_connection_ to the connection and upstream_request_ to stream.
// In cases where the upstream that will receive the request is not deterministic, a second
// upstream index may be provided, in which case both upstreams will be checked for requests.
absl::optional<uint64_t> waitForNextUpstreamRequest(
const std::vector<uint64_t>& upstream_indices,
std::chrono::milliseconds connection_wait_timeout = TestUtility::DefaultTimeout);
void waitForNextUpstreamRequest(
uint64_t upstream_index = 0,
std::chrono::milliseconds connection_wait_timeout = TestUtility::DefaultTimeout);
absl::optional<uint64_t>
waitForNextUpstreamConnection(const std::vector<uint64_t>& upstream_indices,
std::chrono::milliseconds connection_wait_timeout,
FakeHttpConnectionPtr& fake_upstream_connection);
// Close |codec_client_| and |fake_upstream_connection_| cleanly.
void cleanupUpstreamAndDownstream();
// Verifies the response_headers contains the expected_headers, and response body matches given
// body string.
void verifyResponse(IntegrationStreamDecoderPtr response, const std::string& response_code,
const Http::TestResponseHeaderMapImpl& expected_headers,
const std::string& expected_body);
// Helper that sends a request to Envoy, and verifies if Envoy response headers and body size is
// the same as the expected headers map.
// Requires the "http" port has been registered.
void sendRequestAndVerifyResponse(const Http::TestRequestHeaderMapImpl& request_headers,
const int request_size,
const Http::TestResponseHeaderMapImpl& response_headers,
const int response_size, const int backend_idx);
// Check for completion of upstream_request_, and a simple "200" response.
void checkSimpleRequestSuccess(uint64_t expected_request_size, uint64_t expected_response_size,
IntegrationStreamDecoder* response);
using ConnectionCreationFunction = std::function<Network::ClientConnectionPtr()>;
// Sends a simple header-only HTTP request, and waits for a response.
IntegrationStreamDecoderPtr makeHeaderOnlyRequest(ConnectionCreationFunction* create_connection,
int upstream_index,
const std::string& path = "/test/long/url",
const std::string& authority = "host");
void testRouterNotFound();
void testRouterNotFoundWithBody();
void testRouterVirtualClusters();
void testRouterRequestAndResponseWithBody(uint64_t request_size, uint64_t response_size,
bool big_header, bool set_content_length_header = false,
ConnectionCreationFunction* creator = nullptr);
void testRouterHeaderOnlyRequestAndResponse(ConnectionCreationFunction* creator = nullptr,
int upstream_index = 0,
const std::string& path = "/test/long/url",
const std::string& authority = "host");
void testRequestAndResponseShutdownWithActiveConnection();
// Disconnect tests
void testRouterUpstreamDisconnectBeforeRequestComplete();
void
testRouterUpstreamDisconnectBeforeResponseComplete(ConnectionCreationFunction* creator = nullptr);
void testRouterDownstreamDisconnectBeforeRequestComplete(
ConnectionCreationFunction* creator = nullptr);
void testRouterDownstreamDisconnectBeforeResponseComplete(
ConnectionCreationFunction* creator = nullptr);
void testRouterUpstreamResponseBeforeRequestComplete();
void testTwoRequests(bool force_network_backup = false);
void testLargeHeaders(Http::TestRequestHeaderMapImpl request_headers,
Http::TestRequestTrailerMapImpl request_trailers, uint32_t size,
uint32_t max_size);
void testLargeRequestUrl(uint32_t url_size, uint32_t max_headers_size);
void testLargeRequestHeaders(uint32_t size, uint32_t count, uint32_t max_size = 60,
uint32_t max_count = 100);
void testLargeRequestTrailers(uint32_t size, uint32_t max_size = 60);
void testManyRequestHeaders(std::chrono::milliseconds time = TestUtility::DefaultTimeout);
void testAddEncodedTrailers();
void testRetry();
void testRetryHittingBufferLimit();
void testRetryAttemptCountHeader();
void testGrpcRetry();
void testEnvoyHandling100Continue(bool additional_continue_from_upstream = false,
const std::string& via = "", bool disconnect_after_100 = false);
void testEnvoyProxying1xx(bool continue_before_upstream_complete = false,
bool with_encoder_filter = false,
bool with_multiple_1xx_headers = false);
// HTTP/2 client tests.
void testDownstreamResetBeforeResponseComplete();
// Test that trailers are sent. request_trailers_present and
// response_trailers_present will check if the trailers are present, otherwise
// makes sure they were dropped.
void testTrailers(uint64_t request_size, uint64_t response_size, bool request_trailers_present,
bool response_trailers_present);
// Test /drain_listener from admin portal.
void testAdminDrain(Http::CodecClient::Type admin_request_type);
// Test max stream duration.
void testMaxStreamDuration();
void testMaxStreamDurationWithRetry(bool invoke_retry_upstream_disconnect);
Http::CodecClient::Type downstreamProtocol() const { return downstream_protocol_; }
// Prefix listener stat with IP:port, including IP version dependent loopback address.
std::string listenerStatPrefix(const std::string& stat_name);
// The client making requests to Envoy.
IntegrationCodecClientPtr codec_client_;
// A placeholder for the first upstream connection.
FakeHttpConnectionPtr fake_upstream_connection_;
// A placeholder for the first request received at upstream.
FakeStreamPtr upstream_request_;
// A pointer to the request encoder, if used.
Http::RequestEncoder* request_encoder_{nullptr};
// The response headers sent by sendRequestAndWaitForResponse() by default.
Http::TestResponseHeaderMapImpl default_response_headers_{{":status", "200"}};
Http::TestRequestHeaderMapImpl default_request_headers_{
{":method", "GET"}, {":path", "/test/long/url"}, {":scheme", "http"}, {":authority", "host"}};
// The codec type for the client-to-Envoy connection
Http::CodecClient::Type downstream_protocol_{Http::CodecClient::Type::HTTP1};
uint32_t max_request_headers_kb_{Http::DEFAULT_MAX_REQUEST_HEADERS_KB};
uint32_t max_request_headers_count_{Http::DEFAULT_MAX_HEADERS_COUNT};
std::string access_log_name_;
testing::NiceMock<Random::MockRandomGenerator> random_;
};
// Helper class for integration tests using raw HTTP/2 frames
class Http2RawFrameIntegrationTest : public HttpIntegrationTest {
public:
Http2RawFrameIntegrationTest(Network::Address::IpVersion version)
: HttpIntegrationTest(Http::CodecClient::Type::HTTP2, version) {}
protected:
void startHttp2Session();
Http2Frame readFrame();
void sendFrame(const Http2Frame& frame);
virtual void beginSession();
IntegrationTcpClientPtr tcp_client_;
};
} // namespace Envoy