[6a3a178] | 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
|
---|