window.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. 'use strict'
  2. var util = require('util')
  3. var EventEmitter = require('events').EventEmitter
  4. var debug = {
  5. server: require('debug')('spdy:window:server'),
  6. client: require('debug')('spdy:window:client')
  7. }
  8. function Side (window, name, options) {
  9. EventEmitter.call(this)
  10. this.name = name
  11. this.window = window
  12. this.current = options.size
  13. this.max = options.size
  14. this.limit = options.max
  15. this.lowWaterMark = options.lowWaterMark === undefined
  16. ? this.max / 2
  17. : options.lowWaterMark
  18. this._refilling = false
  19. this._refillQueue = []
  20. }
  21. util.inherits(Side, EventEmitter)
  22. Side.prototype.setMax = function setMax (max) {
  23. this.window.debug('id=%d side=%s setMax=%d',
  24. this.window.id,
  25. this.name,
  26. max)
  27. this.max = max
  28. this.lowWaterMark = this.max / 2
  29. }
  30. Side.prototype.updateMax = function updateMax (max) {
  31. var delta = max - this.max
  32. this.window.debug('id=%d side=%s updateMax=%d delta=%d',
  33. this.window.id,
  34. this.name,
  35. max,
  36. delta)
  37. this.max = max
  38. this.lowWaterMark = max / 2
  39. this.update(delta)
  40. }
  41. Side.prototype.setLowWaterMark = function setLowWaterMark (lwm) {
  42. this.lowWaterMark = lwm
  43. }
  44. Side.prototype.update = function update (size, callback) {
  45. // Not enough space for the update, wait for refill
  46. if (size <= 0 && callback && this.isEmpty()) {
  47. this.window.debug('id=%d side=%s wait for refill=%d [%d/%d]',
  48. this.window.id,
  49. this.name,
  50. -size,
  51. this.current,
  52. this.max)
  53. this._refillQueue.push({
  54. size: size,
  55. callback: callback
  56. })
  57. return
  58. }
  59. this.current += size
  60. if (this.current > this.limit) {
  61. this.emit('overflow')
  62. return
  63. }
  64. this.window.debug('id=%d side=%s update by=%d [%d/%d]',
  65. this.window.id,
  66. this.name,
  67. size,
  68. this.current,
  69. this.max)
  70. // Time to send WINDOW_UPDATE
  71. if (size < 0 && this.isDraining()) {
  72. this.window.debug('id=%d side=%s drained', this.window.id, this.name)
  73. this.emit('drain')
  74. }
  75. // Time to write
  76. if (size > 0 && this.current > 0 && this.current <= size) {
  77. this.window.debug('id=%d side=%s full', this.window.id, this.name)
  78. this.emit('full')
  79. }
  80. this._processRefillQueue()
  81. if (callback) { process.nextTick(callback) }
  82. }
  83. Side.prototype.getCurrent = function getCurrent () {
  84. return this.current
  85. }
  86. Side.prototype.getMax = function getMax () {
  87. return this.max
  88. }
  89. Side.prototype.getDelta = function getDelta () {
  90. return this.max - this.current
  91. }
  92. Side.prototype.isDraining = function isDraining () {
  93. return this.current <= this.lowWaterMark
  94. }
  95. Side.prototype.isEmpty = function isEmpty () {
  96. return this.current <= 0
  97. }
  98. // Private
  99. Side.prototype._processRefillQueue = function _processRefillQueue () {
  100. // Prevent recursion
  101. if (this._refilling) {
  102. return
  103. }
  104. this._refilling = true
  105. while (this._refillQueue.length > 0) {
  106. var item = this._refillQueue[0]
  107. if (this.isEmpty()) {
  108. break
  109. }
  110. this.window.debug('id=%d side=%s refilled for size=%d',
  111. this.window.id,
  112. this.name,
  113. -item.size)
  114. this._refillQueue.shift()
  115. this.update(item.size, item.callback)
  116. }
  117. this._refilling = false
  118. }
  119. function Window (options) {
  120. this.id = options.id
  121. this.isServer = options.isServer
  122. this.debug = this.isServer ? debug.server : debug.client
  123. this.recv = new Side(this, 'recv', options.recv)
  124. this.send = new Side(this, 'send', options.send)
  125. }
  126. module.exports = Window
  127. Window.prototype.clone = function clone (id) {
  128. return new Window({
  129. id: id,
  130. isServer: this.isServer,
  131. recv: {
  132. size: this.recv.max,
  133. max: this.recv.limit,
  134. lowWaterMark: this.recv.lowWaterMark
  135. },
  136. send: {
  137. size: this.send.max,
  138. max: this.send.limit,
  139. lowWaterMark: this.send.lowWaterMark
  140. }
  141. })
  142. }