catalyst-proxy/src/thread.coffee
2014-01-06 19:19:44 +01:00

121 lines
2.8 KiB
CoffeeScript

http = require 'http'
{ Duplex } = require 'stream'
_ = require 'lodash'
createRange = (start = 0, end = null) ->
if end is null
return "bytes=#{start}-"
else
return "bytes=#{start}-#{end}"
parseContentRange = (str = null) ->
return null if str is null
match = str.match /bytes ([0-9]+)-([0-9]+)\/([0-9]+)/
range = [(parseInt match[1]), (parseInt match[2]), (parseInt match[3])]
return range
module.exports = class Thread extends Duplex
constructor: (offset, length, reqOpts, opts) ->
super()
@_reading = false
@_readable = false
@_tries = 0
@connecting = false
@connected = false
@range = [offset, offset + length]
@offset = offset
@reqOpts = _.clone reqOpts
@reqOpts.headers = _.clone reqOpts.headers
@opts = _.clone opts
# Tries to connect
_connect: ->
@connecting = true
# Set range header
if @range[0] isnt 0
end = if @opts.endOffset then (@opts.endOffset - 1) else null
@reqOpts.headers['range'] = createRange @offset, end
else if @reqOpts.headers['range']
delete @reqOpts.headers['range']
# Create request
@_req = http.request @reqOpts
# Listen to response
@_req.on 'response', (@_res) =>
@connected = true
@connecting = false
range = parseContentRange @_res.headers['content-range']
@length = (parseInt @_res.headers['content-length']) or null
# Parse and validate range
if (range and (range[0] isnt @offset)) or (not range and @offset isnt 0)
console.log "received invalid range: #{range}, expected #{@offset},#{@offset + @length - 1}"
return
# Push data when available
@_res.on 'readable', =>
@_readable = true
@_readRes() if @_reading
@_res.on 'end', =>
if @offset is @range[1]
@push null
else
@connected = false
# Notify the user
@emit 'disconnect'
@emit 'connect', @_res
@_req.on 'error', (e) =>
@emit 'error', e
_readRes: ->
data = @_res.read()
if data
@_reading = @push data
@offset += data.length
if not @crossed and @offset >= @range[1]
@crossed = true
@emit 'cross'
_read: ->
@_reading = true
@_readRes() if @_readable
_write: (chunk, encoding, callback) ->
@_req.write chunk, encoding, callback
# Starts the thread
start: ->
@_connect()
# Stops the thread
stop: ->
@_readable = false
@connecting = false
@connected = false
@_req.removeAllListeners()
@_req.on 'error', (e) -> console.log 'req error', e # Dummy error listener
@_req.connection.destroy()
if @_res
@_res.removeAllListeners()
@_res.on 'error', (e) -> console.log 'res error', e # Dummy error listener
@_res.connection.destroy()
# Ends the request
end: ->
@_req.end()