Skip to content

Commit

Permalink
fix(refreshing-endpoint): Add requests to a queue, if a request with …
Browse files Browse the repository at this point in the history
…the same body is queued, discard it
  • Loading branch information
Daniel Aparicio committed Jun 10, 2020
1 parent 221481b commit 2f6a0e5
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 12 deletions.
27 changes: 15 additions & 12 deletions packages/gatsby/src/commands/develop-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,13 +231,15 @@ async function startServer(program: IProgram): Promise<IServer> {
)
)

/**
* Refresh external data sources.
* This behavior is disabled by default, but the ENABLE_GATSBY_REFRESH_ENDPOINT env var enables it
* If no GATSBY_REFRESH_TOKEN env var is available, then no Authorization header is required
**/
const REFRESH_ENDPOINT = `/__refresh`
const refresh = async (req: express.Request): Promise<void> => {
if (process.send) {
process.send({
type: `REFRESH`,
action: `STARTED`,
})
}

stopSchemaHotReloader()
let activity = report.activityTimer(`createSchemaCustomization`, {})
activity.start()
Expand All @@ -256,17 +258,18 @@ async function startServer(program: IProgram): Promise<IServer> {
await rebuildSchema({ parentSpan: activity })
activity.end()
startSchemaHotReloader()

if (process.send) {
process.send({
type: `REFRESH`,
action: `FINISHED`,
})
}
}
app.use(REFRESH_ENDPOINT, express.json())
app.post(REFRESH_ENDPOINT, (req, res) => {
const enableRefresh = process.env.ENABLE_GATSBY_REFRESH_ENDPOINT
const refreshToken = process.env.GATSBY_REFRESH_TOKEN
const authorizedRefresh =
!refreshToken || req.headers.authorization === refreshToken
refresh(req)

if (enableRefresh && authorizedRefresh) {
refresh(req)
}
res.end()
})

Expand Down
4 changes: 4 additions & 0 deletions packages/gatsby/src/commands/develop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ module.exports = async (program: IProgram): Promise<void> => {
proxy.serveSite()
io.emit(`develop:started`)
}

if (msg.type === `REFRESH` && msg.action === `FINISHED`) {
proxy.refreshEnded()
}
}

io.on(`connection`, socket => {
Expand Down
75 changes: 75 additions & 0 deletions packages/gatsby/src/utils/develop-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { IProgram } from "../commands/types"
interface IProxyControls {
serveRestartingScreen: () => void
serveSite: () => void
refreshEnded: () => void
server: any
}

Expand All @@ -33,6 +34,12 @@ export const startDevelopProxy = (input: {
program: IProgram
}): IProxyControls => {
let shouldServeRestartingScreen = false
let isRefreshing = false
const refreshQueue: {
res: http.ServerResponse
req: http.IncomingMessage
body: string
}[] = []

const proxy = httpProxy.createProxyServer({
target: `http://localhost:${input.targetPort}`,
Expand Down Expand Up @@ -70,6 +77,52 @@ export const startDevelopProxy = (input: {
return
}

/**
* Refresh external data sources.
* This behavior is disabled by default, but the ENABLE_GATSBY_REFRESH_ENDPOINT env var enables it
* If no GATSBY_REFRESH_TOKEN env var is available, then no Authorization header is required
**/
const REFRESH_ENDPOINT = `/__refresh`
if (req.url === REFRESH_ENDPOINT) {
const enableRefresh = process.env.ENABLE_GATSBY_REFRESH_ENDPOINT
const refreshToken = process.env.GATSBY_REFRESH_TOKEN
const authorizedRefresh =
!refreshToken || req.headers.authorization === refreshToken

if (!enableRefresh || !authorizedRefresh) {
return
}

readRequestBody(req).then((body: string) => {
const isRequestWithSameBodyQueued = !!refreshQueue.filter(
queued => queued.body === body
).length

if (!isRequestWithSameBodyQueued) {
refreshQueue.push({ req, res, body })
}

if (isRefreshing) {
if (isRequestWithSameBodyQueued) {
res.end(
`already queued ${JSON.stringify(refreshQueue.map(q => q.body))}`
)
return
}
res.end(`queued ${JSON.stringify(refreshQueue.map(q => q.body))}`)
return
}

isRefreshing = true
const queued = refreshQueue.shift()
if (queued) {
proxy.web(queued.req, queued.res)
}
})

return
}

if (
shouldServeRestartingScreen ||
req.url === `/___debug-restarting-screen`
Expand All @@ -81,6 +134,18 @@ export const startDevelopProxy = (input: {
proxy.web(req, res)
}

function readRequestBody(request: http.IncomingMessage): Promise<string> {
return new Promise(resolve => {
let body = ``
request.on(`data`, chunk => {
body += chunk.toString()
})
request.on(`end`, () => {
resolve(body)
})
})
}

const server = input.program.ssl
? https.createServer(input.program.ssl, app)
: http.createServer(app)
Expand All @@ -99,5 +164,15 @@ export const startDevelopProxy = (input: {
serveSite: (): void => {
shouldServeRestartingScreen = false
},
refreshEnded: (): void => {
if (!refreshQueue.length) {
isRefreshing = false
} else {
const queued = refreshQueue.shift()
if (queued) {
proxy.web(queued.req, queued.res)
}
}
},
}
}

0 comments on commit 2f6a0e5

Please sign in to comment.