mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
When serving output files, intelligently determine the appropriate content-type.
cherry pick 6fa3fda3ed28239cf3ac9720629f9707663aa197 from datajoy.
This commit is contained in:
parent
ddcfc3ee61
commit
3217b1d58f
3 changed files with 79 additions and 5 deletions
|
@ -3,6 +3,7 @@ Settings = require "settings-sharelatex"
|
||||||
logger = require "logger-sharelatex"
|
logger = require "logger-sharelatex"
|
||||||
logger.initialize("clsi")
|
logger.initialize("clsi")
|
||||||
smokeTest = require "smoke-test-sharelatex"
|
smokeTest = require "smoke-test-sharelatex"
|
||||||
|
ContentTypeMapper = require "./app/js/ContentTypeMapper"
|
||||||
|
|
||||||
Path = require "path"
|
Path = require "path"
|
||||||
fs = require "fs"
|
fs = require "fs"
|
||||||
|
@ -46,17 +47,13 @@ ForbidSymlinks = require "./app/js/StaticServerForbidSymlinks"
|
||||||
# and serving the files
|
# and serving the files
|
||||||
staticServer = ForbidSymlinks express.static, Settings.path.compilesDir, setHeaders: (res, path, stat) ->
|
staticServer = ForbidSymlinks express.static, Settings.path.compilesDir, setHeaders: (res, path, stat) ->
|
||||||
if Path.basename(path) == "output.pdf"
|
if Path.basename(path) == "output.pdf"
|
||||||
res.set("Content-Type", "application/pdf")
|
|
||||||
# Calculate an etag in the same way as nginx
|
# Calculate an etag in the same way as nginx
|
||||||
# https://github.com/tj/send/issues/65
|
# https://github.com/tj/send/issues/65
|
||||||
etag = (path, stat) ->
|
etag = (path, stat) ->
|
||||||
'"' + Math.ceil(+stat.mtime / 1000).toString(16) +
|
'"' + Math.ceil(+stat.mtime / 1000).toString(16) +
|
||||||
'-' + Number(stat.size).toString(16) + '"'
|
'-' + Number(stat.size).toString(16) + '"'
|
||||||
res.set("Etag", etag(path, stat))
|
res.set("Etag", etag(path, stat))
|
||||||
else
|
res.set("Content-Type", ContentTypeMapper.map(path))
|
||||||
# Force plain treatment of other file types to prevent hosting of HTTP/JS files
|
|
||||||
# that could be used in same-origin/XSS attacks.
|
|
||||||
res.set("Content-Type", "text/plain")
|
|
||||||
|
|
||||||
app.get "/project/:project_id/output/*", (req, res, next) ->
|
app.get "/project/:project_id/output/*", (req, res, next) ->
|
||||||
if req.query?.build? && req.query.build.match(OutputCacheManager.BUILD_REGEX)
|
if req.query?.build? && req.query.build.match(OutputCacheManager.BUILD_REGEX)
|
||||||
|
|
26
services/clsi/app/coffee/ContentTypeMapper.coffee
Normal file
26
services/clsi/app/coffee/ContentTypeMapper.coffee
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
Path = require 'path'
|
||||||
|
|
||||||
|
# here we coerce html, css and js to text/plain,
|
||||||
|
# otherwise choose correct mime type based on file extension,
|
||||||
|
# falling back to octet-stream
|
||||||
|
module.exports = ContentTypeMapper =
|
||||||
|
map: (path) ->
|
||||||
|
switch Path.extname(path)
|
||||||
|
when '.txt', '.html', '.js', '.css'
|
||||||
|
return 'text/plain'
|
||||||
|
when '.csv'
|
||||||
|
return 'text/csv'
|
||||||
|
when '.pdf'
|
||||||
|
return 'application/pdf'
|
||||||
|
when '.png'
|
||||||
|
return 'image/png'
|
||||||
|
when '.jpg', '.jpeg'
|
||||||
|
return 'image/jpeg'
|
||||||
|
when '.tiff'
|
||||||
|
return 'image/tiff'
|
||||||
|
when '.gif'
|
||||||
|
return 'image/gif'
|
||||||
|
when '.svg'
|
||||||
|
return 'image/svg+xml'
|
||||||
|
else
|
||||||
|
return 'application/octet-stream'
|
51
services/clsi/test/unit/coffee/ContentTypeMapperTests.coffee
Normal file
51
services/clsi/test/unit/coffee/ContentTypeMapperTests.coffee
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
SandboxedModule = require('sandboxed-module')
|
||||||
|
sinon = require('sinon')
|
||||||
|
require('chai').should()
|
||||||
|
modulePath = require('path').join __dirname, '../../../app/js/ContentTypeMapper'
|
||||||
|
|
||||||
|
describe 'ContentTypeMapper', ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
@ContentTypeMapper = SandboxedModule.require modulePath
|
||||||
|
|
||||||
|
describe 'map', ->
|
||||||
|
|
||||||
|
it 'should map .txt to text/plain', ->
|
||||||
|
content_type = @ContentTypeMapper.map('example.txt')
|
||||||
|
content_type.should.equal 'text/plain'
|
||||||
|
|
||||||
|
it 'should map .csv to text/csv', ->
|
||||||
|
content_type = @ContentTypeMapper.map('example.csv')
|
||||||
|
content_type.should.equal 'text/csv'
|
||||||
|
|
||||||
|
it 'should map .pdf to application/pdf', ->
|
||||||
|
content_type = @ContentTypeMapper.map('example.pdf')
|
||||||
|
content_type.should.equal 'application/pdf'
|
||||||
|
|
||||||
|
it 'should fall back to octet-stream', ->
|
||||||
|
content_type = @ContentTypeMapper.map('example.unknown')
|
||||||
|
content_type.should.equal 'application/octet-stream'
|
||||||
|
|
||||||
|
describe 'coercing web files to plain text', ->
|
||||||
|
|
||||||
|
it 'should map .js to plain text', ->
|
||||||
|
content_type = @ContentTypeMapper.map('example.js')
|
||||||
|
content_type.should.equal 'text/plain'
|
||||||
|
|
||||||
|
it 'should map .html to plain text', ->
|
||||||
|
content_type = @ContentTypeMapper.map('example.html')
|
||||||
|
content_type.should.equal 'text/plain'
|
||||||
|
|
||||||
|
it 'should map .css to plain text', ->
|
||||||
|
content_type = @ContentTypeMapper.map('example.css')
|
||||||
|
content_type.should.equal 'text/plain'
|
||||||
|
|
||||||
|
describe 'image files', ->
|
||||||
|
|
||||||
|
it 'should map .png to image/png', ->
|
||||||
|
content_type = @ContentTypeMapper.map('example.png')
|
||||||
|
content_type.should.equal 'image/png'
|
||||||
|
|
||||||
|
it 'should map .jpeg to image/jpeg', ->
|
||||||
|
content_type = @ContentTypeMapper.map('example.jpeg')
|
||||||
|
content_type.should.equal 'image/jpeg'
|
Loading…
Reference in a new issue