Skip to content
This repository was archived by the owner on Sep 28, 2021. It is now read-only.

chore: convert to async await syntax #28

Merged
merged 4 commits into from
Oct 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
},
"homepage": "https://github.com/ipfs/js-ipfs-http-response#readme",
"dependencies": {
"async": "^2.6.1",
"cids": "~0.7.1",
"debug": "^4.1.1",
"file-type": "^8.0.0",
Expand All @@ -40,8 +39,8 @@
"ipfs-unixfs": "~0.1.16",
"mime-types": "^2.1.21",
"multihashes": "~0.4.14",
"promisify-es6": "^1.0.3",
"stream-to-blob": "^1.0.1"
"p-try-each": "^1.0.1",
"stream-to-blob": "^2.0.0"
},
"devDependencies": {
"aegir": "^18.0.3",
Expand Down
159 changes: 75 additions & 84 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,103 +19,94 @@ const header = (status = 200, statusText = 'OK', headers = {}) => ({
headers
})

const response = (ipfsNode, ipfsPath) => {
const response = async (ipfsNode, ipfsPath) => {
// handle hash resolve error (simple hash, test for directory now)
const handleResolveError = (node, path, error) => {
const handleResolveError = async (node, path, error) => {
if (error) {
const errorString = error.toString()

return new Promise((resolve, reject) => {
// switch case with true feels so wrong.
switch (true) {
case (errorString.includes('dag node is a directory')):
resolver.directory(node, path, error.cid)
.then((content) => {
// dir render
if (typeof content === 'string') {
resolve(new Response(content, header(200, 'OK', { 'Content-Type': 'text/html' })))
}

// redirect to dir entry point (index)
resolve(Response.redirect(pathUtils.joinURLParts(path, content[0].Name)))
})
.catch((error) => {
log(error)
resolve(new Response(errorString, header(500, error.toString())))
})
break
case errorString.startsWith('Error: no link named'):
resolve(new Response(errorString, header(404, errorString)))
break
case errorString.startsWith('Error: multihash length inconsistent'):
case errorString.startsWith('Error: Non-base58 character'):
resolve(new Response(errorString, header(400, errorString)))
break
default:
log(error)
resolve(new Response(errorString, header(500, errorString)))
if (errorString.includes('dag node is a directory')) {
try {
const content = await resolver.directory(node, path, error.cid)
// dir render
if (typeof content === 'string') {
return new Response(content, header(200, 'OK', { 'Content-Type': 'text/html' }))
}

// redirect to dir entry point (index)
return Response.redirect(pathUtils.joinURLParts(path, content[0].Name))
} catch (error) {
log(error)
return new Response(errorString, header(500, error.toString()))
}
})
}

if (errorString.startsWith('Error: no link named')) {
return new Response(errorString, header(404, errorString))
}

if (errorString.startsWith('Error: multihash length inconsistent') || errorString.startsWith('Error: Non-base58 character')) {
return new Response(errorString, header(400, errorString))
}

log(error)
return new Response(errorString, header(500, errorString))
}
}

return new Promise((resolve, reject) => {
// remove trailing slash for files if needed
if (ipfsPath.endsWith('/')) {
resolve(Response.redirect(pathUtils.removeTrailingSlash(ipfsPath)))
}
// remove trailing slash for files if needed
if (ipfsPath.endsWith('/')) {
return Response.redirect(pathUtils.removeTrailingSlash(ipfsPath))
}

resolver.cid(ipfsNode, ipfsPath)
.then((resolvedData) => {
const readableStream = ipfsNode.catReadableStream(resolvedData.cid)
const responseStream = new stream.PassThrough({ highWaterMark: 1 })
readableStream.pipe(responseStream)
try {
const resolvedData = await resolver.cid(ipfsNode, ipfsPath)

readableStream.once('error', (error) => {
if (error) {
log(error)
resolve(new Response(error.toString(), header(500, 'Error fetching the file')))
}
})

// return only after first chunk being checked
let contentTypeDetected = false
readableStream.on('data', (chunk) => {
// check mime on first chunk
if (contentTypeDetected) {
return
}
const readableStream = ipfsNode.catReadableStream(resolvedData.cid)
const responseStream = new stream.PassThrough({ highWaterMark: 1 })
readableStream.pipe(responseStream)

contentTypeDetected = true
// return Response with mime type
const contentType = detectContentType(ipfsPath, chunk)

if (typeof Blob === 'undefined') {
if (contentType) {
resolve(new Response(responseStream, header(200, 'OK', { 'Content-Type': contentType })))
} else {
resolve(new Response(responseStream, header()))
}
} else {
toBlob(responseStream, (err, blob) => {
if (err) {
resolve(new Response(err.toString(), header(500, 'Error fetching the file')))
}

if (contentType) {
resolve(new Response(blob, header(200, 'OK', { 'Content-Type': contentType })))
} else {
resolve(new Response(blob, header()))
}
})
}
})
return new Promise((resolve, reject) => {
readableStream.once('error', (error) => {
if (error) {
log(error)
return resolve(new Response(error.toString(), header(500, 'Error fetching the file')))
}
})
.catch((error) => {
log(error)
resolve(handleResolveError(ipfsNode, ipfsPath, error))

// return only after first chunk being checked
let contentTypeDetected = false
readableStream.on('data', async (chunk) => {
// check mime on first chunk
if (contentTypeDetected) {
return
}

contentTypeDetected = true
// return Response with mime type
const contentType = detectContentType(ipfsPath, chunk)

if (typeof Blob === 'undefined') {
return contentType
? resolve(new Response(responseStream, header(200, 'OK', { 'Content-Type': contentType })))
: resolve(new Response(responseStream, header()))
}

try {
const blob = await toBlob(responseStream)

return contentType
? resolve(new Response(blob, header(200, 'OK', { 'Content-Type': contentType })))
: resolve(new Response(blob, header()))
} catch (err) {
return resolve(new Response(err.toString(), header(500, 'Error fetching the file')))
}
})
})
})
} catch (error) {
log(error)
return handleResolveError(ipfsNode, ipfsPath, error)
}
}

module.exports = {
Expand Down
97 changes: 41 additions & 56 deletions src/resolver.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
'use strict'

const pTryEach = require('p-try-each')
const mh = require('multihashes')
const promisify = require('promisify-es6')
const CID = require('cids')
const debug = require('debug')
const tryEach = require('async/tryEach')
const waterfall = require('async/waterfall')
const log = debug('jsipfs:http:response:resolver')
log.error = debug('jsipfs:http:response:resolver:error')
const dirView = require('./dir-view')
Expand All @@ -16,74 +14,61 @@ const INDEX_HTML_FILES = [
'index.shtml'
]

const findIndexFile = (ipfs, path, callback) => {
return tryEach(INDEX_HTML_FILES.map(file => {
return (cb) => {
waterfall([
(cb) => ipfs.files.stat(`${path}/${file}`, cb),
(stats, cb) => cb(null, {
name: file,
cid: new CID(stats.hash)
})
], cb)
const findIndexFile = (ipfs, path) => {
return pTryEach(INDEX_HTML_FILES.map(file => {
return async () => {
const stats = await ipfs.files.stat(`${path}/${file}`)

return {
name: file,
cid: new CID(stats.hash)
}
}
}), callback)
}))
}

const directory = promisify((ipfs, path, cid, callback) => {
const directory = async (ipfs, path, cid) => {
// Test if it is a Website
findIndexFile(ipfs, path, (err, res) => {
if (err) {
if (err.message.includes('does not exist')) {
// not a website, just show a directory listing
return ipfs.dag.get(cid, (err, result) => {
if (err) {
return callback(err)
}

return callback(null, dirView.render(path, result.value.Links))
})
}
try {
const res = await findIndexFile(ipfs, path)

return callback(err)
return [{ Name: res.name }]
} catch (err) {
if (err.message.includes('does not exist')) {
// not a website, just show a directory listing
const result = await ipfs.dag.get(cid)

return dirView.render(path, result.value.Links)
}

callback(err, [{
Name: res.name
}])
})
})
throw err
}
}

const cid = promisify((ipfs, path, callback) => {
ipfs.files.stat(path, (err, stats) => {
if (err) {
return callback(err)
}
const cid = async (ipfs, path) => {
const stats = await ipfs.files.stat(path)

const cid = new CID(stats.hash)
const cid = new CID(stats.hash)

if (stats.type.includes('directory')) {
const err = new Error('This dag node is a directory')
err.cid = cid
err.fileName = stats.name
err.dagDirType = stats.type
if (stats.type.includes('directory')) {
const err = new Error('This dag node is a directory')
err.cid = cid
err.fileName = stats.name
err.dagDirType = stats.type

return callback(err)
}
throw err
}

callback(err, {
cid
})
})
})
return { cid }
}

const multihash = promisify((ipfs, path, callback) => {
const multihash = async (ipfs, path) => {
// deprecated, use 'cid' instead
// (left for backward-compatibility)
cid(ipfs, path)
.then((result) => { callback(null, { multihash: mh.toB58String(result.cid.multihash) }) })
.catch((err) => { callback(err) })
})
const result = await cid(ipfs, path)

return { multihash: mh.toB58String(result.cid.multihash) }
}

module.exports = {
directory: directory,
Expand Down