Skip to content

Commit

Permalink
introduce graphql-transport-ws protocol (TT-8005) (TykTechnologies#5400)
Browse files Browse the repository at this point in the history
This PR adds the `graphql-transport-ws` websocket protocol for client
<-> GW communication.

## Types of changes

<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Refactoring or add test (improvements in base code or adds test
coverage to functionality)
  • Loading branch information
pvormste authored Aug 9, 2023
1 parent a46d876 commit 09046af
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 27 deletions.
8 changes: 3 additions & 5 deletions gateway/mw_graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/TykTechnologies/graphql-go-tools/pkg/engine/resolve"
"github.com/TykTechnologies/graphql-go-tools/pkg/execution/datasource"
gqlwebsocket "github.com/TykTechnologies/graphql-go-tools/pkg/subscription/websocket"

"github.com/TykTechnologies/tyk/apidef"
"github.com/TykTechnologies/tyk/apidef/adapter"
Expand All @@ -33,10 +34,6 @@ const (
TykGraphQLDataSource = "TykGraphQLDataSource"
)

const (
GraphQLWebSocketProtocol = "graphql-ws"
)

var (
ProxyingRequestFailedErr = errors.New("there was a problem proxying the request")
GraphQLDepthLimitExceededErr = errors.New("depth limit exceeded")
Expand Down Expand Up @@ -334,7 +331,8 @@ func (m *GraphQLMiddleware) writeGraphQLError(w http.ResponseWriter, errors gql.

func (m *GraphQLMiddleware) websocketUpgradeUsesGraphQLProtocol(r *http.Request) bool {
websocketProtocol := r.Header.Get(header.SecWebSocketProtocol)
return websocketProtocol == GraphQLWebSocketProtocol
return websocketProtocol == string(gqlwebsocket.ProtocolGraphQLWS) ||
websocketProtocol == string(gqlwebsocket.ProtocolGraphQLTransportWS)
}

func (m *GraphQLMiddleware) checkForUnsupportedUsage() error {
Expand Down
53 changes: 37 additions & 16 deletions gateway/mw_graphql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/TykTechnologies/tyk/user"

gql "github.com/TykTechnologies/graphql-go-tools/pkg/graphql"
gqlwebsocket "github.com/TykTechnologies/graphql-go-tools/pkg/subscription/websocket"

"github.com/TykTechnologies/tyk/test"
)
Expand Down Expand Up @@ -556,7 +557,7 @@ func TestGraphQLMiddleware_EngineMode(t *testing.T) {
cfg.HttpServerOptions.EnableWebSockets = true
g.Gw.SetConfig(cfg)

t.Run("should deny upgrade with 400 when protocol is not graphql-ws", func(t *testing.T) {
t.Run("should deny upgrade with 400 when protocol is not graphql-ws or graphql-transport-ws", func(t *testing.T) {
_, _ = g.Run(t, []test.TestCase{
{
Headers: map[string]string{
Expand All @@ -573,21 +574,41 @@ func TestGraphQLMiddleware_EngineMode(t *testing.T) {
})

t.Run("should upgrade to websocket connection with correct protocol", func(t *testing.T) {
wsConn, _, err := websocket.DefaultDialer.Dial(baseURL, map[string][]string{
header.SecWebSocketProtocol: {GraphQLWebSocketProtocol},
t.Run("graphql-ws", func(t *testing.T) {
wsConn, _, err := websocket.DefaultDialer.Dial(baseURL, map[string][]string{
header.SecWebSocketProtocol: {string(gqlwebsocket.ProtocolGraphQLWS)},
})
require.NoError(t, err)
defer wsConn.Close()

// Send a connection init message to gateway
err = wsConn.WriteMessage(websocket.BinaryMessage, []byte(`{"type":"connection_init","payload":{}}`))
require.NoError(t, err)

_, msg, err := wsConn.ReadMessage()

// Gateway should acknowledge the connection
assert.Equal(t, `{"type":"connection_ack"}`, string(msg))
assert.NoError(t, err)
})
require.NoError(t, err)
defer wsConn.Close()
t.Run("graphql-transport-ws", func(t *testing.T) {
wsConn, _, err := websocket.DefaultDialer.Dial(baseURL, map[string][]string{
header.SecWebSocketProtocol: {string(gqlwebsocket.ProtocolGraphQLTransportWS)},
})
require.NoError(t, err)
defer wsConn.Close()

// Send a connection init message to gateway
err = wsConn.WriteMessage(websocket.BinaryMessage, []byte(`{"type":"connection_init"}`))
require.NoError(t, err)

// Send a connection init message to gateway
err = wsConn.WriteMessage(websocket.BinaryMessage, []byte(`{"type":"connection_init","payload":{}}`))
require.NoError(t, err)
_, msg, err := wsConn.ReadMessage()

_, msg, err := wsConn.ReadMessage()
// Gateway should acknowledge the connection
assert.Equal(t, `{"type":"connection_ack"}`, string(msg))
assert.NoError(t, err)
})

// Gateway should acknowledge the connection
assert.Equal(t, `{"id":"","type":"connection_ack","payload":null}`, string(msg))
assert.NoError(t, err)
})

t.Run("graphql over websockets", func(t *testing.T) {
Expand All @@ -611,7 +632,7 @@ func TestGraphQLMiddleware_EngineMode(t *testing.T) {
})

wsConn, _, err := websocket.DefaultDialer.Dial(baseURL, map[string][]string{
header.SecWebSocketProtocol: {GraphQLWebSocketProtocol},
header.SecWebSocketProtocol: {string(gqlwebsocket.ProtocolGraphQLWS)},
header.Authorization: {directKey},
})
require.NoError(t, err)
Expand All @@ -624,7 +645,7 @@ func TestGraphQLMiddleware_EngineMode(t *testing.T) {
_, msg, err := wsConn.ReadMessage()

// Gateway should acknowledge the connection
require.Equal(t, `{"id":"","type":"connection_ack","payload":null}`, string(msg))
require.Equal(t, `{"type":"connection_ack"}`, string(msg))
require.NoError(t, err)

err = wsConn.WriteMessage(websocket.BinaryMessage, []byte(`{"id": "1", "type": "start", "payload": {"query": "{ countries { name } }", "variables": null}}`))
Expand All @@ -647,7 +668,7 @@ func TestGraphQLMiddleware_EngineMode(t *testing.T) {
})

wsConn, _, err := websocket.DefaultDialer.Dial(baseURL, map[string][]string{
header.SecWebSocketProtocol: {GraphQLWebSocketProtocol},
header.SecWebSocketProtocol: {string(gqlwebsocket.ProtocolGraphQLWS)},
header.Authorization: {directKey},
})
require.NoError(t, err)
Expand All @@ -660,7 +681,7 @@ func TestGraphQLMiddleware_EngineMode(t *testing.T) {
_, msg, err := wsConn.ReadMessage()

// Gateway should acknowledge the connection
require.Equal(t, `{"id":"","type":"connection_ack","payload":null}`, string(msg))
require.Equal(t, `{"type":"connection_ack"}`, string(msg))
require.NoError(t, err)

err = wsConn.WriteMessage(websocket.BinaryMessage, []byte(`{"id": "1", "type": "start", "payload": {"query": "{ countries { name } }", "variables": null}}`))
Expand Down
13 changes: 10 additions & 3 deletions gateway/reverse_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ import (
"golang.org/x/net/http2"

"github.com/TykTechnologies/graphql-go-tools/pkg/graphql"
gqlhttp "github.com/TykTechnologies/graphql-go-tools/pkg/http"
"github.com/TykTechnologies/graphql-go-tools/pkg/subscription"
gqlwebsocket "github.com/TykTechnologies/graphql-go-tools/pkg/subscription/websocket"

"github.com/TykTechnologies/tyk/apidef"
"github.com/TykTechnologies/tyk/ctx"
Expand Down Expand Up @@ -1058,7 +1058,7 @@ func (p *ReverseProxy) handleGraphQLIntrospection(gqlRequest *graphql.Request) (

func (p *ReverseProxy) handleGraphQLEngineWebsocketUpgrade(roundTripper *TykRoundTripper, r *http.Request, w http.ResponseWriter) (res *http.Response, hijacked bool, err error) {
conn, err := p.wsUpgrader.Upgrade(w, r, http.Header{
header.SecWebSocketProtocol: {GraphQLWebSocketProtocol},
header.SecWebSocketProtocol: {r.Header.Get(header.SecWebSocketProtocol)},
})
if err != nil {
p.logger.Error("websocket upgrade for GraphQL engine failed: ", err)
Expand Down Expand Up @@ -1233,7 +1233,14 @@ func (p *ReverseProxy) handoverWebSocketConnectionToGraphQLExecutionEngine(round
executorPool = subscription.NewExecutorV2Pool(p.TykAPISpec.GraphQLExecutor.EngineV2, initialRequestContext)
}

go gqlhttp.HandleWebsocket(done, errChan, conn, executorPool, absLogger)
go gqlwebsocket.Handle(
done,
errChan,
conn,
executorPool,
gqlwebsocket.WithLogger(absLogger),
gqlwebsocket.WithProtocolFromRequestHeaders(req),
)
select {
case err := <-errChan:
log.Error("could not start graphql websocket handler: ", err)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
github.com/TykTechnologies/gojsonschema v0.0.0-20170222154038-dcb3e4bb7990
github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9
github.com/TykTechnologies/goverify v0.0.0-20220808203004-1486f89e7708
github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230807142628-f787e7ab594e
github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230809131244-801d4374ae26
github.com/TykTechnologies/leakybucket v0.0.0-20170301023702-71692c943e3c
github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632
github.com/TykTechnologies/openid2go v0.1.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9/go.mod h1:v6
github.com/TykTechnologies/goverify v0.0.0-20220808203004-1486f89e7708 h1:cmXjlMzcexhc/Cg+QB/c2CPUVs1ux9xn6162qaf/LC4=
github.com/TykTechnologies/goverify v0.0.0-20220808203004-1486f89e7708/go.mod h1:mkS8jKcz8otdfEXhJs1QQ/DKoIY1NFFsRPKS0RwQENI=
github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230320143102-7a16078ce517/go.mod h1:ZiFZcrue3+n2mHH+KLHRipbYVULkgy3Myko5S7IIs74=
github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230807142628-f787e7ab594e h1:5iMgqxmb7ZobBi0LNPKbQSnLxmaLCp37DXigeXbVtgI=
github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230807142628-f787e7ab594e/go.mod h1:ZWpfrRjWhBPryIMCDK8kAew4hji0QUQxj3Uhc2COlMU=
github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230809131244-801d4374ae26 h1:LvDrPzO0PLEAlmaO+6WjdYBLzTKXCxGlW0gkuAzJG54=
github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20230809131244-801d4374ae26/go.mod h1:ZWpfrRjWhBPryIMCDK8kAew4hji0QUQxj3Uhc2COlMU=
github.com/TykTechnologies/kin-openapi v0.90.0 h1:kHw0mtANwIpmlU6eCeeCgRMa52EiPPhEZPuHc3lKawo=
github.com/TykTechnologies/kin-openapi v0.90.0/go.mod h1:pkzuiceujHvAuDu3bTD/AD5OacuP4eMfrz9QhJlMvdQ=
github.com/TykTechnologies/leakybucket v0.0.0-20170301023702-71692c943e3c h1:j6fd0Fz1R4oSWOmcooGjrdahqrML+btQ+PfEJw8SzbA=
Expand Down

0 comments on commit 09046af

Please sign in to comment.