Skip to content
This repository was archived by the owner on Mar 10, 2020. It is now read-only.

Commit 0a4f008

Browse files
committed
feat: tests, concurrency, simplification
1 parent 4191525 commit 0a4f008

8 files changed

+620
-109
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"async": "^2.6.1",
3030
"big.js": "^5.1.2",
3131
"bs58": "^4.0.1",
32+
"buffer-to-stream": "^1.0.0",
3233
"cids": "~0.5.3",
3334
"concat-stream": "^1.6.2",
3435
"content": "^4.0.5",
@@ -62,7 +63,6 @@
6263
"pump": "^3.0.0",
6364
"qs": "^6.5.2",
6465
"readable-stream": "^2.3.6",
65-
"readable-stream-node-to-web": "^1.0.1",
6666
"stream-http": "hugomrdias/stream-http#fix/body-handling",
6767
"stream-to-pull-stream": "^1.7.2",
6868
"streamifier": "~0.1.1",

src/files/add.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ module.exports = (send) => {
4747
})
4848

4949
return function (data, options, callback) {
50-
if (options.experimental) {
50+
if (options && options.experimental) {
5151
return require('./add-experimental').add(send)(data, options, callback)
5252
}
5353

src/utils/multipart-experimental.js

+64-55
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
'use strict'
22

3-
const { Duplex } = require('stream')
3+
const { Duplex, PassThrough } = require('stream')
44
const { isSource } = require('is-pull-stream')
5-
const toStream = require('pull-stream-to-stream')
5+
const pump = require('pump')
6+
const pullToStream = require('pull-stream-to-stream')
7+
const bufferToStream = require('buffer-to-stream')
68

79
/** @private @typedef {import("../files/add-experimental").AddOptions} AddOptions */
810

@@ -63,7 +65,8 @@ class Multipart extends Duplex {
6365
constructor (options) {
6466
super({
6567
writableObjectMode: true,
66-
writableHighWaterMark: 1
68+
writableHighWaterMark: 1,
69+
readableHighWaterMark: options.chunkSize ? Math.max(136, options.chunkSize) : 16384 // min is 136
6770
})
6871

6972
this._boundary = generateBoundary()
@@ -72,12 +75,11 @@ class Multipart extends Duplex {
7275
this.buffer = Buffer.alloc(this.chunkSize)
7376
this.bufferOffset = 0
7477
this.extraBytes = 0
78+
this.sourceReadable = false
7579
}
7680

7781
_read () {
78-
if (this.source && !this.isPaused()) {
79-
this.source.resume()
80-
}
82+
// empty read
8183
}
8284

8385
_write (file, encoding, callback) {
@@ -87,30 +89,34 @@ class Multipart extends Duplex {
8789
}
8890

8991
_final (callback) {
90-
this.pushChunk(Buffer.from(PADDING + this._boundary + PADDING + NEW_LINE), true)
9192
// Flush the rest and finish
92-
if (this.bufferOffset && !this.destroyed) {
93+
const tail = Buffer.from(PADDING + this._boundary + PADDING + NEW_LINE)
94+
if (this.chunkSize === 0) {
95+
this.push(tail)
96+
} else {
97+
this.extraBytes += tail.length
9398
const slice = this.buffer.slice(0, this.bufferOffset)
94-
this.push(slice)
99+
95100
this.bufferOffset = 0
101+
this.push(Buffer.concat([slice, tail], slice.length + tail.length))
96102
}
103+
97104
this.push(null)
98105
callback()
99106
}
100107

101-
pauseAll () {
102-
this.pause()
103-
if (this.source) {
104-
this.source.pause()
105-
}
106-
}
108+
resume () {
109+
super.resume()
107110

108-
resumeAll () {
109-
this.resume()
110-
if (this.source) {
111-
this.source.resume()
111+
// Chunked mode
112+
if (this.chunkSize > 0 && this.sourceReadable) {
113+
let chunk
114+
while (!this.isPaused() && (chunk = this.source.read(this.chunkSize - this.bufferOffset)) !== null) {
115+
this.pushChunk(chunk)
116+
}
112117
}
113118
}
119+
114120
/**
115121
* Push chunk
116122
*
@@ -119,7 +125,6 @@ class Multipart extends Duplex {
119125
* @return {boolean}
120126
*/
121127
pushChunk (chunk, isExtra = false) {
122-
let result = true
123128
if (chunk === null) {
124129
return this.push(null)
125130
}
@@ -132,61 +137,65 @@ class Multipart extends Duplex {
132137
this.extraBytes += chunk.length
133138
}
134139

135-
// If we have enough bytes in this chunk to get buffer up to chunkSize,
136-
// fill in buffer, push it, and reset its offset.
137-
// Otherwise, just copy the entire chunk in to buffer.
140+
if (this.bufferOffset === 0 && chunk.length === this.chunkSize) {
141+
return this.push(chunk)
142+
}
143+
138144
const bytesNeeded = (this.chunkSize - this.bufferOffset)
139-
if (chunk.length >= bytesNeeded) {
140-
chunk.copy(this.buffer, this.bufferOffset, 0, bytesNeeded)
141-
result = this.push(this.buffer)
145+
// make sure we have the correct amount of bytes
146+
if (chunk.length === bytesNeeded) {
147+
// chunk.copy(this.buffer, this.bufferOffset, 0, bytesNeeded)
148+
const slice = this.buffer.slice(0, this.bufferOffset)
142149
this.bufferOffset = 0
143-
// Handle leftovers from the chunk
144-
const leftovers = chunk.slice(0, chunk.length - bytesNeeded)
145-
let size = leftovers.length
146-
while (size >= this.chunkSize) {
147-
result = this.push(chunk.slice(this.bufferOffset, this.bufferOffset + this.chunkSize))
148-
this.bufferOffset += this.chunkSize
149-
size -= this.chunkSize
150-
}
151-
// if we still have anything left copy to the buffer
152-
chunk.copy(this.buffer, 0, this.bufferOffset, this.bufferOffset + size)
153-
this.bufferOffset = size
154-
} else {
155-
chunk.copy(this.buffer, this.bufferOffset)
156-
this.bufferOffset += chunk.length
150+
return this.push(Buffer.concat([slice, chunk], slice.length + chunk.length))
157151
}
158152

159-
return result
153+
if (chunk.length > bytesNeeded) {
154+
this.emit('error', new RangeError(`Chunk is too big needed ${bytesNeeded} got ${chunk.length}`))
155+
return false
156+
}
157+
158+
chunk.copy(this.buffer, this.bufferOffset)
159+
this.bufferOffset += chunk.length
160+
161+
return true
160162
}
161163

162164
pushFile (file, callback) {
163165
this.pushChunk(leading(file.headers, this._boundary), true)
164166

165-
let content = file.content || Buffer.alloc(0)
167+
this.source = file.content || Buffer.alloc(0)
166168

167-
if (Buffer.isBuffer(content)) {
168-
this.pushChunk(content)
169-
this.pushChunk(NEW_LINE_BUFFER, true)
170-
return callback()
169+
if (Buffer.isBuffer(this.source)) {
170+
this.source = bufferToStream(this.source)
171171
}
172172

173-
if (isSource(content)) {
174-
content = toStream.source(content)
173+
if (isSource(file.content)) {
174+
// pull-stream-to-stream doesn't support readable event...
175+
this.source = pump([pullToStream.source(file.content), new PassThrough()])
175176
}
176-
this.source = content
177177

178-
// From now on we assume content is a stream
179-
content.on('data', (data) => {
180-
if (!this.pushChunk(data)) {
181-
content.pause()
178+
this.source.on('readable', () => {
179+
this.sourceReadable = true
180+
let chunk = null
181+
if (this.chunkSize === 0) {
182+
if ((chunk = this.source.read()) !== null) {
183+
this.pushChunk(chunk)
184+
}
185+
} else {
186+
while (!this.isPaused() && (chunk = this.source.read(this.chunkSize - this.bufferOffset)) !== null) {
187+
this.pushChunk(chunk)
188+
}
182189
}
183190
})
184-
content.once('error', this.emit.bind(this, 'error'))
185191

186-
content.once('end', () => {
192+
this.source.on('end', () => {
193+
this.sourceReadable = false
187194
this.pushChunk(NEW_LINE_BUFFER, true)
188195
callback()
189196
})
197+
198+
this.source.on('error', err => this.emit('error', err))
190199
}
191200
}
192201

src/utils/prepare-file.js

+5-7
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,13 @@ function prepareFile (file, opts) {
8989
}
9090

9191
function prepare (file, opts) {
92+
// probably it should be valid and would be handled below with Buffer.from
9293
if (typeof file === 'string') {
93-
if (!isNode) {
94-
throw new Error('Can only add file paths in node')
95-
}
96-
97-
return loadPaths(opts, file)
94+
throw new Error('String isn\'t valid as an input')
9895
}
9996

100-
if (file.path && !file.content) {
97+
// needs to test for stream because fs.createReadStream has path prop and would handle here
98+
if (!isStream(file) && file.path && !file.content) {
10199
file.dir = true
102100
return file
103101
}
@@ -106,7 +104,7 @@ function prepare (file, opts) {
106104
return file
107105
}
108106

109-
if (isBrowser && file instanceof window.File) {
107+
if (isBrowser && file instanceof self.File) {
110108
return {
111109
path: file.name,
112110
symlink: false,

src/utils/send-files-stream.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const Duplex = require('stream').Duplex
44
const eachSeries = require('async/eachSeries')
55
const isStream = require('is-stream')
66
const once = require('once')
7-
const {prepareFile} = require('./prepare-file')
7+
const { prepareFile } = require('./prepare-file')
88
const Multipart = require('./multipart')
99

1010
function headers (file) {

0 commit comments

Comments
 (0)