7e9a877d57123f552733103ee75cb252f6e3c99a8b115620a4b7a98db29bcdb31789502d935c1ad0807526ddc41824ae397bba50869eb9fe40b9946ddeb368 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. 'use strict'
  2. var parser = exports
  3. var transport = require('../../../spdy-transport')
  4. var base = transport.protocol.base
  5. var utils = base.utils
  6. var constants = require('./constants')
  7. var assert = require('assert')
  8. var util = require('util')
  9. var OffsetBuffer = require('obuf')
  10. function Parser (options) {
  11. base.Parser.call(this, options)
  12. this.isServer = options.isServer
  13. this.waiting = constants.FRAME_HEADER_SIZE
  14. this.state = 'frame-head'
  15. this.pendingHeader = null
  16. }
  17. util.inherits(Parser, base.Parser)
  18. parser.create = function create (options) {
  19. return new Parser(options)
  20. }
  21. Parser.prototype.setMaxFrameSize = function setMaxFrameSize (size) {
  22. // http2-only
  23. }
  24. Parser.prototype.setMaxHeaderListSize = function setMaxHeaderListSize (size) {
  25. // http2-only
  26. }
  27. // Only for testing
  28. Parser.prototype.skipPreface = function skipPreface () {
  29. }
  30. Parser.prototype.execute = function execute (buffer, callback) {
  31. if (this.state === 'frame-head') { return this.onFrameHead(buffer, callback) }
  32. assert(this.state === 'frame-body' && this.pendingHeader !== null)
  33. var self = this
  34. var header = this.pendingHeader
  35. this.pendingHeader = null
  36. this.onFrameBody(header, buffer, function (err, frame) {
  37. if (err) {
  38. return callback(err)
  39. }
  40. self.state = 'frame-head'
  41. self.waiting = constants.FRAME_HEADER_SIZE
  42. self.partial = false
  43. callback(null, frame)
  44. })
  45. }
  46. Parser.prototype.executePartial = function executePartial (buffer, callback) {
  47. var header = this.pendingHeader
  48. if (this.window) {
  49. this.window.recv.update(-buffer.size)
  50. }
  51. // DATA frame
  52. callback(null, {
  53. type: 'DATA',
  54. id: header.id,
  55. // Partial DATA can't be FIN
  56. fin: false,
  57. data: buffer.take(buffer.size)
  58. })
  59. }
  60. Parser.prototype.onFrameHead = function onFrameHead (buffer, callback) {
  61. var header = {
  62. control: (buffer.peekUInt8() & 0x80) === 0x80,
  63. version: null,
  64. type: null,
  65. id: null,
  66. flags: null,
  67. length: null
  68. }
  69. if (header.control) {
  70. header.version = buffer.readUInt16BE() & 0x7fff
  71. header.type = buffer.readUInt16BE()
  72. } else {
  73. header.id = buffer.readUInt32BE(0) & 0x7fffffff
  74. }
  75. header.flags = buffer.readUInt8()
  76. header.length = buffer.readUInt24BE()
  77. if (this.version === null && header.control) {
  78. // TODO(indutny): do ProtocolError here and in the rest of errors
  79. if (header.version !== 2 && header.version !== 3) {
  80. return callback(new Error('Unsupported SPDY version: ' + header.version))
  81. }
  82. this.setVersion(header.version)
  83. }
  84. this.state = 'frame-body'
  85. this.waiting = header.length
  86. this.pendingHeader = header
  87. this.partial = !header.control
  88. callback(null, null)
  89. }
  90. Parser.prototype.onFrameBody = function onFrameBody (header, buffer, callback) {
  91. // Data frame
  92. if (!header.control) {
  93. // Count received bytes
  94. if (this.window) {
  95. this.window.recv.update(-buffer.size)
  96. }
  97. // No support for compressed DATA
  98. if ((header.flags & constants.flags.FLAG_COMPRESSED) !== 0) {
  99. return callback(new Error('DATA compression not supported'))
  100. }
  101. if (header.id === 0) {
  102. return callback(this.error(constants.error.PROTOCOL_ERROR,
  103. 'Invalid stream id for DATA'))
  104. }
  105. return callback(null, {
  106. type: 'DATA',
  107. id: header.id,
  108. fin: (header.flags & constants.flags.FLAG_FIN) !== 0,
  109. data: buffer.take(buffer.size)
  110. })
  111. }
  112. if (header.type === 0x01 || header.type === 0x02) { // SYN_STREAM or SYN_REPLY
  113. this.onSynHeadFrame(header.type, header.flags, buffer, callback)
  114. } else if (header.type === 0x03) { // RST_STREAM
  115. this.onRSTFrame(buffer, callback)
  116. } else if (header.type === 0x04) { // SETTINGS
  117. this.onSettingsFrame(buffer, callback)
  118. } else if (header.type === 0x05) {
  119. callback(null, { type: 'NOOP' })
  120. } else if (header.type === 0x06) { // PING
  121. this.onPingFrame(buffer, callback)
  122. } else if (header.type === 0x07) { // GOAWAY
  123. this.onGoawayFrame(buffer, callback)
  124. } else if (header.type === 0x08) { // HEADERS
  125. this.onHeaderFrames(buffer, callback)
  126. } else if (header.type === 0x09) { // WINDOW_UPDATE
  127. this.onWindowUpdateFrame(buffer, callback)
  128. } else if (header.type === 0xf000) { // X-FORWARDED
  129. this.onXForwardedFrame(buffer, callback)
  130. } else {
  131. callback(null, { type: 'unknown: ' + header.type })
  132. }
  133. }
  134. Parser.prototype._filterHeader = function _filterHeader (headers, name) {
  135. var res = {}
  136. var keys = Object.keys(headers)
  137. for (var i = 0; i < keys.length; i++) {
  138. var key = keys[i]
  139. if (key !== name) {
  140. res[key] = headers[key]
  141. }
  142. }
  143. return res
  144. }
  145. Parser.prototype.onSynHeadFrame = function onSynHeadFrame (type,
  146. flags,
  147. body,
  148. callback) {
  149. var self = this
  150. var stream = type === 0x01
  151. var offset = stream ? 10 : this.version === 2 ? 6 : 4
  152. if (!body.has(offset)) {
  153. return callback(new Error('SynHead OOB'))
  154. }
  155. var head = body.clone(offset)
  156. body.skip(offset)
  157. this.parseKVs(body, function (err, headers) {
  158. if (err) {
  159. return callback(err)
  160. }
  161. if (stream &&
  162. (!headers[':method'] || !headers[':path'])) {
  163. return callback(new Error('Missing `:method` and/or `:path` header'))
  164. }
  165. var id = head.readUInt32BE() & 0x7fffffff
  166. if (id === 0) {
  167. return callback(self.error(constants.error.PROTOCOL_ERROR,
  168. 'Invalid stream id for HEADERS'))
  169. }
  170. var associated = stream ? head.readUInt32BE() & 0x7fffffff : 0
  171. var priority = stream
  172. ? head.readUInt8() >> 5
  173. : utils.weightToPriority(constants.DEFAULT_WEIGHT)
  174. var fin = (flags & constants.flags.FLAG_FIN) !== 0
  175. var unidir = (flags & constants.flags.FLAG_UNIDIRECTIONAL) !== 0
  176. var path = headers[':path']
  177. var isPush = stream && associated !== 0
  178. var weight = utils.priorityToWeight(priority)
  179. var priorityInfo = {
  180. weight: weight,
  181. exclusive: false,
  182. parent: 0
  183. }
  184. if (!isPush) {
  185. callback(null, {
  186. type: 'HEADERS',
  187. id: id,
  188. priority: priorityInfo,
  189. fin: fin,
  190. writable: !unidir,
  191. headers: headers,
  192. path: path
  193. })
  194. return
  195. }
  196. if (stream && !headers[':status']) {
  197. return callback(new Error('Missing `:status` header'))
  198. }
  199. var filteredHeaders = self._filterHeader(headers, ':status')
  200. callback(null, [ {
  201. type: 'PUSH_PROMISE',
  202. id: associated,
  203. fin: false,
  204. promisedId: id,
  205. headers: filteredHeaders,
  206. path: path
  207. }, {
  208. type: 'HEADERS',
  209. id: id,
  210. fin: fin,
  211. priority: priorityInfo,
  212. writable: true,
  213. path: undefined,
  214. headers: {
  215. ':status': headers[':status']
  216. }
  217. }])
  218. })
  219. }
  220. Parser.prototype.onHeaderFrames = function onHeaderFrames (body, callback) {
  221. var offset = this.version === 2 ? 6 : 4
  222. if (!body.has(offset)) {
  223. return callback(new Error('HEADERS OOB'))
  224. }
  225. var streamId = body.readUInt32BE() & 0x7fffffff
  226. if (this.version === 2) { body.skip(2) }
  227. this.parseKVs(body, function (err, headers) {
  228. if (err) { return callback(err) }
  229. callback(null, {
  230. type: 'HEADERS',
  231. priority: {
  232. parent: 0,
  233. exclusive: false,
  234. weight: constants.DEFAULT_WEIGHT
  235. },
  236. id: streamId,
  237. fin: false,
  238. writable: true,
  239. path: undefined,
  240. headers: headers
  241. })
  242. })
  243. }
  244. Parser.prototype.parseKVs = function parseKVs (buffer, callback) {
  245. var self = this
  246. this.decompress.write(buffer.toChunks(), function (err, chunks) {
  247. if (err) {
  248. return callback(err)
  249. }
  250. var buffer = new OffsetBuffer()
  251. for (var i = 0; i < chunks.length; i++) {
  252. buffer.push(chunks[i])
  253. }
  254. var size = self.version === 2 ? 2 : 4
  255. if (!buffer.has(size)) { return callback(new Error('KV OOB')) }
  256. var count = self.version === 2
  257. ? buffer.readUInt16BE()
  258. : buffer.readUInt32BE()
  259. var headers = {}
  260. function readString () {
  261. if (!buffer.has(size)) { return null }
  262. var len = self.version === 2
  263. ? buffer.readUInt16BE()
  264. : buffer.readUInt32BE()
  265. if (!buffer.has(len)) { return null }
  266. var value = buffer.take(len)
  267. return value.toString()
  268. }
  269. while (count > 0) {
  270. var key = readString()
  271. var value = readString()
  272. if (key === null || value === null) {
  273. return callback(new Error('Headers OOB'))
  274. }
  275. if (self.version < 3) {
  276. var isInternal = /^(method|version|url|host|scheme|status)$/.test(key)
  277. if (key === 'url') {
  278. key = 'path'
  279. }
  280. if (isInternal) {
  281. key = ':' + key
  282. }
  283. }
  284. // Compatibility with HTTP2
  285. if (key === ':status') {
  286. value = value.split(/ /g, 2)[0]
  287. }
  288. count--
  289. if (key === ':host') {
  290. key = ':authority'
  291. }
  292. // Skip version, not present in HTTP2
  293. if (key === ':version') {
  294. continue
  295. }
  296. value = value.split(/\0/g)
  297. for (var j = 0; j < value.length; j++) {
  298. utils.addHeaderLine(key, value[j], headers)
  299. }
  300. }
  301. callback(null, headers)
  302. })
  303. }
  304. Parser.prototype.onRSTFrame = function onRSTFrame (body, callback) {
  305. if (!body.has(8)) { return callback(new Error('RST OOB')) }
  306. var frame = {
  307. type: 'RST',
  308. id: body.readUInt32BE() & 0x7fffffff,
  309. code: constants.errorByCode[body.readUInt32BE()]
  310. }
  311. if (frame.id === 0) {
  312. return callback(this.error(constants.error.PROTOCOL_ERROR,
  313. 'Invalid stream id for RST'))
  314. }
  315. if (body.size !== 0) {
  316. frame.extra = body.take(body.size)
  317. }
  318. callback(null, frame)
  319. }
  320. Parser.prototype.onSettingsFrame = function onSettingsFrame (body, callback) {
  321. if (!body.has(4)) {
  322. return callback(new Error('SETTINGS OOB'))
  323. }
  324. var settings = {}
  325. var number = body.readUInt32BE()
  326. var idMap = {
  327. 1: 'upload_bandwidth',
  328. 2: 'download_bandwidth',
  329. 3: 'round_trip_time',
  330. 4: 'max_concurrent_streams',
  331. 5: 'current_cwnd',
  332. 6: 'download_retrans_rate',
  333. 7: 'initial_window_size',
  334. 8: 'client_certificate_vector_size'
  335. }
  336. if (!body.has(number * 8)) {
  337. return callback(new Error('SETTINGS OOB#2'))
  338. }
  339. for (var i = 0; i < number; i++) {
  340. var id = this.version === 2
  341. ? body.readUInt32LE()
  342. : body.readUInt32BE()
  343. var flags = (id >> 24) & 0xff
  344. id = id & 0xffffff
  345. // Skip persisted settings
  346. if (flags & 0x2) { continue }
  347. var name = idMap[id]
  348. settings[name] = body.readUInt32BE()
  349. }
  350. callback(null, {
  351. type: 'SETTINGS',
  352. settings: settings
  353. })
  354. }
  355. Parser.prototype.onPingFrame = function onPingFrame (body, callback) {
  356. if (!body.has(4)) {
  357. return callback(new Error('PING OOB'))
  358. }
  359. var isServer = this.isServer
  360. var opaque = body.clone(body.size).take(body.size)
  361. var id = body.readUInt32BE()
  362. var ack = isServer ? (id % 2 === 0) : (id % 2 === 1)
  363. callback(null, { type: 'PING', opaque: opaque, ack: ack })
  364. }
  365. Parser.prototype.onGoawayFrame = function onGoawayFrame (body, callback) {
  366. if (!body.has(8)) {
  367. return callback(new Error('GOAWAY OOB'))
  368. }
  369. callback(null, {
  370. type: 'GOAWAY',
  371. lastId: body.readUInt32BE() & 0x7fffffff,
  372. code: constants.goawayByCode[body.readUInt32BE()]
  373. })
  374. }
  375. Parser.prototype.onWindowUpdateFrame = function onWindowUpdateFrame (body,
  376. callback) {
  377. if (!body.has(8)) {
  378. return callback(new Error('WINDOW_UPDATE OOB'))
  379. }
  380. callback(null, {
  381. type: 'WINDOW_UPDATE',
  382. id: body.readUInt32BE() & 0x7fffffff,
  383. delta: body.readInt32BE()
  384. })
  385. }
  386. Parser.prototype.onXForwardedFrame = function onXForwardedFrame (body,
  387. callback) {
  388. if (!body.has(4)) {
  389. return callback(new Error('X_FORWARDED OOB'))
  390. }
  391. var len = body.readUInt32BE()
  392. if (!body.has(len)) { return callback(new Error('X_FORWARDED host length OOB')) }
  393. callback(null, {
  394. type: 'X_FORWARDED_FOR',
  395. host: body.take(len).toString()
  396. })
  397. }