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"
"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
// Obtain id for this connection
lastHasuraConnectionId ++
hasuraConnectionId := "HC" + fmt . Sprintf ( "%010d" , lastHasuraConnectionId )
2024-09-18 20:48:36 +08:00
browserConnection . Logger = browserConnection . Logger . WithField ( "hasuraConnectionId" , hasuraConnectionId )
defer browserConnection . Logger . 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 {
2024-10-23 23:19:01 +08:00
browserConnection . Logger . Infof ( "Closing browser connection because Hasura connection was closed, reason: %s" , thisConnection . WebsocketCloseError . Reason )
2024-05-30 04:43:17 +08:00
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
2024-09-18 20:48:36 +08:00
browserConnection . Logger . Info ( "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
}