multipart.js (2674B)
1 'use strict' 2 3 var uuid = require('uuid/v4') 4 var CombinedStream = require('combined-stream') 5 var isstream = require('isstream') 6 var Buffer = require('safe-buffer').Buffer 7 8 function Multipart (request) { 9 this.request = request 10 this.boundary = uuid() 11 this.chunked = false 12 this.body = null 13 } 14 15 Multipart.prototype.isChunked = function (options) { 16 var self = this 17 var chunked = false 18 var parts = options.data || options 19 20 if (!parts.forEach) { 21 self.request.emit('error', new Error('Argument error, options.multipart.')) 22 } 23 24 if (options.chunked !== undefined) { 25 chunked = options.chunked 26 } 27 28 if (self.request.getHeader('transfer-encoding') === 'chunked') { 29 chunked = true 30 } 31 32 if (!chunked) { 33 parts.forEach(function (part) { 34 if (typeof part.body === 'undefined') { 35 self.request.emit('error', new Error('Body attribute missing in multipart.')) 36 } 37 if (isstream(part.body)) { 38 chunked = true 39 } 40 }) 41 } 42 43 return chunked 44 } 45 46 Multipart.prototype.setHeaders = function (chunked) { 47 var self = this 48 49 if (chunked && !self.request.hasHeader('transfer-encoding')) { 50 self.request.setHeader('transfer-encoding', 'chunked') 51 } 52 53 var header = self.request.getHeader('content-type') 54 55 if (!header || header.indexOf('multipart') === -1) { 56 self.request.setHeader('content-type', 'multipart/related; boundary=' + self.boundary) 57 } else { 58 if (header.indexOf('boundary') !== -1) { 59 self.boundary = header.replace(/.*boundary=([^\s;]+).*/, '$1') 60 } else { 61 self.request.setHeader('content-type', header + '; boundary=' + self.boundary) 62 } 63 } 64 } 65 66 Multipart.prototype.build = function (parts, chunked) { 67 var self = this 68 var body = chunked ? new CombinedStream() : [] 69 70 function add (part) { 71 if (typeof part === 'number') { 72 part = part.toString() 73 } 74 return chunked ? body.append(part) : body.push(Buffer.from(part)) 75 } 76 77 if (self.request.preambleCRLF) { 78 add('\r\n') 79 } 80 81 parts.forEach(function (part) { 82 var preamble = '--' + self.boundary + '\r\n' 83 Object.keys(part).forEach(function (key) { 84 if (key === 'body') { return } 85 preamble += key + ': ' + part[key] + '\r\n' 86 }) 87 preamble += '\r\n' 88 add(preamble) 89 add(part.body) 90 add('\r\n') 91 }) 92 add('--' + self.boundary + '--') 93 94 if (self.request.postambleCRLF) { 95 add('\r\n') 96 } 97 98 return body 99 } 100 101 Multipart.prototype.onRequest = function (options) { 102 var self = this 103 104 var chunked = self.isChunked(options) 105 var parts = options.data || options 106 107 self.setHeaders(chunked) 108 self.chunked = chunked 109 self.body = self.build(parts, chunked) 110 } 111 112 exports.Multipart = Multipart