123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- '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
- }
- })
- }
|