handle.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. 'use strict'
  2. var assert = require('assert')
  3. var thing = require('handle-thing')
  4. var httpDeceiver = require('http-deceiver')
  5. var util = require('util')
  6. function Handle (options, stream, socket) {
  7. var state = {}
  8. this._spdyState = state
  9. state.options = options || {}
  10. state.stream = stream
  11. state.socket = null
  12. state.rawSocket = socket || stream.connection.socket
  13. state.deceiver = null
  14. state.ending = false
  15. var self = this
  16. thing.call(this, stream, {
  17. getPeerName: function () {
  18. return self._getPeerName()
  19. },
  20. close: function (callback) {
  21. return self._closeCallback(callback)
  22. }
  23. })
  24. if (!state.stream) {
  25. this.on('stream', function (stream) {
  26. state.stream = stream
  27. })
  28. }
  29. }
  30. util.inherits(Handle, thing)
  31. module.exports = Handle
  32. Handle.create = function create (options, stream, socket) {
  33. return new Handle(options, stream, socket)
  34. }
  35. Handle.prototype._getPeerName = function _getPeerName () {
  36. var state = this._spdyState
  37. if (state.rawSocket._getpeername) {
  38. return state.rawSocket._getpeername()
  39. }
  40. return null
  41. }
  42. Handle.prototype._closeCallback = function _closeCallback (callback) {
  43. var state = this._spdyState
  44. var stream = state.stream
  45. if (state.ending) {
  46. // The .end() method of the stream may be called by us or by the
  47. // .shutdown() method in our super-class. If the latter has already been
  48. // called, then calling the .end() method below will have no effect, with
  49. // the result that the callback will never get executed, leading to an ever
  50. // so subtle memory leak.
  51. if (stream._writableState.finished) {
  52. // NOTE: it is important to call `setImmediate` instead of `nextTick`,
  53. // since this is how regular `handle.close()` works in node.js core.
  54. //
  55. // Using `nextTick` will lead to `net.Socket` emitting `close` before
  56. // `end` on UV_EOF. This results in aborted request without `end` event.
  57. setImmediate(callback)
  58. } else if (stream._writableState.ending) {
  59. stream.once('finish', function () {
  60. callback(null)
  61. })
  62. } else {
  63. stream.end(callback)
  64. }
  65. } else {
  66. stream.abort(callback)
  67. }
  68. // Only a single end is allowed
  69. state.ending = false
  70. }
  71. Handle.prototype.getStream = function getStream (callback) {
  72. var state = this._spdyState
  73. if (!callback) {
  74. assert(state.stream)
  75. return state.stream
  76. }
  77. if (state.stream) {
  78. process.nextTick(function () {
  79. callback(state.stream)
  80. })
  81. return
  82. }
  83. this.on('stream', callback)
  84. }
  85. Handle.prototype.assignSocket = function assignSocket (socket, options) {
  86. var state = this._spdyState
  87. state.socket = socket
  88. state.deceiver = httpDeceiver.create(socket, options)
  89. function onStreamError (err) {
  90. state.socket.emit('error', err)
  91. }
  92. this.getStream(function (stream) {
  93. stream.on('error', onStreamError)
  94. })
  95. }
  96. Handle.prototype.assignClientRequest = function assignClientRequest (req) {
  97. var state = this._spdyState
  98. var oldEnd = req.end
  99. var oldSend = req._send
  100. // Catch the headers before request will be sent
  101. var self = this
  102. // For old nodes
  103. if (thing.mode !== 'modern') {
  104. req.end = function end () {
  105. this.end = oldEnd
  106. this._send('')
  107. return this.end.apply(this, arguments)
  108. }
  109. }
  110. req._send = function send (data) {
  111. this._headerSent = true
  112. // for v0.10 and below, otherwise it will set `hot = false` and include
  113. // headers in first write
  114. this._header = 'ignore me'
  115. // To prevent exception
  116. this.connection = state.socket
  117. // It is very important to leave this here, otherwise it will be executed
  118. // on a next tick, after `_send` will perform write
  119. self.getStream(function (stream) {
  120. if (!stream.connection._isGoaway(stream.id)) {
  121. stream.send()
  122. }
  123. })
  124. // We are ready to create stream
  125. self.emit('needStream')
  126. // Ensure that the connection is still ok to use
  127. if (state.stream && state.stream.connection._isGoaway(state.stream.id)) {
  128. return
  129. }
  130. req._send = oldSend
  131. // Ignore empty writes
  132. if (req.method === 'GET' && data.length === 0) {
  133. return
  134. }
  135. return req._send.apply(this, arguments)
  136. }
  137. // No chunked encoding
  138. req.useChunkedEncodingByDefault = false
  139. req.on('finish', function () {
  140. req.socket.end()
  141. })
  142. }
  143. Handle.prototype.assignRequest = function assignRequest (req) {
  144. // Emit trailing headers
  145. this.getStream(function (stream) {
  146. stream.on('headers', function (headers) {
  147. req.emit('trailers', headers)
  148. })
  149. })
  150. }
  151. Handle.prototype.assignResponse = function assignResponse (res) {
  152. var self = this
  153. res.addTrailers = function addTrailers (headers) {
  154. self.getStream(function (stream) {
  155. stream.sendHeaders(headers)
  156. })
  157. }
  158. }
  159. Handle.prototype._transformHeaders = function _transformHeaders (kind, headers) {
  160. var state = this._spdyState
  161. var res = {}
  162. var keys = Object.keys(headers)
  163. if (kind === 'request' && state.options['x-forwarded-for']) {
  164. var xforwarded = state.stream.connection.getXForwardedFor()
  165. if (xforwarded !== null) {
  166. res['x-forwarded-for'] = xforwarded
  167. }
  168. }
  169. for (var i = 0; i < keys.length; i++) {
  170. var key = keys[i]
  171. var value = headers[key]
  172. if (key === ':authority') {
  173. res.host = value
  174. }
  175. if (/^:/.test(key)) {
  176. continue
  177. }
  178. res[key] = value
  179. }
  180. return res
  181. }
  182. Handle.prototype.emitRequest = function emitRequest () {
  183. var state = this._spdyState
  184. var stream = state.stream
  185. state.deceiver.emitRequest({
  186. method: stream.method,
  187. path: stream.path,
  188. headers: this._transformHeaders('request', stream.headers)
  189. })
  190. }
  191. Handle.prototype.emitResponse = function emitResponse (status, headers) {
  192. var state = this._spdyState
  193. state.deceiver.emitResponse({
  194. status: status,
  195. headers: this._transformHeaders('response', headers)
  196. })
  197. }