Gqlgen: Could not connect to websocket

Created on 19 Mar 2019  路  21Comments  路  Source: 99designs/gqlgen

Could not connect to websocket endpoint wss://127.0.0.1:4000/api/query. Please check if the endpoint url is correct.

Im using mux.

resolver.go

var TicketSub struct {
    sync.RWMutex
    Subscribers map[string]chan *gqlapi.TicketStatusChangePayload
}

func (r *subscriptionRootResolver) TicketStatusChanged(ctx context.Context) (<-chan *gqlapi.TicketStatusChangePayload, error) {
    user := &models.User{
        UUID: "e70e78bb-9d08-405d-a0ed-266ec703de19",
    }
    events := make(chan *gqlapi.TicketStatusChangePayload, 1)

    go func() {
        <-ctx.Done()
        TicketSub.Lock()
        delete(TicketSub.Subscribers, user.UUID)
        TicketSub.Unlock()
    }()

    TicketSub.Lock()
    TicketSub.Subscribers[user.UUID] = events
    TicketSub.Unlock()

    return events, nil
}

func (r *queryRootResolver) SubscriptionTest(ctx context.Context) (err error) {
    id := uuid.New().String()
    notifyTicketStatusChange("e70e78bb-9d08-405d-a0ed-266ec703de19", id, fmt.Sprintf("Ticket (uuid=%s) status changed.", id))
    return
}

main.go

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    Start(ctx)

    // Wait for interrupt signal to gracefully shutdown the server with
    // a timeout of 10 seconds.
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs,
        os.Kill,
        os.Interrupt,
        syscall.SIGHUP,
        syscall.SIGINT,
        syscall.SIGTERM,
        syscall.SIGQUIT)
    <-sigs

    defer func() {
        cancel()
    }()

}

func Start(ctx context.Context) {
    go start(ctx)
}

func start(ctx context.Context) {
    cfg := gqlapi.Config{
        Resolvers: &resolvers.Resolver{},
        Directives: gqlapi.DirectiveRoot{
            Auth:    directives.Auth,
        },
    }
    r := mux.NewRouter()
    r.HandleFunc("/", handler.Playground("GraphQL playground", "/api/query"))

    upgrader := websocket.Upgrader{
        CheckOrigin: func(r *http.Request) bool {
            return true
        },
        EnableCompression: true,
    }

    options := []handler.Option{
        handler.RecoverFunc(func(ctx context.Context, err interface{}) error {
            // notify bug tracker...
            return fmt.Errorf("Internel server error.")
        }),
        handler.WebsocketUpgrader(upgrader),
    }

    r.HandleFunc("/api/query", handler.GraphQL(
        gqlapi.NewExecutableSchema(cfg),
        options...,
    ))
    srv := &http.Server{
        Handler: r,              //
        Addr:    "0.0.0.0:4000", //
    }
    srv.SetKeepAlivesEnabled(true)
    log.Fatal(srv.ListenAndServeTLS("cert.pem", "key.pem"))
}

Screenshot from 2019-03-19 18 56 02

I can push message via websocket tunnel as you can see on the attached screenshot, but it closed after about 10-15 seconds.

v0.9.2

Most helpful comment

I solved the issue by lowering down the keepalive interval to 10s handler.WebsocketKeepAliveDuration(10*time.Second). The option name is a bit confusing. Probably, we should name it as WebsocketKeepAlivePingInterval instead?

All 21 comments

I am seeing the same issue. Has there been any update about this issue?

Hmm, and if you play with WebsocketKeepAliveDuration option? (Just by curiosity)

https://github.com/99designs/gqlgen/pull/529/files

I tried playing with that function, but it seems to have the same behavior no matter what I set as the duration. I tried 40 then 100 (seconds) and got the same result: after about 30 seconds it would lose the connection after sending a 'ka' message (I saw the 'ka' in the messages section in the inspector for the request).

Has anyone figured this out? I'm having the same issue. After 1 minute if no messages transmitted the error pops out:

Could not connect to websocket endpoint ws://localhost:5000/query. Please check if the endpoint url is correct.

I was not able to figure it out, from the code it looks like it should work, but it seems to timeout. I had to setup my own Websocket handling code using Gorilla Websocket library, while it would have been nice to use subscriptions with the GQLGen library, using the Gorilla Websocket library is working out well so far.

I've figured out that the problem with my code was having WebSocket and GraphQL handler on the same URL pattern.
My current hack is "duplicating" the ExecutableSchema on both HTTP and WS URL pattern. That works as expected. I'll probably have to refactor the code to fit this setting a bit it looks like a keeper.

My current code:

router.Handle("/", handler.Playground("GraphQL playground", "/query"))
    router.Handle("/query", handler.GraphQL(mailio.NewExecutableSchema(mailio.New(dtableSDK, awsSMTPSession, emailObserver, serverID))))
    router.Handle("/ws", handler.GraphQL(mailio.NewExecutableSchema(mailio.New(dtableSDK, awsSMTPSession, emailObserver, serverID)), handler.WebsocketUpgrader(websocket.Upgrader{
        CheckOrigin: func(r *http.Request) bool {
            return true
        },
    })))

I didn't dive into GqlGen code or Gorilla code but it might be Gorilla problem. Should be fairly easy to check.
In fact, I'm not even sure the same URL pattern with HTTP and WS should work by design?

const DefaultConnectionKeepAlivePingInterval = 25 * time.Second

It's not the problem with gqlgen. I played with WebsocketKeepAliveDuration and in the case of WebsocketKeepAliveDuration greater than 20 seconds, the above error occurred. For example, for 15 seconds everything works as it should.
screen

It's some protocol details that needs to be sorted.

I could not use github.com/alexanderGugel/wsd but curl worked fine:

curl -i --no-buffer -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Host: localhost:8080" -H "Origin: http://localhost:8080" -H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" -H "Sec-WebSocket-Version: 13" localhost:8080/graphql
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: qGEgH3En71di5rrssAZTmtRTyFk=
Sec-WebSocket-Protocol: graphql-ws

I solved the issue by lowering down the keepalive interval to 10s handler.WebsocketKeepAliveDuration(10*time.Second). The option name is a bit confusing. Probably, we should name it as WebsocketKeepAlivePingInterval instead?

This can be solved by setting WebsocketKeepAliveDuration = 0.It works fine with local.

     queryHandler := corsAccess(handler.GraphQL(graph.NewExecutableSchema(
     handler.WebsocketKeepAliveDuration(0),
     handler.WebsocketUpgrader(websocket.Upgrader{
         CheckOrigin: func(request *http.Request) bool {
            return true
        },
        HandshakeTimeout: 5 * time.Second,
    }),
          ...
     )).

Still, get the issue in secure connection(i.e. Not work in wss://connection-URL)

I think this was fixed by https://github.com/99designs/gqlgen/pull/820. Please reopen if not

Seems like its still happening.

Testing with the chat example (not Gin), anything higher than handler.WebsocketKeepAliveDuration(19*time.Second) leads to the websocket connection being dropped in the GraphQL Playground.

Testing with my own app (Gin), I have the same experience.

It seems you need handler.WebsocketKeepAliveDuration() to be passed something less than 20 seconds in order to keep the websocket connection alive. Does gqlgen have to implement its own websocket ping/pong logic? I'm surprised this hasn't caused lots of issues for anyone trying to do subscriptions? Do others have the same experience I report above?

Could you please add a recipe in https://gqlgen.com/ on how to use with Gin and gqlgen for subscriptions

I'm on 0.10.2 and I still need the option handler.WebsocketKeepAliveDuration(10*time.Second) to get subscriptions to not die after a few seconds (using Playground). Can we re-open this?

Could you please add a recipe in https://gqlgen.com/ on how to use with Gin and gqlgen for subscriptions

could you please update on this

I might have a similar issue #1150. It seems like when it's time to write a message, gorilla websockets returns an error indicating websocket: close sent but the client never sent a close and I believe gqlgen didn't either.

Same issue here. I've tried a few of the above solutions, and what worked for me was handler.WebsocketKeepAliveDuration(10*time.Second).

Would probably be a good idea to update this.

Had same issue, only the following work-around works. We need to have a better solution:

handler.GraphQL(
        generated.NewExecutableSchema(generated.Config{Resolvers: r}),
        handler.WebsocketKeepAliveDuration(10*time.Second),
    )

Hi. The same problem. I have middleware which go to user service and auth user by token.
If this middleware disabled(middleware returns hardcoded user) - subscription works well.
If "time.Sleep(1 * time.Second)" added to middleware - subscription not works.

gqlgen version v0.11.3
server.AddTransport(transport.Websocket{ KeepAlivePingInterval: 10 * time.Second, })

{ "error": "Could not connect to websocket endpoint ws://localhost:9090/graphql. Please check if the endpoint url is correct." }

Gqlgen can't do much about it. Playground (used by gqlgen) uses a subscription client from this package. It has built-in inactivity timeout (for prisma playground it's 20 seconds).

The only way to fix it is to set KeepAlivePingInterval between 1 and 15 seconds.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lynntobing picture lynntobing  路  3Comments

theoks picture theoks  路  3Comments

steebchen picture steebchen  路  3Comments

cemremengu picture cemremengu  路  3Comments

andrewmunro picture andrewmunro  路  4Comments