264236dcd738ee6992e46b81040ecbe690b65f323ae3a4823f7a613f4e749d672874dcf126f012eb14fd9121d4e3dabec5bad3c011dea0664de27a38db9288 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. 'use strict'
  2. var transport = require('../../../spdy-transport')
  3. var constants = require('./').constants
  4. var base = transport.protocol.base
  5. var utils = base.utils
  6. var assert = require('assert')
  7. var util = require('util')
  8. var Buffer = require('buffer').Buffer
  9. var WriteBuffer = require('wbuf')
  10. var debug = require('debug')('spdy:framer')
  11. function Framer (options) {
  12. base.Framer.call(this, options)
  13. }
  14. util.inherits(Framer, base.Framer)
  15. module.exports = Framer
  16. Framer.create = function create (options) {
  17. return new Framer(options)
  18. }
  19. Framer.prototype.setMaxFrameSize = function setMaxFrameSize (size) {
  20. // http2-only
  21. }
  22. Framer.prototype.headersToDict = function headersToDict (headers,
  23. preprocess,
  24. callback) {
  25. function stringify (value) {
  26. if (value !== undefined) {
  27. if (Array.isArray(value)) {
  28. return value.join('\x00')
  29. } else if (typeof value === 'string') {
  30. return value
  31. } else {
  32. return value.toString()
  33. }
  34. } else {
  35. return ''
  36. }
  37. }
  38. // Lower case of all headers keys
  39. var loweredHeaders = {}
  40. Object.keys(headers || {}).map(function (key) {
  41. loweredHeaders[key.toLowerCase()] = headers[key]
  42. })
  43. // Allow outer code to add custom headers or remove something
  44. if (preprocess) { preprocess(loweredHeaders) }
  45. // Transform object into kv pairs
  46. var size = this.version === 2 ? 2 : 4
  47. var len = size
  48. var pairs = Object.keys(loweredHeaders).filter(function (key) {
  49. var lkey = key.toLowerCase()
  50. // Will be in `:host`
  51. if (lkey === 'host' && this.version >= 3) {
  52. return false
  53. }
  54. return lkey !== 'connection' && lkey !== 'keep-alive' &&
  55. lkey !== 'proxy-connection' && lkey !== 'transfer-encoding'
  56. }, this).map(function (key) {
  57. var klen = Buffer.byteLength(key)
  58. var value = stringify(loweredHeaders[key])
  59. var vlen = Buffer.byteLength(value)
  60. len += size * 2 + klen + vlen
  61. return [klen, key, vlen, value]
  62. })
  63. var block = new WriteBuffer()
  64. block.reserve(len)
  65. if (this.version === 2) {
  66. block.writeUInt16BE(pairs.length)
  67. } else {
  68. block.writeUInt32BE(pairs.length)
  69. }
  70. pairs.forEach(function (pair) {
  71. // Write key length
  72. if (this.version === 2) {
  73. block.writeUInt16BE(pair[0])
  74. } else {
  75. block.writeUInt32BE(pair[0])
  76. }
  77. // Write key
  78. block.write(pair[1])
  79. // Write value length
  80. if (this.version === 2) {
  81. block.writeUInt16BE(pair[2])
  82. } else {
  83. block.writeUInt32BE(pair[2])
  84. }
  85. // Write value
  86. block.write(pair[3])
  87. }, this)
  88. assert(this.compress !== null, 'Framer version not initialized')
  89. this.compress.write(block.render(), callback)
  90. }
  91. Framer.prototype._frame = function _frame (frame, body, callback) {
  92. if (!this.version) {
  93. this.on('version', function () {
  94. this._frame(frame, body, callback)
  95. })
  96. return
  97. }
  98. debug('id=%d type=%s', frame.id, frame.type)
  99. var buffer = new WriteBuffer()
  100. buffer.writeUInt16BE(0x8000 | this.version)
  101. buffer.writeUInt16BE(constants.frameType[frame.type])
  102. buffer.writeUInt8(frame.flags)
  103. var len = buffer.skip(3)
  104. body(buffer)
  105. var frameSize = buffer.size - constants.FRAME_HEADER_SIZE
  106. len.writeUInt24BE(frameSize)
  107. var chunks = buffer.render()
  108. var toWrite = {
  109. stream: frame.id,
  110. priority: false,
  111. chunks: chunks,
  112. callback: callback
  113. }
  114. this._resetTimeout()
  115. this.schedule(toWrite)
  116. return chunks
  117. }
  118. Framer.prototype._synFrame = function _synFrame (frame, callback) {
  119. var self = this
  120. if (!frame.path) {
  121. throw new Error('`path` is required frame argument')
  122. }
  123. function preprocess (headers) {
  124. var method = frame.method || base.constants.DEFAULT_METHOD
  125. var version = frame.version || 'HTTP/1.1'
  126. var scheme = frame.scheme || 'https'
  127. var host = frame.host ||
  128. (frame.headers && frame.headers.host) ||
  129. base.constants.DEFAULT_HOST
  130. if (self.version === 2) {
  131. headers.method = method
  132. headers.version = version
  133. headers.url = frame.path
  134. headers.scheme = scheme
  135. headers.host = host
  136. if (frame.status) {
  137. headers.status = frame.status
  138. }
  139. } else {
  140. headers[':method'] = method
  141. headers[':version'] = version
  142. headers[':path'] = frame.path
  143. headers[':scheme'] = scheme
  144. headers[':host'] = host
  145. if (frame.status) { headers[':status'] = frame.status }
  146. }
  147. }
  148. this.headersToDict(frame.headers, preprocess, function (err, chunks) {
  149. if (err) {
  150. if (callback) {
  151. return callback(err)
  152. } else {
  153. return self.emit('error', err)
  154. }
  155. }
  156. self._frame({
  157. type: 'SYN_STREAM',
  158. id: frame.id,
  159. flags: frame.fin ? constants.flags.FLAG_FIN : 0
  160. }, function (buf) {
  161. buf.reserve(10)
  162. buf.writeUInt32BE(frame.id & 0x7fffffff)
  163. buf.writeUInt32BE(frame.associated & 0x7fffffff)
  164. var weight = (frame.priority && frame.priority.weight) ||
  165. constants.DEFAULT_WEIGHT
  166. // We only have 3 bits for priority in SPDY, try to fit it into this
  167. var priority = utils.weightToPriority(weight)
  168. buf.writeUInt8(priority << 5)
  169. // CREDENTIALS slot
  170. buf.writeUInt8(0)
  171. for (var i = 0; i < chunks.length; i++) {
  172. buf.copyFrom(chunks[i])
  173. }
  174. }, callback)
  175. })
  176. }
  177. Framer.prototype.requestFrame = function requestFrame (frame, callback) {
  178. this._synFrame({
  179. id: frame.id,
  180. fin: frame.fin,
  181. associated: 0,
  182. method: frame.method,
  183. version: frame.version,
  184. scheme: frame.scheme,
  185. host: frame.host,
  186. path: frame.path,
  187. priority: frame.priority,
  188. headers: frame.headers
  189. }, callback)
  190. }
  191. Framer.prototype.responseFrame = function responseFrame (frame, callback) {
  192. var self = this
  193. var reason = frame.reason
  194. if (!reason) {
  195. reason = constants.statusReason[frame.status]
  196. }
  197. function preprocess (headers) {
  198. if (self.version === 2) {
  199. headers.status = frame.status + ' ' + reason
  200. headers.version = 'HTTP/1.1'
  201. } else {
  202. headers[':status'] = frame.status + ' ' + reason
  203. headers[':version'] = 'HTTP/1.1'
  204. }
  205. }
  206. this.headersToDict(frame.headers, preprocess, function (err, chunks) {
  207. if (err) {
  208. if (callback) {
  209. return callback(err)
  210. } else {
  211. return self.emit('error', err)
  212. }
  213. }
  214. self._frame({
  215. type: 'SYN_REPLY',
  216. id: frame.id,
  217. flags: 0
  218. }, function (buf) {
  219. buf.reserve(self.version === 2 ? 6 : 4)
  220. buf.writeUInt32BE(frame.id & 0x7fffffff)
  221. // Unused data
  222. if (self.version === 2) {
  223. buf.writeUInt16BE(0)
  224. }
  225. for (var i = 0; i < chunks.length; i++) {
  226. buf.copyFrom(chunks[i])
  227. }
  228. }, callback)
  229. })
  230. }
  231. Framer.prototype.pushFrame = function pushFrame (frame, callback) {
  232. var self = this
  233. this._checkPush(function (err) {
  234. if (err) { return callback(err) }
  235. self._synFrame({
  236. id: frame.promisedId,
  237. associated: frame.id,
  238. method: frame.method,
  239. status: frame.status || 200,
  240. version: frame.version,
  241. scheme: frame.scheme,
  242. host: frame.host,
  243. path: frame.path,
  244. priority: frame.priority,
  245. // Merge everything together, there is no difference in SPDY protocol
  246. headers: Object.assign(Object.assign({}, frame.headers), frame.response)
  247. }, callback)
  248. })
  249. }
  250. Framer.prototype.headersFrame = function headersFrame (frame, callback) {
  251. var self = this
  252. this.headersToDict(frame.headers, null, function (err, chunks) {
  253. if (err) {
  254. if (callback) { return callback(err) } else {
  255. return self.emit('error', err)
  256. }
  257. }
  258. self._frame({
  259. type: 'HEADERS',
  260. id: frame.id,
  261. priority: false,
  262. flags: 0
  263. }, function (buf) {
  264. buf.reserve(4 + (self.version === 2 ? 2 : 0))
  265. buf.writeUInt32BE(frame.id & 0x7fffffff)
  266. // Unused data
  267. if (self.version === 2) { buf.writeUInt16BE(0) }
  268. for (var i = 0; i < chunks.length; i++) {
  269. buf.copyFrom(chunks[i])
  270. }
  271. }, callback)
  272. })
  273. }
  274. Framer.prototype.dataFrame = function dataFrame (frame, callback) {
  275. if (!this.version) {
  276. return this.on('version', function () {
  277. this.dataFrame(frame, callback)
  278. })
  279. }
  280. debug('id=%d type=DATA', frame.id)
  281. var buffer = new WriteBuffer()
  282. buffer.reserve(8 + frame.data.length)
  283. buffer.writeUInt32BE(frame.id & 0x7fffffff)
  284. buffer.writeUInt8(frame.fin ? 0x01 : 0x0)
  285. buffer.writeUInt24BE(frame.data.length)
  286. buffer.copyFrom(frame.data)
  287. var chunks = buffer.render()
  288. var toWrite = {
  289. stream: frame.id,
  290. priority: frame.priority,
  291. chunks: chunks,
  292. callback: callback
  293. }
  294. var self = this
  295. this._resetTimeout()
  296. var bypass = this.version < 3.1
  297. this.window.send.update(-frame.data.length, bypass ? undefined : function () {
  298. self._resetTimeout()
  299. self.schedule(toWrite)
  300. })
  301. if (bypass) {
  302. this._resetTimeout()
  303. this.schedule(toWrite)
  304. }
  305. }
  306. Framer.prototype.pingFrame = function pingFrame (frame, callback) {
  307. this._frame({
  308. type: 'PING',
  309. id: 0,
  310. flags: 0
  311. }, function (buf, callback) {
  312. buf.reserve(4)
  313. var opaque = frame.opaque
  314. buf.writeUInt32BE(opaque.readUInt32BE(opaque.length - 4, true))
  315. }, callback)
  316. }
  317. Framer.prototype.rstFrame = function rstFrame (frame, callback) {
  318. this._frame({
  319. type: 'RST_STREAM',
  320. id: frame.id,
  321. flags: 0
  322. }, function (buf) {
  323. buf.reserve(8)
  324. // Stream ID
  325. buf.writeUInt32BE(frame.id & 0x7fffffff)
  326. // Status Code
  327. buf.writeUInt32BE(constants.error[frame.code])
  328. // Extra debugging information
  329. if (frame.extra) {
  330. buf.write(frame.extra)
  331. }
  332. }, callback)
  333. }
  334. Framer.prototype.prefaceFrame = function prefaceFrame () {
  335. }
  336. Framer.prototype.settingsFrame = function settingsFrame (options, callback) {
  337. var self = this
  338. var key = this.version + '/' + JSON.stringify(options)
  339. var settings = Framer.settingsCache[key]
  340. if (settings) {
  341. debug('cached settings')
  342. this._resetTimeout()
  343. this.schedule({
  344. stream: 0,
  345. priority: false,
  346. chunks: settings,
  347. callback: callback
  348. })
  349. return
  350. }
  351. var params = []
  352. for (var i = 0; i < constants.settingsIndex.length; i++) {
  353. var name = constants.settingsIndex[i]
  354. if (!name) { continue }
  355. // value: Infinity
  356. if (!isFinite(options[name])) {
  357. continue
  358. }
  359. if (options[name] !== undefined) {
  360. params.push({ key: i, value: options[name] })
  361. }
  362. }
  363. var frame = this._frame({
  364. type: 'SETTINGS',
  365. id: 0,
  366. flags: 0
  367. }, function (buf) {
  368. buf.reserve(4 + 8 * params.length)
  369. // Count of entries
  370. buf.writeUInt32BE(params.length)
  371. params.forEach(function (param) {
  372. var flag = constants.settings.FLAG_SETTINGS_PERSIST_VALUE << 24
  373. if (self.version === 2) {
  374. buf.writeUInt32LE(flag | param.key)
  375. } else { buf.writeUInt32BE(flag | param.key) }
  376. buf.writeUInt32BE(param.value & 0x7fffffff)
  377. })
  378. }, callback)
  379. Framer.settingsCache[key] = frame
  380. }
  381. Framer.settingsCache = {}
  382. Framer.prototype.ackSettingsFrame = function ackSettingsFrame (callback) {
  383. if (callback) {
  384. process.nextTick(callback)
  385. }
  386. }
  387. Framer.prototype.windowUpdateFrame = function windowUpdateFrame (frame,
  388. callback) {
  389. this._frame({
  390. type: 'WINDOW_UPDATE',
  391. id: frame.id,
  392. flags: 0
  393. }, function (buf) {
  394. buf.reserve(8)
  395. // ID
  396. buf.writeUInt32BE(frame.id & 0x7fffffff)
  397. // Delta
  398. buf.writeInt32BE(frame.delta)
  399. }, callback)
  400. }
  401. Framer.prototype.goawayFrame = function goawayFrame (frame, callback) {
  402. this._frame({
  403. type: 'GOAWAY',
  404. id: 0,
  405. flags: 0
  406. }, function (buf) {
  407. buf.reserve(8)
  408. // Last-good-stream-ID
  409. buf.writeUInt32BE(frame.lastId & 0x7fffffff)
  410. // Status
  411. buf.writeUInt32BE(constants.goaway[frame.code])
  412. }, callback)
  413. }
  414. Framer.prototype.priorityFrame = function priorityFrame (frame, callback) {
  415. // No such thing in SPDY
  416. if (callback) {
  417. process.nextTick(callback)
  418. }
  419. }
  420. Framer.prototype.xForwardedFor = function xForwardedFor (frame, callback) {
  421. this._frame({
  422. type: 'X_FORWARDED_FOR',
  423. id: 0,
  424. flags: 0
  425. }, function (buf) {
  426. buf.writeUInt32BE(Buffer.byteLength(frame.host))
  427. buf.write(frame.host)
  428. }, callback)
  429. }