Skip to content
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

Merged
merged 63 commits into from
Aug 8, 2017
Merged

Websocket support #1274

merged 63 commits into from
Aug 8, 2017

Conversation

rshriram
Copy link
Member

@rshriram rshriram commented Jul 18, 2017

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

@rshriram
Copy link
Member Author

rshriram commented Aug 2, 2017

@mattklein123 @dnoe addressed comments.

@dnoe
Copy link
Contributor

dnoe commented Aug 3, 2017

Will take a look today, thanks.

Copy link
Contributor

@dnoe dnoe left a 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) {
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Member

@mattklein123 mattklein123 left a 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.

struct UpstreamHelperCallbacks {
virtual ~UpstreamHelperCallbacks() {}

virtual const std::string& getUpstreamCluster() PURE;
Copy link
Member

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();
Copy link
Member

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();
Copy link
Member

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(
Copy link
Member

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,
Copy link
Member

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 {
Copy link
Member

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{};
Copy link
Member

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(){};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: del, not needed.

Copy link
Member

@mattklein123 mattklein123 left a 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());
}
Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member

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.

@@ -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);
Copy link
Member

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."

Copy link
Member

@mattklein123 mattklein123 left a 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?

Copy link
Contributor

@dnoe dnoe left a 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

@mattklein123 mattklein123 merged commit 8c42f16 into envoyproxy:master Aug 8, 2017
@mattklein123 mattklein123 mentioned this pull request Aug 8, 2017
@rshriram rshriram deleted the envoy-websocket branch August 14, 2017 18:10
jpsim pushed a commit that referenced this pull request Nov 28, 2022
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>
jpsim pushed a commit that referenced this pull request Nov 29, 2022
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants