2024-05-02 21:45:32 +08:00
|
|
|
package hasura
|
2023-04-27 09:03:40 +08:00
|
|
|
|
|
|
|
import (
|
2024-08-30 05:40:52 +08:00
|
|
|
"bbb-graphql-middleware/config"
|
2024-08-09 03:50:41 +08:00
|
|
|
"bbb-graphql-middleware/internal/hasura/conn/reader"
|
|
|
|
"bbb-graphql-middleware/internal/hasura/conn/writer"
|
2023-04-27 09:03:40 +08:00
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"math"
|
|
|
|
"net/http"
|
|
|
|
"net/http/cookiejar"
|
|
|
|
"net/url"
|
|
|
|
"sync"
|
|
|
|
|
2024-08-09 03:50:41 +08:00
|
|
|
"bbb-graphql-middleware/internal/common"
|
2023-04-27 09:03:40 +08:00
|
|
|
"golang.org/x/xerrors"
|
|
|
|
"nhooyr.io/websocket"
|
|
|
|
)
|
|
|
|
|
|
|
|
var lastHasuraConnectionId int
|
2024-08-30 05:40:52 +08:00
|
|
|
var hasuraEndpoint = config.GetConfig().Hasura.Url
|
2023-04-27 09:03:40 +08:00
|
|
|
|
|
|
|
// Hasura client connection
|
2024-05-30 04:43:17 +08:00
|
|
|
func HasuraClient(
|
2024-07-06 00:35:08 +08:00
|
|
|
browserConnection *common.BrowserConnection) error {
|
2023-04-27 09:03:40 +08:00
|
|
|
log := log.WithField("_routine", "HasuraClient").WithField("browserConnectionId", browserConnection.Id)
|
|
|
|
|
|
|
|
// Obtain id for this connection
|
|
|
|
lastHasuraConnectionId++
|
|
|
|
hasuraConnectionId := "HC" + fmt.Sprintf("%010d", lastHasuraConnectionId)
|
|
|
|
log = log.WithField("hasuraConnectionId", hasuraConnectionId)
|
|
|
|
|
2023-09-07 22:54:27 +08:00
|
|
|
defer log.Debugf("finished")
|
2023-04-27 09:03:40 +08:00
|
|
|
|
|
|
|
// Add sub-protocol
|
|
|
|
var dialOptions websocket.DialOptions
|
2024-05-23 02:51:12 +08:00
|
|
|
dialOptions.Subprotocols = append(dialOptions.Subprotocols, "graphql-transport-ws")
|
2023-04-27 09:03:40 +08:00
|
|
|
|
|
|
|
// Create cookie jar
|
|
|
|
jar, err := cookiejar.New(nil)
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("failed to create cookie jar: %w", err)
|
|
|
|
}
|
|
|
|
parsedURL, err := url.Parse(hasuraEndpoint)
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("failed to parse url: %w", err)
|
|
|
|
}
|
|
|
|
parsedURL.Scheme = "http"
|
2024-05-30 04:43:17 +08:00
|
|
|
jar.SetCookies(parsedURL, browserConnection.BrowserRequestCookies)
|
2023-04-27 09:03:40 +08:00
|
|
|
hc := &http.Client{
|
|
|
|
Jar: jar,
|
|
|
|
}
|
|
|
|
dialOptions.HTTPClient = hc
|
|
|
|
|
|
|
|
// Create a context for the hasura connection, that depends on the browser context
|
|
|
|
// this means that if browser connection is closed, the hasura connection will close also
|
|
|
|
// this also means that we can close the hasura connection without closing the browser one
|
|
|
|
// this is used for the invalidation process (reconnection only on the hasura side )
|
|
|
|
var hasuraConnectionContext, hasuraConnectionContextCancel = context.WithCancel(browserConnection.Context)
|
|
|
|
defer hasuraConnectionContextCancel()
|
|
|
|
|
|
|
|
var thisConnection = common.HasuraConnection{
|
2024-07-06 00:35:08 +08:00
|
|
|
Id: hasuraConnectionId,
|
|
|
|
BrowserConn: browserConnection,
|
|
|
|
Context: hasuraConnectionContext,
|
|
|
|
ContextCancelFunc: hasuraConnectionContextCancel,
|
2023-04-27 09:03:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
browserConnection.HasuraConnection = &thisConnection
|
2024-01-24 07:20:16 +08:00
|
|
|
defer func() {
|
2024-05-30 04:43:17 +08:00
|
|
|
//When Hasura sends an CloseError, it will forward the error to the browser and close the connection
|
|
|
|
if thisConnection.WebsocketCloseError != nil {
|
|
|
|
browserConnection.Websocket.Close(thisConnection.WebsocketCloseError.Code, thisConnection.WebsocketCloseError.Reason)
|
|
|
|
browserConnection.ContextCancelFunc()
|
|
|
|
}
|
|
|
|
|
2024-01-24 07:20:16 +08:00
|
|
|
browserConnection.HasuraConnection = nil
|
|
|
|
|
|
|
|
//It's necessary to freeze the channel to avoid client trying to start subscriptions before Hasura connection is initialised
|
|
|
|
//It will unfreeze after `connection_ack` is sent by Hasura
|
2024-07-06 00:35:08 +08:00
|
|
|
browserConnection.FromBrowserToHasuraChannel.FreezeChannel()
|
2024-01-24 07:20:16 +08:00
|
|
|
}()
|
2023-04-27 09:03:40 +08:00
|
|
|
|
|
|
|
// Make the connection
|
2024-05-30 04:43:17 +08:00
|
|
|
hasuraWsConn, _, err := websocket.Dial(hasuraConnectionContext, hasuraEndpoint, &dialOptions)
|
2023-04-27 09:03:40 +08:00
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("error connecting to hasura: %v", err)
|
|
|
|
}
|
2024-05-30 04:43:17 +08:00
|
|
|
defer hasuraWsConn.Close(websocket.StatusInternalError, "the sky is falling")
|
2023-04-27 09:03:40 +08:00
|
|
|
|
2024-05-30 04:43:17 +08:00
|
|
|
hasuraWsConn.SetReadLimit(math.MaxInt64 - 1)
|
2023-04-27 09:03:40 +08:00
|
|
|
|
2024-05-30 04:43:17 +08:00
|
|
|
thisConnection.Websocket = hasuraWsConn
|
2023-04-27 09:03:40 +08:00
|
|
|
|
|
|
|
// Log the connection success
|
2023-09-07 22:54:27 +08:00
|
|
|
log.Debugf("connected with Hasura")
|
2023-04-27 09:03:40 +08:00
|
|
|
|
|
|
|
// Configure the wait group
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(2)
|
|
|
|
|
|
|
|
// Start routines
|
|
|
|
|
|
|
|
// reads from browser, writes to hasura
|
2024-07-06 00:35:08 +08:00
|
|
|
go writer.HasuraConnectionWriter(&thisConnection, &wg, browserConnection.ConnectionInitMessage)
|
2023-04-27 09:03:40 +08:00
|
|
|
|
|
|
|
// reads from hasura, writes to browser
|
2024-07-06 00:35:08 +08:00
|
|
|
go reader.HasuraConnectionReader(&thisConnection, &wg)
|
2023-04-27 09:03:40 +08:00
|
|
|
|
|
|
|
// Wait
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|