bigbluebutton-Github/bbb-graphql-middleware/internal/hasura/client.go

131 lines
4.5 KiB
Go
Raw Normal View History

package hasura
2023-04-27 09:03:40 +08:00
import (
"context"
"fmt"
"github.com/iMDT/bbb-graphql-middleware/internal/hasura/conn/reader"
"github.com/iMDT/bbb-graphql-middleware/internal/hasura/conn/writer"
2023-04-27 09:03:40 +08:00
log "github.com/sirupsen/logrus"
"math"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
2023-04-27 09:03:40 +08:00
"sync"
"github.com/iMDT/bbb-graphql-middleware/internal/common"
"golang.org/x/xerrors"
"nhooyr.io/websocket"
)
var lastHasuraConnectionId int
var hasuraEndpoint = os.Getenv("BBB_GRAPHQL_MIDDLEWARE_HASURA_WS")
2023-04-27 09:03:40 +08:00
// Hasura client connection
func HasuraClient(
browserConnection *common.BrowserConnection,
fromBrowserToHasuraChannel *common.SafeChannelByte,
fromHasuraToBrowserChannel *common.SafeChannelByte) error {
2023-04-27 09:03:40 +08:00
log := log.WithField("_routine", "HasuraClient").WithField("browserConnectionId", browserConnection.Id)
common.ActivitiesOverviewStarted("__HasuraConnection")
defer common.ActivitiesOverviewCompleted("__HasuraConnection")
defer func() {
//Remove subscriptions from ActivitiesOverview here once Hasura-Reader will ignore "complete" msg for them
browserConnection.ActiveSubscriptionsMutex.RLock()
for _, subscription := range browserConnection.ActiveSubscriptions {
common.ActivitiesOverviewCompleted(string(subscription.Type) + "-" + subscription.OperationName)
common.ActivitiesOverviewCompleted("_Sum-" + string(subscription.Type))
}
browserConnection.ActiveSubscriptionsMutex.RUnlock()
}()
2023-04-27 09:03:40 +08:00
// Obtain id for this connection
lastHasuraConnectionId++
hasuraConnectionId := "HC" + fmt.Sprintf("%010d", lastHasuraConnectionId)
log = log.WithField("hasuraConnectionId", hasuraConnectionId)
defer log.Debugf("finished")
2023-04-27 09:03:40 +08:00
// Add sub-protocol
var dialOptions websocket.DialOptions
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"
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-03-13 21:35:51 +08:00
Id: hasuraConnectionId,
BrowserConn: browserConnection,
Context: hasuraConnectionContext,
ContextCancelFunc: hasuraConnectionContextCancel,
FreezeMsgFromBrowserChan: common.NewSafeChannel(1),
2023-04-27 09:03:40 +08:00
}
browserConnection.HasuraConnection = &thisConnection
defer func() {
//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()
}
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
fromBrowserToHasuraChannel.FreezeChannel()
}()
2023-04-27 09:03:40 +08:00
// Make the connection
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)
}
defer hasuraWsConn.Close(websocket.StatusInternalError, "the sky is falling")
2023-04-27 09:03:40 +08:00
hasuraWsConn.SetReadLimit(math.MaxInt64 - 1)
2023-04-27 09:03:40 +08:00
thisConnection.Websocket = hasuraWsConn
2023-04-27 09:03:40 +08:00
// Log the connection success
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
go writer.HasuraConnectionWriter(&thisConnection, fromBrowserToHasuraChannel, &wg, browserConnection.ConnectionInitMessage)
2023-04-27 09:03:40 +08:00
// reads from hasura, writes to browser
go reader.HasuraConnectionReader(&thisConnection, fromHasuraToBrowserChannel, fromBrowserToHasuraChannel, &wg)
2023-04-27 09:03:40 +08:00
// Wait
wg.Wait()
return nil
}