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

Commit

Permalink
chore: convert to async await syntax (#28)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: All places in the API that used callbacks are now replaced with async/await

Co-authored-by: PedroMiguelSS <pedro.santos@moxy.studio>
  • Loading branch information
vasco-santos and PedroMiguelSS committed Oct 14, 2019
1 parent 5f9e3e3 commit a22900a
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 143 deletions.
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

0 comments on commit a22900a

Please sign in to comment.