Skip to content

Commit

Permalink
non-expiring cookies
Browse files Browse the repository at this point in the history
  • Loading branch information
nl0 committed Apr 25, 2024
1 parent 831c446 commit 6f312c0
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 54 deletions.
6 changes: 2 additions & 4 deletions catalog/app/components/Preview/loaders/Html/Html.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ function useCreateSession() {
})
switch (r.__typename) {
case 'BrowsingSession':
await ensureCookie(SESSION_TTL, REFRESH_INTERVAL)
await ensureCookie()
return r
case 'OperationError':
case 'InvalidInput':
Expand All @@ -120,14 +120,12 @@ function useCreateSession() {

function useRefreshSession() {
const refreshSession = GQL.useMutation(REFRESH_BROWSING_SESSION)
const ensureCookie = useEnsurePFSCookie()
return React.useCallback(
async (id: SessionId | null) => {
if (!id) return
const { browsingSessionRefresh: r } = await refreshSession({ id, ttl: SESSION_TTL })
switch (r.__typename) {
case 'BrowsingSession':
await ensureCookie(SESSION_TTL, REFRESH_INTERVAL)
return
case 'OperationError':
case 'InvalidInput':
Expand All @@ -136,7 +134,7 @@ function useRefreshSession() {
assertNever(r)
}
},
[refreshSession, ensureCookie],
[refreshSession],
)
}

Expand Down
89 changes: 39 additions & 50 deletions catalog/app/utils/PFSCookieManager.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as dateFns from 'date-fns'
import invariant from 'invariant'
import * as React from 'react'
import * as redux from 'react-redux'
Expand All @@ -8,7 +7,7 @@ import cfg from 'constants/config'
import { tokens as tokensSelector } from 'containers/Auth/selectors'
import { useApi } from 'utils/APIConnector'

type Ensure = (ttl: number, histeresis: number) => Promise<void>
type Ensure = () => Promise<void>

const Ctx = React.createContext<Ensure | null>(null)

Expand All @@ -19,65 +18,62 @@ const selectToken = createSelector(

interface State {
token: string | undefined
expires: Date
promise: Promise<void>
}

function needsRefresh(
state: State | undefined,
params: {
token: string | undefined
expires: Date
histeresis: number
},
): boolean {
if (!state) return true
if (state.token !== params.token) return true
if (state.expires.getTime() + params.histeresis < params.expires.getTime()) return true
return false
lastRequest: Promise<void>
state: 'pending' | 'success' | 'error'
}

export function PFSCookieManager({ children }: React.PropsWithChildren<{}>) {
const stateRef = React.useRef<State>()

const req = useApi()
const req: (opts: any) => Promise<void> = useApi()

const setPFSCookie = React.useCallback(
(token: string | undefined, ttl: number): Promise<void> =>
token
(token: string | undefined) => {
const cookieRequest = token
? req({
auth: { tokens: { token }, handleInvalidToken: false },
url: `${cfg.s3Proxy}/browse/set_browse_cookie`,
method: 'POST',
credentials: 'include',
body: { ttl },
}).catch((e: any) => {
throw new Error(`Could not set PFS cookie: ${e.message}`)
})
: Promise.resolve(),
[req],
: Promise.resolve()

stateRef.current = { token, lastRequest: cookieRequest, state: 'pending' }

cookieRequest
.then(
() => 'success' as const,
() => 'error' as const,
)
.then((state) => {
// update the state only if it corresponds to this request
// (concurrent request has not been issued in the meantime)
if (stateRef.current?.lastRequest === cookieRequest) {
stateRef.current.state = state
}
})
},
[req, stateRef],
)

const store = redux.useStore()

const ensure = React.useCallback<Ensure>(
async (ttl: number, histeresis: number) => {
const expires = dateFns.addSeconds(new Date(), ttl)
const ensure = React.useCallback<Ensure>(async () => {
// issue a new request if not initialized or if previous request failed
if (!stateRef.current || stateRef.current.state === 'error') {
const token = selectToken(store.getState())
if (needsRefresh(stateRef.current, { token, expires, histeresis })) {
const promise = setPFSCookie(token, ttl)
stateRef.current = { token, expires, promise }
}
while (true) {
// if a new request has been issued while waiting for the response,
// wait for it to complete
let promise = stateRef.current?.promise
if (promise) await promise
if (promise === stateRef.current?.promise) return
}
},
[store, stateRef, setPFSCookie],
)
setPFSCookie(token)
}

// wait for the state to stabilize in case another request(s)
// issued concurrently by the update logic
while (stateRef.current?.state === 'pending') {
await stateRef.current.lastRequest.catch(() => {})
}
return stateRef.current?.lastRequest
}, [store, stateRef, setPFSCookie])

// refresh cookie on token change
React.useEffect(
Expand All @@ -91,15 +87,8 @@ export function PFSCookieManager({ children }: React.PropsWithChildren<{}>) {
// bail if token hasn't changed
if (state.token === token) return

const ttlLeft = Math.ceil((state.expires.getTime() - Date.now()) / 1000)
if (ttlLeft <= 0) {
// cookie expired (noone's using it) -- cleanup state and bail
stateRef.current = undefined
return
}

state.token = token
state.promise = setPFSCookie(token, ttlLeft)
// set new cookie and replace the stored request
setPFSCookie(token)
}),
[store, stateRef, setPFSCookie],
)
Expand Down

0 comments on commit 6f312c0

Please sign in to comment.