You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
112 lines
2.6 KiB
112 lines
2.6 KiB
'use strict' |
|
|
|
var uuid = require('uuid/v4') |
|
var CombinedStream = require('combined-stream') |
|
var isstream = require('isstream') |
|
var Buffer = require('safe-buffer').Buffer |
|
|
|
function Multipart (request) { |
|
this.request = request |
|
this.boundary = uuid() |
|
this.chunked = false |
|
this.body = null |
|
} |
|
|
|
Multipart.prototype.isChunked = function (options) { |
|
var self = this |
|
var chunked = false |
|
var parts = options.data || options |
|
|
|
if (!parts.forEach) { |
|
self.request.emit('error', new Error('Argument error, options.multipart.')) |
|
} |
|
|
|
if (options.chunked !== undefined) { |
|
chunked = options.chunked |
|
} |
|
|
|
if (self.request.getHeader('transfer-encoding') === 'chunked') { |
|
chunked = true |
|
} |
|
|
|
if (!chunked) { |
|
parts.forEach(function (part) { |
|
if (typeof part.body === 'undefined') { |
|
self.request.emit('error', new Error('Body attribute missing in multipart.')) |
|
} |
|
if (isstream(part.body)) { |
|
chunked = true |
|
} |
|
}) |
|
} |
|
|
|
return chunked |
|
} |
|
|
|
Multipart.prototype.setHeaders = function (chunked) { |
|
var self = this |
|
|
|
if (chunked && !self.request.hasHeader('transfer-encoding')) { |
|
self.request.setHeader('transfer-encoding', 'chunked') |
|
} |
|
|
|
var header = self.request.getHeader('content-type') |
|
|
|
if (!header || header.indexOf('multipart') === -1) { |
|
self.request.setHeader('content-type', 'multipart/related; boundary=' + self.boundary) |
|
} else { |
|
if (header.indexOf('boundary') !== -1) { |
|
self.boundary = header.replace(/.*boundary=([^\s;]+).*/, '$1') |
|
} else { |
|
self.request.setHeader('content-type', header + '; boundary=' + self.boundary) |
|
} |
|
} |
|
} |
|
|
|
Multipart.prototype.build = function (parts, chunked) { |
|
var self = this |
|
var body = chunked ? new CombinedStream() : [] |
|
|
|
function add (part) { |
|
if (typeof part === 'number') { |
|
part = part.toString() |
|
} |
|
return chunked ? body.append(part) : body.push(Buffer.from(part)) |
|
} |
|
|
|
if (self.request.preambleCRLF) { |
|
add('\r\n') |
|
} |
|
|
|
parts.forEach(function (part) { |
|
var preamble = '--' + self.boundary + '\r\n' |
|
Object.keys(part).forEach(function (key) { |
|
if (key === 'body') { return } |
|
preamble += key + ': ' + part[key] + '\r\n' |
|
}) |
|
preamble += '\r\n' |
|
add(preamble) |
|
add(part.body) |
|
add('\r\n') |
|
}) |
|
add('--' + self.boundary + '--') |
|
|
|
if (self.request.postambleCRLF) { |
|
add('\r\n') |
|
} |
|
|
|
return body |
|
} |
|
|
|
Multipart.prototype.onRequest = function (options) { |
|
var self = this |
|
|
|
var chunked = self.isChunked(options) |
|
var parts = options.data || options |
|
|
|
self.setHeaders(chunked) |
|
self.chunked = chunked |
|
self.body = self.build(parts, chunked) |
|
} |
|
|
|
exports.Multipart = Multipart
|
|
|