-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Websocket support #1274
Websocket support #1274
Conversation
…ng test temporarily
Initial implementation of WsHandlerImpl without WsHandler interface. Still missing the initial header encode.
@mattklein123 @dnoe addressed comments. |
Will take a look today, thanks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, thanks for the TCP proxy refactoring. This is a big PR and my eyes are glazing over by the end of it, I'll try and make one more pass for nits in a bit. Otherwise looks good to me pending @htuch and @mattklein123 approval for final review.
connection_manager_.ws_connection_->initializeReadFilterCallbacks( | ||
*connection_manager_.read_callbacks_); | ||
if (connection_manager_.ws_connection_->onNewConnection() == | ||
Network::FilterStatus::Continue) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an error case here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the onNewConnection will send the error response back to caller via the WsHandlerCallbacks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rshriram nice work this looks great. Few random comments then I think we are ready to go.
source/common/filter/tcp_proxy.h
Outdated
struct UpstreamHelperCallbacks { | ||
virtual ~UpstreamHelperCallbacks() {} | ||
|
||
virtual const std::string& getUpstreamCluster() PURE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason that we need to have a new object here that we allocate? Can these just be normal virtual functions that get overriden by the WsHandlerImpl derived class? That would save the extra allocation which AFAICT isn't needed?
@@ -91,7 +93,11 @@ ConnectionManagerImpl::~ConnectionManagerImpl() { | |||
if (codec_->protocol() == Protocol::Http2) { | |||
stats_.named_.downstream_cx_http2_active_.dec(); | |||
} else { | |||
stats_.named_.downstream_cx_http1_active_.dec(); | |||
if (isWebSocketConnection()) { | |||
stats_.named_.downstream_cx_websocket_active_.dec(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optimally, you would verify this stat dance in your integration tests. Can you make sure that the gauges are correct at various points?
// should return 404. The current returns no response if there is no router filter. | ||
if ((protocol == Protocol::Http11) && cached_route_.value()) { | ||
const Router::RouteEntry* route_entry = cached_route_.value()->routeEntry(); | ||
bool websocket_required = (route_entry != nullptr) && route_entry->useWebSocket(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const for websocket_required and websocket_requested
|
||
connection_manager_.ws_connection_.reset(new WebSocket::WsHandlerImpl( | ||
*request_headers_, *route_entry, *this, connection_manager_.cluster_manager_)); | ||
connection_manager_.ws_connection_->initializeReadFilterCallbacks( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Can this be done as part of the WsHandlerImpl constructor?
*/ | ||
class WsHandlerImpl : public Filter::TcpProxy { | ||
public: | ||
WsHandlerImpl(Http::HeaderMap& request_headers, const Router::RouteEntry& route_entry, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Since you are in Http namespace, you shouldn't need Http::
in all/most places in this file and cc file. Can you remove?
} | ||
|
||
protected: | ||
struct WsUpstreamHelperCallbacks : public UpstreamHelperCallbacks { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
per other comment I think these can just be virtual overrides.
private: | ||
struct NullHttpConnectionCallbacks : public Http::ConnectionCallbacks { | ||
// Http::ConnectionCallbacks | ||
void onGoAway() override{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: remove ';' at end of line
public: | ||
WsHandlerImpl(Http::HeaderMap& request_headers, const Router::RouteEntry& route_entry, | ||
WsHandlerCallbacks& callbacks, Upstream::ClusterManager& cluster_manager); | ||
~WsHandlerImpl(){}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: del, not needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small test request, otherwise very nice work and LGTM. @dnoe @htuch @alyssawilk any further comments?
EXPECT_EQ(1U, stats_.named_.downstream_cx_websocket_active_.value()); | ||
EXPECT_EQ(1U, stats_.named_.downstream_cx_websocket_total_.value()); | ||
EXPECT_EQ(0U, stats_.named_.downstream_cx_http1_active_.value()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you reset() conn_manager_ here can you then verify the gauges are zero? Same below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isn't working. Getting assertion error from ActiveStream in tsan and asan.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know what the problem is off the top of my head. As long as you have coverage in the integration tests which it looks like you added, that's fine.
test/integration/server.h
Outdated
@@ -199,6 +199,10 @@ class IntegrationTestServer : Logger::Loggable<Logger::Id::testing>, | |||
return TestUtility::findCounter(*stat_store_, name); | |||
} | |||
|
|||
Stats::GaugeSharedPtr gauge(const std::string& name) { | |||
return TestUtility::findGauge(*stat_store_, name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same comment applies here as to counter() above. Can you add a similar comment or just have a comment that says something to the effect of "See counter() above for why this method exists."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready to ship IMO. @dnoe any further comments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took a quick final pass, looks good to me. Many thanks for the hard work @rshriram
As per #1274 (comment), this patch is no longer required and as per offline discussion it doesn't seem effective. Signed-off-by: Ryan Hamilton <rch@google.com> Signed-off-by: JP Simard <jp@jpsim.com>
As per #1274 (comment), this patch is no longer required and as per offline discussion it doesn't seem effective. Signed-off-by: Ryan Hamilton <rch@google.com> Signed-off-by: JP Simard <jp@jpsim.com>
This PR adds WebSocket support to Envoy.
The main implementation is fairly complete. I have tested this code with a simple python based websocket client talking to echo.websocket.org (both 80 and 443) and it works :). I would appreciate any feedback on the code.
Tests are TODO. Finding it difficult, as this requires part HTTP mocks and part TCP mocks. Pointers appreciated.
Need to test interaction of WebSockets in the presence of RDS (cc @kyessenov @GregHanson @ijsnellf)
cc @mattklein123 @htuch @PiotrSikora