mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #21011 from overleaf/ls-serve-static-wrapper
add a wrapper for serve static to handle premature close error GitOrigin-RevId: 8128702d9340a893624061d07bf0da15ea457f43
This commit is contained in:
parent
3be3bf9037
commit
8c342dc226
3 changed files with 104 additions and 4 deletions
34
services/web/app/src/infrastructure/ServeStaticWrapper.mjs
Normal file
34
services/web/app/src/infrastructure/ServeStaticWrapper.mjs
Normal file
|
@ -0,0 +1,34 @@
|
|||
import express from 'express'
|
||||
import { plainTextResponse } from './Response.js'
|
||||
|
||||
/*
|
||||
This wrapper is implemented specifically to handle "Premature Close" errors.
|
||||
These errors occur when the client cancels a request while static assets are being loaded.
|
||||
This issue is beyond our control, it can result in unnecessary log noise.
|
||||
Therefore, this wrapper is added to handle such errors.
|
||||
*/
|
||||
function serveStaticWrapper(root, options) {
|
||||
const serveStatic = express.static(root, options)
|
||||
return (req, res, next) => {
|
||||
serveStatic(req, res, error => {
|
||||
if (!error) {
|
||||
return next()
|
||||
}
|
||||
|
||||
if (error.code !== 'ERR_STREAM_PREMATURE_CLOSE') {
|
||||
return next(error)
|
||||
}
|
||||
|
||||
req.logger.addFields({ err: error })
|
||||
req.logger.setLevel('debug')
|
||||
if (res.headersSent) {
|
||||
res.end()
|
||||
} else {
|
||||
res.status(400)
|
||||
plainTextResponse(res, 'Premature close')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default serveStaticWrapper
|
|
@ -37,6 +37,7 @@ import noCache from 'nocache'
|
|||
import os from 'os'
|
||||
import http from 'http'
|
||||
import { fileURLToPath } from 'url'
|
||||
import serveStaticWrapper from './ServeStaticWrapper.mjs'
|
||||
|
||||
const sessionsRedisClient = UserSessionsRedis.client()
|
||||
|
||||
|
@ -120,10 +121,13 @@ if (Settings.exposeHostname) {
|
|||
}
|
||||
|
||||
webRouter.use(
|
||||
express.static(fileURLToPath(new URL('../../../public', import.meta.url)), {
|
||||
maxAge: STATIC_CACHE_AGE,
|
||||
setHeaders: csp.removeCSPHeaders,
|
||||
})
|
||||
serveStaticWrapper(
|
||||
fileURLToPath(new URL('../../../public', import.meta.url)),
|
||||
{
|
||||
maxAge: STATIC_CACHE_AGE,
|
||||
setHeaders: csp.removeCSPHeaders,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
app.set('views', fileURLToPath(new URL('../../views', import.meta.url)))
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import { strict as esmock } from 'esmock'
|
||||
import { expect } from 'chai'
|
||||
import Path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import sinon from 'sinon'
|
||||
import MockResponse from '../helpers/MockResponse.js'
|
||||
import MockRequest from '../helpers/MockRequest.js'
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url))
|
||||
const modulePath = Path.join(
|
||||
__dirname,
|
||||
'../../../../app/src/infrastructure/ServeStaticWrapper'
|
||||
)
|
||||
|
||||
describe('ServeStaticWrapperTests', function () {
|
||||
let error = null
|
||||
|
||||
beforeEach(async function () {
|
||||
this.req = new MockRequest()
|
||||
this.res = new MockResponse()
|
||||
this.express = {
|
||||
static: () => (req, res, next) => {
|
||||
if (error) {
|
||||
next(error)
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
},
|
||||
}
|
||||
this.serveStaticWrapper = await esmock(modulePath, {
|
||||
express: this.express,
|
||||
})
|
||||
})
|
||||
|
||||
this.afterEach(() => {
|
||||
error = null
|
||||
})
|
||||
|
||||
it('Premature close error thrown', async function () {
|
||||
error = new Error()
|
||||
error.code = 'ERR_STREAM_PREMATURE_CLOSE'
|
||||
const middleware = this.serveStaticWrapper('test_folder', {})
|
||||
const next = sinon.stub()
|
||||
middleware(this.req, this.res, next)
|
||||
expect(next.called).to.be.false
|
||||
})
|
||||
|
||||
it('No error thrown', async function () {
|
||||
const middleware = this.serveStaticWrapper('test_folder', {})
|
||||
const next = sinon.stub()
|
||||
middleware(this.req, this.res, next)
|
||||
expect(next).to.be.calledWith()
|
||||
})
|
||||
|
||||
it('Other error thrown', async function () {
|
||||
error = new Error()
|
||||
const middleware = this.serveStaticWrapper('test_folder', {})
|
||||
const next = sinon.stub()
|
||||
middleware(this.req, this.res, next)
|
||||
expect(next).to.be.calledWith(error)
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue