You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
174 lines
3.7 KiB
174 lines
3.7 KiB
'use strict' |
|
|
|
var util = require('util') |
|
var EventEmitter = require('events').EventEmitter |
|
var debug = { |
|
server: require('debug')('spdy:window:server'), |
|
client: require('debug')('spdy:window:client') |
|
} |
|
|
|
function Side (window, name, options) { |
|
EventEmitter.call(this) |
|
|
|
this.name = name |
|
this.window = window |
|
this.current = options.size |
|
this.max = options.size |
|
this.limit = options.max |
|
this.lowWaterMark = options.lowWaterMark === undefined |
|
? this.max / 2 |
|
: options.lowWaterMark |
|
|
|
this._refilling = false |
|
this._refillQueue = [] |
|
} |
|
util.inherits(Side, EventEmitter) |
|
|
|
Side.prototype.setMax = function setMax (max) { |
|
this.window.debug('id=%d side=%s setMax=%d', |
|
this.window.id, |
|
this.name, |
|
max) |
|
this.max = max |
|
this.lowWaterMark = this.max / 2 |
|
} |
|
|
|
Side.prototype.updateMax = function updateMax (max) { |
|
var delta = max - this.max |
|
this.window.debug('id=%d side=%s updateMax=%d delta=%d', |
|
this.window.id, |
|
this.name, |
|
max, |
|
delta) |
|
|
|
this.max = max |
|
this.lowWaterMark = max / 2 |
|
|
|
this.update(delta) |
|
} |
|
|
|
Side.prototype.setLowWaterMark = function setLowWaterMark (lwm) { |
|
this.lowWaterMark = lwm |
|
} |
|
|
|
Side.prototype.update = function update (size, callback) { |
|
// Not enough space for the update, wait for refill |
|
if (size <= 0 && callback && this.isEmpty()) { |
|
this.window.debug('id=%d side=%s wait for refill=%d [%d/%d]', |
|
this.window.id, |
|
this.name, |
|
-size, |
|
this.current, |
|
this.max) |
|
this._refillQueue.push({ |
|
size: size, |
|
callback: callback |
|
}) |
|
return |
|
} |
|
|
|
this.current += size |
|
|
|
if (this.current > this.limit) { |
|
this.emit('overflow') |
|
return |
|
} |
|
|
|
this.window.debug('id=%d side=%s update by=%d [%d/%d]', |
|
this.window.id, |
|
this.name, |
|
size, |
|
this.current, |
|
this.max) |
|
|
|
// Time to send WINDOW_UPDATE |
|
if (size < 0 && this.isDraining()) { |
|
this.window.debug('id=%d side=%s drained', this.window.id, this.name) |
|
this.emit('drain') |
|
} |
|
|
|
// Time to write |
|
if (size > 0 && this.current > 0 && this.current <= size) { |
|
this.window.debug('id=%d side=%s full', this.window.id, this.name) |
|
this.emit('full') |
|
} |
|
|
|
this._processRefillQueue() |
|
|
|
if (callback) { process.nextTick(callback) } |
|
} |
|
|
|
Side.prototype.getCurrent = function getCurrent () { |
|
return this.current |
|
} |
|
|
|
Side.prototype.getMax = function getMax () { |
|
return this.max |
|
} |
|
|
|
Side.prototype.getDelta = function getDelta () { |
|
return this.max - this.current |
|
} |
|
|
|
Side.prototype.isDraining = function isDraining () { |
|
return this.current <= this.lowWaterMark |
|
} |
|
|
|
Side.prototype.isEmpty = function isEmpty () { |
|
return this.current <= 0 |
|
} |
|
|
|
// Private |
|
|
|
Side.prototype._processRefillQueue = function _processRefillQueue () { |
|
// Prevent recursion |
|
if (this._refilling) { |
|
return |
|
} |
|
this._refilling = true |
|
|
|
while (this._refillQueue.length > 0) { |
|
var item = this._refillQueue[0] |
|
|
|
if (this.isEmpty()) { |
|
break |
|
} |
|
|
|
this.window.debug('id=%d side=%s refilled for size=%d', |
|
this.window.id, |
|
this.name, |
|
-item.size) |
|
|
|
this._refillQueue.shift() |
|
this.update(item.size, item.callback) |
|
} |
|
|
|
this._refilling = false |
|
} |
|
|
|
function Window (options) { |
|
this.id = options.id |
|
this.isServer = options.isServer |
|
this.debug = this.isServer ? debug.server : debug.client |
|
|
|
this.recv = new Side(this, 'recv', options.recv) |
|
this.send = new Side(this, 'send', options.send) |
|
} |
|
module.exports = Window |
|
|
|
Window.prototype.clone = function clone (id) { |
|
return new Window({ |
|
id: id, |
|
isServer: this.isServer, |
|
recv: { |
|
size: this.recv.max, |
|
max: this.recv.limit, |
|
lowWaterMark: this.recv.lowWaterMark |
|
}, |
|
send: { |
|
size: this.send.max, |
|
max: this.send.limit, |
|
lowWaterMark: this.send.lowWaterMark |
|
} |
|
}) |
|
}
|
|
|