twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

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