From 1588294fef09c04e0a10cfa08ab8a64b2524b306 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 24 Oct 2015 16:19:28 +0200 Subject: [PATCH] fix: Replace `http.request` with request --- .eslintrc | 1 + package.json | 3 +- src/get-files-stream.js | 3 +- src/request-api.js | 199 +++++++++++++++++++++------------------- test/tests.js | 17 ++-- 5 files changed, 120 insertions(+), 103 deletions(-) diff --git a/.eslintrc b/.eslintrc index e3578aadf..e9059f080 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,3 +1,4 @@ { + "parser": "babel-eslint", "extends": "standard" } diff --git a/package.json b/package.json index c5237118f..6026554cc 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "merge-stream": "^1.0.0", "multiaddr": "^1.0.0", "multipart-stream": "^2.0.0", + "request": "^2.65.0", "vinyl": "^0.5.1", "vinyl-fs-browser": "^2.1.1-1", "vinyl-multipart-stream": "^1.2.6" @@ -25,7 +26,7 @@ "url": "https://github.com/ipfs/js-ipfs-api" }, "devDependencies": { - "brfs": "^1.4.1", + "babel-eslint": "^4.1.3", "browserify": "^11.0.0", "concurrently": "^0.1.1", "eslint-config-standard": "^4.4.0", diff --git a/src/get-files-stream.js b/src/get-files-stream.js index 4ad6d03f9..9e7d54dee 100644 --- a/src/get-files-stream.js +++ b/src/get-files-stream.js @@ -8,7 +8,6 @@ exports = module.exports = getFilesStream function getFilesStream (files, opts) { if (!files) return null - if (!Array.isArray(files)) files = [files] // merge all inputs into one stream var adder = new Merge() @@ -31,7 +30,7 @@ function getFilesStream (files, opts) { adder.add(vinylfs.src(file, srcOpts)) // if recursive, glob the contents - if (opts.r || opts.recursive) { + if (opts.recursive) { adder.add(vinylfs.src(file + '/**/*', srcOpts)) } } else { diff --git a/src/request-api.js b/src/request-api.js index dfb3c353b..a1cb446a4 100644 --- a/src/request-api.js +++ b/src/request-api.js @@ -1,108 +1,123 @@ -var http = require('http') -var qs = require('querystring') -var getFilesStream = require('./get-files-stream') +'use strict' -exports = module.exports = function getRequestAPI (config) { - return requestAPI +const request = require('request') +const getFilesStream = require('./get-files-stream') - function requestAPI (path, args, opts, files, buffer, cb) { - var query, stream, contentType - contentType = 'application/json' +const isNode = !global.window - if (Array.isArray(path)) path = path.join('/') +// -- Internal - opts = opts || {} +function onEnd (buffer, result, cb) { + return (err, res, body) => { + if (err) { + return cb(err) + } - if (args && !Array.isArray(args)) args = [args] - if (args) opts.arg = args + if (res.statusCode >= 400 || !res.statusCode) { + return cb(new Error(`Server responded with ${res.statusCode}: ${body}`)) + } - if (files) { - stream = getFilesStream(files, opts) - if (!stream.boundary) { - throw new Error('no boundary in multipart stream') - } - contentType = 'multipart/form-data; boundary=' + stream.boundary + if ((result.stream && !buffer) || + (result.chunkedObjects && buffer)) { + return cb(null, body) } - if (typeof buffer === 'function') { - cb = buffer - buffer = false + if (result.chunkedObjects) return cb(null, result.objects) + + try { + const parsedBody = JSON.parse(body) + cb(null, parsedBody) + } catch (e) { + cb(null, body) } + } +} - // this option is only used internally, not passed to daemon - delete opts.followSymlinks - - opts['stream-channels'] = true - query = qs.stringify(opts) - - var reqo = { - method: files ? 'POST' : 'GET', - host: config.host, - port: config.port, - path: config['api-path'] + path + '?' + query, - headers: { - 'User-Agent': config['user-agent'], - 'Content-Type': contentType - }, - withCredentials: false +function onData (result) { + return chunk => { + if (!result.chunkedObjects) return + + try { + const obj = JSON.parse(chunk.toString()) + result.objects.push(obj) + } catch (e) { + result.chunkedObjects = false } + } +} + +function onResponse (result) { + return res => { + result.stream = !!res.headers['x-stream-output'] + result.chunkedObjects = !!res.headers['x-chunked-output'] + } +} + +function makeRequest (opts, buffer, cb) { + // this option is only used internally, not passed to daemon + delete opts.qs.followSymlinks + + const result = { + stream: false, + chunkedObjects: false, + objects: [] + } + + return request(opts, onEnd(buffer, result, cb)) + .on('data', onData(result)) + .on('response', onResponse(result)) +} + +function requestAPI (config, path, args, qs, files, buffer, cb) { + qs = qs || {} + if (Array.isArray(path)) path = path.join('/') + if (args && !Array.isArray(args)) args = [args] + if (args) qs.arg = args + if (files && !Array.isArray(files)) files = [files] + + if (typeof buffer === 'function') { + cb = buffer + buffer = false + } + + if (qs.r) qs.recursive = qs.r + + if (!isNode && qs.recursive && path === 'add') { + return cb(new Error('Recursive uploads are not supported in the browser')) + } + + qs['stream-channels'] = true - var req = http.request(reqo, function (res) { - var data = '' - var objects = [] - var stream = !!res.headers && !!res.headers['x-stream-output'] - var chunkedObjects = !!res.headers && !!res.headers['x-chunked-output'] - - if (stream && !buffer) return cb(null, res) - if (chunkedObjects && buffer) return cb(null, res) - - res.on('data', function (chunk) { - if (!chunkedObjects) { - data += chunk - return data - } - - try { - var obj = JSON.parse(chunk.toString()) - objects.push(obj) - } catch (e) { - chunkedObjects = false - data += chunk - } - }) - res.on('end', function () { - var parsed - - if (!chunkedObjects) { - try { - parsed = JSON.parse(data) - data = parsed - } catch (e) {} - } else { - data = objects - } - - if (res.statusCode >= 400 || !res.statusCode) { - if (!data) data = new Error() - return cb(data, null) - } - return cb(null, data) - }) - res.on('error', function (err) { - return cb(err, null) - }) - }) - - req.on('error', function (err) { - return cb(err, null) - }) - - if (stream) { - stream.pipe(req) - } else { - req.end() + const opts = { + method: files ? 'POST' : 'GET', + uri: `http://${config.host}:${config.port}${config['api-path']}${path}`, + qs: qs, + useQuerystring: true, + headers: {}, + withCredentials: false, + gzip: true + } + + if (isNode) { + // Browsers do not allow you to modify the user agent + opts.headers['User-Agent'] = config['user-agent'] + } + + if (files) { + const stream = getFilesStream(files, qs) + if (!stream.boundary) { + return cb(new Error('No boundary in multipart stream')) } - return req + opts.headers['Content-Type'] = `multipart/form-data; boundary=${stream.boundary}` + stream.pipe(makeRequest(opts, buffer, cb)) + } else { + makeRequest(opts, buffer, cb) } } + +// -- Interface + +exports = module.exports = function getRequestAPI (config) { + return requestAPI.bind(null, config) +} diff --git a/test/tests.js b/test/tests.js index cbf9eb656..fca56185f 100644 --- a/test/tests.js +++ b/test/tests.js @@ -130,18 +130,19 @@ describe('IPFS Node.js API wrapper tests', function () { }) it('add a nested dir', function (done) { - if (!isNode) { - return done() - } this.timeout(10000) apiClients['a'].add(__dirname + '/test-folder', { recursive: true }, function (err, res) { - if (err) { - throw err + if (isNode) { + if (err) throw err + + var added = res[res.length - 1] + assert.equal(added.Hash, 'QmZdsefMGMeG6bL719gX44XSVQrL6psEgRZdw1SGadFaK2') + done() + } else { + assert.equal(err.message, 'Recursive uploads are not supported in the browser') + done() } - var added = res[res.length - 1] - assert.equal(added.Hash, 'QmZdsefMGMeG6bL719gX44XSVQrL6psEgRZdw1SGadFaK2') - done() }) }) })