multipart.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. 'use strict'
  2. var uuid = require('node-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. return chunked ? body.append(part) : body.push(new Buffer(part))
  57. }
  58. if (self.request.preambleCRLF) {
  59. add('\r\n')
  60. }
  61. parts.forEach(function (part) {
  62. var preamble = '--' + self.boundary + '\r\n'
  63. Object.keys(part).forEach(function (key) {
  64. if (key === 'body') { return }
  65. preamble += key + ': ' + part[key] + '\r\n'
  66. })
  67. preamble += '\r\n'
  68. add(preamble)
  69. add(part.body)
  70. add('\r\n')
  71. })
  72. add('--' + self.boundary + '--')
  73. if (self.request.postambleCRLF) {
  74. add('\r\n')
  75. }
  76. return body
  77. }
  78. Multipart.prototype.onRequest = function (options) {
  79. var self = this
  80. var chunked = self.isChunked(options)
  81. , parts = options.data || options
  82. self.setHeaders(chunked)
  83. self.chunked = chunked
  84. self.body = self.build(parts, chunked)
  85. }
  86. exports.Multipart = Multipart