Skip to content
This repository has been archived by the owner on Feb 12, 2024. It is now read-only.

Commit

Permalink
fix: fix content-type by doing a fall-back using extensions (#1482)
Browse files Browse the repository at this point in the history
The JS IPFS gateway was only responding with the correct content-type for some mime-types, see https://github.com/sindresorhus/file-type#supported-file-types.
This commit now fall-backs to detecting based on the extension as well.

Note that SVGs aren't supported by the `file-type` module.
  • Loading branch information
satazor authored and alanshaw committed Aug 2, 2018
1 parent 5e80ee3 commit d528b3f
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 22 deletions.
46 changes: 26 additions & 20 deletions src/http/gateway/resources/gateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@ const Stream = require('readable-stream')
const { resolver } = require('ipfs-http-response')
const PathUtils = require('../utils/path')

function detectContentType (ref, chunk) {
let fileSignature

// try to guess the filetype based on the first bytes
// note that `file-type` doesn't support svgs, therefore we assume it's a svg if ref looks like it
if (!ref.endsWith('.svg')) {
fileSignature = fileType(chunk)
}

// if we were unable to, fallback to the `ref` which might contain the extension
const mimeType = mime.lookup(fileSignature ? fileSignature.ext : ref)

return mime.contentType(mimeType)
}

module.exports = {
checkCID: (request, reply) => {
if (!request.params.cid) {
Expand Down Expand Up @@ -97,7 +112,7 @@ module.exports = {
}

// response.continue()
let filetypeChecked = false
let contentTypeDetected = false
let stream2 = new Stream.PassThrough({ highWaterMark: 1 })
stream2.on('error', (err) => {
log.error('stream2 err: ', err)
Expand All @@ -108,29 +123,20 @@ module.exports = {
pull(
toPull.source(stream),
pull.through((chunk) => {
// Check file type. do this once.
if (chunk.length > 0 && !filetypeChecked) {
log('got first chunk')
let fileSignature = fileType(chunk)
log('file type: ', fileSignature)

filetypeChecked = true
const mimeType = mime.lookup(fileSignature
? fileSignature.ext
: null)
// Guess content-type (only once)
if (chunk.length > 0 && !contentTypeDetected) {
let contentType = detectContentType(ref, chunk)
contentTypeDetected = true

log('ref ', ref)
log('mime-type ', mimeType)

if (mimeType) {
log('writing mimeType')
log('mime-type ', contentType)

response
.header('Content-Type', mime.contentType(mimeType))
.send()
} else {
response.send()
if (contentType) {
log('writing content-type header')
response.header('Content-Type', contentType)
}

response.send()
}

stream2.write(chunk)
Expand Down
50 changes: 48 additions & 2 deletions test/gateway/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ const directoryContent = {
'nested-folder/hello.txt': loadFixture('test/gateway/test-folder/nested-folder/hello.txt'),
'nested-folder/ipfs.txt': loadFixture('test/gateway/test-folder/nested-folder/ipfs.txt'),
'nested-folder/nested.html': loadFixture('test/gateway/test-folder/nested-folder/nested.html'),
'cat-folder/cat.jpg': loadFixture('test/gateway/test-folder/cat-folder/cat.jpg')
'cat-folder/cat.jpg': loadFixture('test/gateway/test-folder/cat-folder/cat.jpg'),
'unsniffable-folder/hexagons-xml.svg': loadFixture('test/gateway/test-folder/unsniffable-folder/hexagons-xml.svg'),
'unsniffable-folder/hexagons.svg': loadFixture('test/gateway/test-folder/unsniffable-folder/hexagons.svg')
}

describe('HTTP Gateway', function () {
Expand Down Expand Up @@ -113,6 +115,22 @@ describe('HTTP Gateway', function () {
expect(file.hash).to.equal(expectedMultihash)
cb()
})
},
(cb) => {
const expectedMultihash = 'QmVZoGxDvKM9KExc8gaL4uTbhdNtWhzQR7ndrY7J1gWs3F'

let dir = [
content('unsniffable-folder/hexagons-xml.svg'),
content('unsniffable-folder/hexagons.svg')
]

http.api.node.files.add(dir, (err, res) => {
expect(err).to.not.exist()
const file = res[res.length - 2]
expect(file.path).to.equal('test-folder/unsniffable-folder')
expect(file.hash).to.equal(expectedMultihash)
cb()
})
}
], done)
})
Expand Down Expand Up @@ -166,7 +184,7 @@ describe('HTTP Gateway', function () {
})
})

it('load a non text file', (done) => {
it('load a jpg file', (done) => {
let kitty = 'QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg'

gateway.inject({
Expand All @@ -184,6 +202,34 @@ describe('HTTP Gateway', function () {
})
})

it('load a svg file (unsniffable)', (done) => {
let hexagons = 'QmVZoGxDvKM9KExc8gaL4uTbhdNtWhzQR7ndrY7J1gWs3F/hexagons.svg'

gateway.inject({
method: 'GET',
url: '/ipfs/' + hexagons
}, (res) => {
expect(res.statusCode).to.equal(200)
expect(res.headers['content-type']).to.equal('image/svg+xml')

done()
})
})

it('load a svg file with xml leading declaration (unsniffable)', (done) => {
let hexagons = 'QmVZoGxDvKM9KExc8gaL4uTbhdNtWhzQR7ndrY7J1gWs3F/hexagons-xml.svg'

gateway.inject({
method: 'GET',
url: '/ipfs/' + hexagons
}, (res) => {
expect(res.statusCode).to.equal(200)
expect(res.headers['content-type']).to.equal('image/svg+xml')

done()
})
})

it('load a directory', (done) => {
let dir = 'QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/'

Expand Down
56 changes: 56 additions & 0 deletions test/gateway/test-folder/unsniffable-folder/hexagons-xml.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 55 additions & 0 deletions test/gateway/test-folder/unsniffable-folder/hexagons.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d528b3f

Please sign in to comment.