
var fs = require('fs')
var isstream = require('isstream')


function contentLength (req, options, done) {
  if (options.headers.get('transfer-encoding') === 'chunked' ||
      options.headers.get('content-length') !== undefined) {
    return done()
  }

  if (options.body && !options.multipart) {
    if (isstream(options.body)) {
      contentLengthAsync(options.body, done)
    }
    else {
      done(contentLengthSync(options.body))
    }
  }
  else if (req._src) {
    contentLengthAsync(req._src, done)
  }
  else if (options.multipart) {
    contentLengthMultipart(options.body, done)
  }
}

function contentLengthSync (body) {
  var length = 0
  if (typeof body === 'string') {
    length = Buffer.byteLength(body)
  }
  else if (Array.isArray(body)) {
    length = body.reduce(function (a, b) {return a + b.length}, 0)
  }
  else if (Buffer.isBuffer(body)) {
    length = body.length
  }

  return length
}

function contentLengthAsync (body, done) {
  // file stream
  if (body.hasOwnProperty('fd')) {
    fs.stat(body.path, function (err, stats) {
      if (err) return done(0)
      done(stats.size)
    })
  }
  // http response
  else if (body.hasOwnProperty('httpVersion')) {
    done(parseInt(body.headers['content-length']))
  }
  // request
  else if (body.hasOwnProperty('httpModule')) {
    body.on('response', function (res) {
      done(parseInt(res.headers['content-length']))
    })
  }
  // request-next
  else if (body.hasOwnProperty('_client')) {
    body.on('response', function (res) {
      done(parseInt(res.headers.get('content-length')) || 0)
    })
  }
  else {
    done(0)
  }
}

function contentLengthMultipart (body, done) {
  var length = 0
  body._items.forEach(function (item) {
    length += contentLengthSync(item)
  })
  if (!body._streams.length) return done(length)

  // should be parallel
  ;(function loop (index) {
    if (index === body._streams.length) {
      return done(length)
    }
    var stream = body._streams[index]
    if (stream._knownLength) {
      length += stream._knownLength
      loop(++index)
    }
    else {
      contentLengthAsync(stream, function (len) {
        length += len
        loop(++index)
      })
    }
  }(0))
}

module.exports = contentLength

@@ -1,27 +1,4 @@
-%0Avar fs = require('fs')
 %0Avar
@@ -29,16 +29,55 @@
 tream')%0A
+  , length = require('request-length')%0A
 %0A%0Afuncti
@@ -344,38 +344,32 @@
 y)) %7B%0A      
-contentL
+l
 ength
-A
+.a
 sync(options
@@ -409,30 +409,24 @@
    done(
-contentL
+l
 ength
-S
+.s
 ync(opti
@@ -469,38 +469,32 @@
 _src) %7B%0A    
-contentL
+l
 ength
-A
+.a
 sync(req._sr
@@ -546,1655 +546,48 @@
     
-contentLengthMultipart(options.body, done)%0A  %7D%0A%7D%0A%0Afunction contentLengthSync (body) %7B%0A  var length = 0%0A  if (typeof body === 'string') %7B%0A    length = Buffer.byteLength(body)%0A  %7D%0A  else if (Array.isArray(body)) %7B%0A    length = body.reduce(function (a, b) %7Breturn a + b.length%7D, 0)%0A  %7D%0A  else if (Buffer.isBuffer(body)) %7B%0A    length = body.length%0A  %7D%0A%0A  return length%0A%7D%0A%0Afunction contentLengthAsync (body, done) %7B%0A  // file stream%0A  if (body.hasOwnProperty('fd')) %7B%0A    fs.stat(body.path, function (err, stats) %7B%0A      if (err) return done(0)%0A      done(stats.size)%0A    %7D)%0A  %7D%0A  // http response%0A  else if (body.hasOwnProperty('httpVersion')) %7B%0A    done(parseInt(body.headers%5B'content-length'%5D))%0A  %7D%0A  // request%0A  else if (body.hasOwnProperty('httpModule')) %7B%0A    body.on('response', function (res) %7B%0A      done(parseInt(res.headers%5B'content-length'%5D))%0A    %7D)%0A  %7D%0A  // request-next%0A  else if (body.hasOwnProperty('_client')) %7B%0A    body.on('response', function (res) %7B%0A      done(parseInt(res.headers.get('content-length')) %7C%7C 0)%0A    %7D)%0A  %7D%0A  else %7B%0A    done(0)%0A  %7D%0A%7D%0A%0Afunction contentLengthMultipart (body, done) %7B%0A  var length = 0%0A  body._items.forEach(function (item) %7B%0A    length += contentLengthSync(item)%0A  %7D)%0A  if (!body._streams.length) return done(length)%0A%0A  // should be parallel%0A  ;(function loop (index) %7B%0A    if (index === body._streams.length) %7B%0A      return done(length)%0A    %7D%0A    var stream = body._streams%5Bindex%5D%0A    if (stream._knownLength) %7B%0A      length += stream._knownLength%0A      loop(++index)%0A    %7D%0A    else %7B%0A      contentLengthAsync(stream, function (len) %7B%0A        length += len%0A        loop(++index)%0A      %7D)%0A    %7D%0A  %7D(0))
+length.multipart(options.body, done)%0A  %7D
 %0A%7D%0A%0A

Move content-length logic into separate module