multipart.js 2.6 KB

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