17e40f4164d982c519218450061c248b0c8d91eefeffe9c63c57146c2c2a613d4c7a55baa7b2138a4bab51430f3d5aec380a314efeaf37a6434e0c58ee19d0 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. 'use strict'
  2. var transport = require('../../../spdy-transport')
  3. var base = transport.protocol.base
  4. var constants = require('./').constants
  5. var assert = require('assert')
  6. var util = require('util')
  7. var WriteBuffer = require('wbuf')
  8. var OffsetBuffer = require('obuf')
  9. var debug = require('debug')('spdy:framer')
  10. var debugExtra = require('debug')('spdy:framer:extra')
  11. function Framer (options) {
  12. base.Framer.call(this, options)
  13. this.maxFrameSize = constants.INITIAL_MAX_FRAME_SIZE
  14. }
  15. util.inherits(Framer, base.Framer)
  16. module.exports = Framer
  17. Framer.create = function create (options) {
  18. return new Framer(options)
  19. }
  20. Framer.prototype.setMaxFrameSize = function setMaxFrameSize (size) {
  21. this.maxFrameSize = size
  22. }
  23. Framer.prototype._frame = function _frame (frame, body, callback) {
  24. debug('id=%d type=%s', frame.id, frame.type)
  25. var buffer = new WriteBuffer()
  26. buffer.reserve(constants.FRAME_HEADER_SIZE)
  27. var len = buffer.skip(3)
  28. buffer.writeUInt8(constants.frameType[frame.type])
  29. buffer.writeUInt8(frame.flags)
  30. buffer.writeUInt32BE(frame.id & 0x7fffffff)
  31. body(buffer)
  32. var frameSize = buffer.size - constants.FRAME_HEADER_SIZE
  33. len.writeUInt24BE(frameSize)
  34. var chunks = buffer.render()
  35. var toWrite = {
  36. stream: frame.id,
  37. priority: frame.priority === undefined ? false : frame.priority,
  38. chunks: chunks,
  39. callback: callback
  40. }
  41. if (this.window && frame.type === 'DATA') {
  42. var self = this
  43. this._resetTimeout()
  44. this.window.send.update(-frameSize, function () {
  45. self._resetTimeout()
  46. self.schedule(toWrite)
  47. })
  48. } else {
  49. this._resetTimeout()
  50. this.schedule(toWrite)
  51. }
  52. return chunks
  53. }
  54. Framer.prototype._split = function _split (frame) {
  55. var buf = new OffsetBuffer()
  56. for (var i = 0; i < frame.chunks.length; i++) { buf.push(frame.chunks[i]) }
  57. var frames = []
  58. while (!buf.isEmpty()) {
  59. // First frame may have reserved bytes in it
  60. var size = this.maxFrameSize
  61. if (frames.length === 0) {
  62. size -= frame.reserve
  63. }
  64. size = Math.min(size, buf.size)
  65. var frameBuf = buf.clone(size)
  66. buf.skip(size)
  67. frames.push({
  68. size: frameBuf.size,
  69. chunks: frameBuf.toChunks()
  70. })
  71. }
  72. return frames
  73. }
  74. Framer.prototype._continuationFrame = function _continuationFrame (frame,
  75. body,
  76. callback) {
  77. var frames = this._split(frame)
  78. frames.forEach(function (subFrame, i) {
  79. var isFirst = i === 0
  80. var isLast = i === frames.length - 1
  81. var flags = isLast ? constants.flags.END_HEADERS : 0
  82. // PRIORITY and friends
  83. if (isFirst) {
  84. flags |= frame.flags
  85. }
  86. this._frame({
  87. id: frame.id,
  88. priority: false,
  89. type: isFirst ? frame.type : 'CONTINUATION',
  90. flags: flags
  91. }, function (buf) {
  92. // Fill those reserved bytes
  93. if (isFirst && body) { body(buf) }
  94. buf.reserve(subFrame.size)
  95. for (var i = 0; i < subFrame.chunks.length; i++) { buf.copyFrom(subFrame.chunks[i]) }
  96. }, isLast ? callback : null)
  97. }, this)
  98. if (frames.length === 0) {
  99. this._frame({
  100. id: frame.id,
  101. priority: false,
  102. type: frame.type,
  103. flags: frame.flags | constants.flags.END_HEADERS
  104. }, function (buf) {
  105. if (body) { body(buf) }
  106. }, callback)
  107. }
  108. }
  109. Framer.prototype._compressHeaders = function _compressHeaders (headers,
  110. pairs,
  111. callback) {
  112. Object.keys(headers || {}).forEach(function (name) {
  113. var lowName = name.toLowerCase()
  114. // Not allowed in HTTP2
  115. switch (lowName) {
  116. case 'host':
  117. case 'connection':
  118. case 'keep-alive':
  119. case 'proxy-connection':
  120. case 'transfer-encoding':
  121. case 'upgrade':
  122. return
  123. }
  124. // Should be in `pairs`
  125. if (/^:/.test(lowName)) {
  126. return
  127. }
  128. // Do not compress, or index Cookie field (for security reasons)
  129. var neverIndex = lowName === 'cookie' || lowName === 'set-cookie'
  130. var value = headers[name]
  131. if (Array.isArray(value)) {
  132. for (var i = 0; i < value.length; i++) {
  133. pairs.push({
  134. name: lowName,
  135. value: value[i] + '',
  136. neverIndex: neverIndex,
  137. huffman: !neverIndex
  138. })
  139. }
  140. } else {
  141. pairs.push({
  142. name: lowName,
  143. value: value + '',
  144. neverIndex: neverIndex,
  145. huffman: !neverIndex
  146. })
  147. }
  148. })
  149. assert(this.compress !== null, 'Framer version not initialized')
  150. debugExtra('compressing headers=%j', pairs)
  151. this.compress.write([ pairs ], callback)
  152. }
  153. Framer.prototype._isDefaultPriority = function _isDefaultPriority (priority) {
  154. if (!priority) { return true }
  155. return !priority.parent &&
  156. priority.weight === constants.DEFAULT &&
  157. !priority.exclusive
  158. }
  159. Framer.prototype._defaultHeaders = function _defaultHeaders (frame, pairs) {
  160. if (!frame.path) {
  161. throw new Error('`path` is required frame argument')
  162. }
  163. pairs.push({
  164. name: ':method',
  165. value: frame.method || base.constants.DEFAULT_METHOD
  166. })
  167. pairs.push({ name: ':path', value: frame.path })
  168. pairs.push({ name: ':scheme', value: frame.scheme || 'https' })
  169. pairs.push({
  170. name: ':authority',
  171. value: frame.host ||
  172. (frame.headers && frame.headers.host) ||
  173. base.constants.DEFAULT_HOST
  174. })
  175. }
  176. Framer.prototype._headersFrame = function _headersFrame (kind, frame, callback) {
  177. var pairs = []
  178. if (kind === 'request') {
  179. this._defaultHeaders(frame, pairs)
  180. } else if (kind === 'response') {
  181. pairs.push({ name: ':status', value: (frame.status || 200) + '' })
  182. }
  183. var self = this
  184. this._compressHeaders(frame.headers, pairs, function (err, chunks) {
  185. if (err) {
  186. if (callback) {
  187. return callback(err)
  188. } else {
  189. return self.emit('error', err)
  190. }
  191. }
  192. var reserve = 0
  193. // If priority info is present, and the values are not default ones
  194. // reserve space for the priority info and add PRIORITY flag
  195. var priority = frame.priority
  196. if (!self._isDefaultPriority(priority)) { reserve = 5 }
  197. var flags = reserve === 0 ? 0 : constants.flags.PRIORITY
  198. // Mostly for testing
  199. if (frame.fin) {
  200. flags |= constants.flags.END_STREAM
  201. }
  202. self._continuationFrame({
  203. id: frame.id,
  204. type: 'HEADERS',
  205. flags: flags,
  206. reserve: reserve,
  207. chunks: chunks
  208. }, function (buf) {
  209. if (reserve === 0) {
  210. return
  211. }
  212. buf.writeUInt32BE(((priority.exclusive ? 0x80000000 : 0) |
  213. priority.parent) >>> 0)
  214. buf.writeUInt8((priority.weight | 0) - 1)
  215. }, callback)
  216. })
  217. }
  218. Framer.prototype.requestFrame = function requestFrame (frame, callback) {
  219. return this._headersFrame('request', frame, callback)
  220. }
  221. Framer.prototype.responseFrame = function responseFrame (frame, callback) {
  222. return this._headersFrame('response', frame, callback)
  223. }
  224. Framer.prototype.headersFrame = function headersFrame (frame, callback) {
  225. return this._headersFrame('headers', frame, callback)
  226. }
  227. Framer.prototype.pushFrame = function pushFrame (frame, callback) {
  228. var self = this
  229. function compress (headers, pairs, callback) {
  230. self._compressHeaders(headers, pairs, function (err, chunks) {
  231. if (err) {
  232. if (callback) {
  233. return callback(err)
  234. } else {
  235. return self.emit('error', err)
  236. }
  237. }
  238. callback(chunks)
  239. })
  240. }
  241. function sendPromise (chunks) {
  242. self._continuationFrame({
  243. id: frame.id,
  244. type: 'PUSH_PROMISE',
  245. reserve: 4,
  246. chunks: chunks
  247. }, function (buf) {
  248. buf.writeUInt32BE(frame.promisedId)
  249. })
  250. }
  251. function sendResponse (chunks, callback) {
  252. var priority = frame.priority
  253. var isDefaultPriority = self._isDefaultPriority(priority)
  254. var flags = isDefaultPriority ? 0 : constants.flags.PRIORITY
  255. // Mostly for testing
  256. if (frame.fin) {
  257. flags |= constants.flags.END_STREAM
  258. }
  259. self._continuationFrame({
  260. id: frame.promisedId,
  261. type: 'HEADERS',
  262. flags: flags,
  263. reserve: isDefaultPriority ? 0 : 5,
  264. chunks: chunks
  265. }, function (buf) {
  266. if (isDefaultPriority) {
  267. return
  268. }
  269. buf.writeUInt32BE((priority.exclusive ? 0x80000000 : 0) |
  270. priority.parent)
  271. buf.writeUInt8((priority.weight | 0) - 1)
  272. }, callback)
  273. }
  274. this._checkPush(function (err) {
  275. if (err) {
  276. return callback(err)
  277. }
  278. var pairs = {
  279. promise: [],
  280. response: []
  281. }
  282. self._defaultHeaders(frame, pairs.promise)
  283. pairs.response.push({ name: ':status', value: (frame.status || 200) + '' })
  284. compress(frame.headers, pairs.promise, function (promiseChunks) {
  285. sendPromise(promiseChunks)
  286. if (frame.response === false) {
  287. return callback(null)
  288. }
  289. compress(frame.response, pairs.response, function (responseChunks) {
  290. sendResponse(responseChunks, callback)
  291. })
  292. })
  293. })
  294. }
  295. Framer.prototype.priorityFrame = function priorityFrame (frame, callback) {
  296. this._frame({
  297. id: frame.id,
  298. priority: false,
  299. type: 'PRIORITY',
  300. flags: 0
  301. }, function (buf) {
  302. var priority = frame.priority
  303. buf.writeUInt32BE((priority.exclusive ? 0x80000000 : 0) |
  304. priority.parent)
  305. buf.writeUInt8((priority.weight | 0) - 1)
  306. }, callback)
  307. }
  308. Framer.prototype.dataFrame = function dataFrame (frame, callback) {
  309. var frames = this._split({
  310. reserve: 0,
  311. chunks: [ frame.data ]
  312. })
  313. var fin = frame.fin ? constants.flags.END_STREAM : 0
  314. var self = this
  315. frames.forEach(function (subFrame, i) {
  316. var isLast = i === frames.length - 1
  317. var flags = 0
  318. if (isLast) {
  319. flags |= fin
  320. }
  321. self._frame({
  322. id: frame.id,
  323. priority: frame.priority,
  324. type: 'DATA',
  325. flags: flags
  326. }, function (buf) {
  327. buf.reserve(subFrame.size)
  328. for (var i = 0; i < subFrame.chunks.length; i++) { buf.copyFrom(subFrame.chunks[i]) }
  329. }, isLast ? callback : null)
  330. })
  331. // Empty DATA
  332. if (frames.length === 0) {
  333. this._frame({
  334. id: frame.id,
  335. priority: frame.priority,
  336. type: 'DATA',
  337. flags: fin
  338. }, function (buf) {
  339. // No-op
  340. }, callback)
  341. }
  342. }
  343. Framer.prototype.pingFrame = function pingFrame (frame, callback) {
  344. this._frame({
  345. id: 0,
  346. type: 'PING',
  347. flags: frame.ack ? constants.flags.ACK : 0
  348. }, function (buf) {
  349. buf.copyFrom(frame.opaque)
  350. }, callback)
  351. }
  352. Framer.prototype.rstFrame = function rstFrame (frame, callback) {
  353. this._frame({
  354. id: frame.id,
  355. type: 'RST_STREAM',
  356. flags: 0
  357. }, function (buf) {
  358. buf.writeUInt32BE(constants.error[frame.code])
  359. }, callback)
  360. }
  361. Framer.prototype.prefaceFrame = function prefaceFrame (callback) {
  362. debug('preface')
  363. this._resetTimeout()
  364. this.schedule({
  365. stream: 0,
  366. priority: false,
  367. chunks: [ constants.PREFACE_BUFFER ],
  368. callback: callback
  369. })
  370. }
  371. Framer.prototype.settingsFrame = function settingsFrame (options, callback) {
  372. var key = JSON.stringify(options)
  373. var settings = Framer.settingsCache[key]
  374. if (settings) {
  375. debug('cached settings')
  376. this._resetTimeout()
  377. this.schedule({
  378. id: 0,
  379. priority: false,
  380. chunks: settings,
  381. callback: callback
  382. })
  383. return
  384. }
  385. var params = []
  386. for (var i = 0; i < constants.settingsIndex.length; i++) {
  387. var name = constants.settingsIndex[i]
  388. if (!name) {
  389. continue
  390. }
  391. // value: Infinity
  392. if (!isFinite(options[name])) {
  393. continue
  394. }
  395. if (options[name] !== undefined) {
  396. params.push({ key: i, value: options[name] })
  397. }
  398. }
  399. var bodySize = params.length * 6
  400. var chunks = this._frame({
  401. id: 0,
  402. type: 'SETTINGS',
  403. flags: 0
  404. }, function (buffer) {
  405. buffer.reserve(bodySize)
  406. for (var i = 0; i < params.length; i++) {
  407. var param = params[i]
  408. buffer.writeUInt16BE(param.key)
  409. buffer.writeUInt32BE(param.value)
  410. }
  411. }, callback)
  412. Framer.settingsCache[key] = chunks
  413. }
  414. Framer.settingsCache = {}
  415. Framer.prototype.ackSettingsFrame = function ackSettingsFrame (callback) {
  416. /* var chunks = */ this._frame({
  417. id: 0,
  418. type: 'SETTINGS',
  419. flags: constants.flags.ACK
  420. }, function (buffer) {
  421. // No-op
  422. }, callback)
  423. }
  424. Framer.prototype.windowUpdateFrame = function windowUpdateFrame (frame,
  425. callback) {
  426. this._frame({
  427. id: frame.id,
  428. type: 'WINDOW_UPDATE',
  429. flags: 0
  430. }, function (buffer) {
  431. buffer.reserve(4)
  432. buffer.writeInt32BE(frame.delta)
  433. }, callback)
  434. }
  435. Framer.prototype.goawayFrame = function goawayFrame (frame, callback) {
  436. this._frame({
  437. type: 'GOAWAY',
  438. id: 0,
  439. flags: 0
  440. }, function (buf) {
  441. buf.reserve(8)
  442. // Last-good-stream-ID
  443. buf.writeUInt32BE(frame.lastId & 0x7fffffff)
  444. // Code
  445. buf.writeUInt32BE(constants.goaway[frame.code])
  446. // Extra debugging information
  447. if (frame.extra) { buf.write(frame.extra) }
  448. }, callback)
  449. }
  450. Framer.prototype.xForwardedFor = function xForwardedFor (frame, callback) {
  451. this._frame({
  452. type: 'X_FORWARDED_FOR',
  453. id: 0,
  454. flags: 0
  455. }, function (buf) {
  456. buf.write(frame.host)
  457. }, callback)
  458. }