Skip to content

Commit 1bbbcda

Browse files
committed
update code
1 parent ad1528e commit 1bbbcda

2 files changed

Lines changed: 200 additions & 24 deletions

File tree

dist/restore/index.js

Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13788,6 +13788,24 @@ class SecureProxyConnectionError extends UndiciError {
1378813788
[kSecureProxyConnectionError] = true
1378913789
}
1379013790

13791+
const kMessageSizeExceededError = Symbol.for('undici.error.UND_ERR_WS_MESSAGE_SIZE_EXCEEDED')
13792+
class MessageSizeExceededError extends UndiciError {
13793+
constructor (message) {
13794+
super(message)
13795+
this.name = 'MessageSizeExceededError'
13796+
this.message = message || 'Max decompressed message size exceeded'
13797+
this.code = 'UND_ERR_WS_MESSAGE_SIZE_EXCEEDED'
13798+
}
13799+
13800+
static [Symbol.hasInstance] (instance) {
13801+
return instance && instance[kMessageSizeExceededError] === true
13802+
}
13803+
13804+
get [kMessageSizeExceededError] () {
13805+
return true
13806+
}
13807+
}
13808+
1379113809
module.exports = {
1379213810
AbortError,
1379313811
HTTPParserError,
@@ -13811,7 +13829,8 @@ module.exports = {
1381113829
ResponseExceededMaxSizeError,
1381213830
RequestRetryError,
1381313831
ResponseError,
13814-
SecureProxyConnectionError
13832+
SecureProxyConnectionError,
13833+
MessageSizeExceededError
1381513834
}
1381613835

1381713836

@@ -13889,6 +13908,10 @@ class Request {
1388913908
throw new InvalidArgumentError('upgrade must be a string')
1389013909
}
1389113910

13911+
if (upgrade && !isValidHeaderValue(upgrade)) {
13912+
throw new InvalidArgumentError('invalid upgrade header')
13913+
}
13914+
1389213915
if (headersTimeout != null && (!Number.isFinite(headersTimeout) || headersTimeout < 0)) {
1389313916
throw new InvalidArgumentError('invalid headersTimeout')
1389413917
}
@@ -14183,13 +14206,19 @@ function processHeader (request, key, val) {
1418314206
val = `${val}`
1418414207
}
1418514208

14186-
if (request.host === null && headerName === 'host') {
14209+
if (headerName === 'host') {
14210+
if (request.host !== null) {
14211+
throw new InvalidArgumentError('duplicate host header')
14212+
}
1418714213
if (typeof val !== 'string') {
1418814214
throw new InvalidArgumentError('invalid host header')
1418914215
}
1419014216
// Consumed by Client
1419114217
request.host = val
14192-
} else if (request.contentLength === null && headerName === 'content-length') {
14218+
} else if (headerName === 'content-length') {
14219+
if (request.contentLength !== null) {
14220+
throw new InvalidArgumentError('duplicate content-length header')
14221+
}
1419314222
request.contentLength = parseInt(val, 10)
1419414223
if (!Number.isFinite(request.contentLength)) {
1419514224
throw new InvalidArgumentError('invalid content-length header')
@@ -36980,17 +37009,30 @@ module.exports = {
3698037009

3698137010
const { createInflateRaw, Z_DEFAULT_WINDOWBITS } = __nccwpck_require__(8522)
3698237011
const { isValidClientWindowBits } = __nccwpck_require__(8625)
37012+
const { MessageSizeExceededError } = __nccwpck_require__(8707)
3698337013

3698437014
const tail = Buffer.from([0x00, 0x00, 0xff, 0xff])
3698537015
const kBuffer = Symbol('kBuffer')
3698637016
const kLength = Symbol('kLength')
3698737017

37018+
// Default maximum decompressed message size: 4 MB
37019+
const kDefaultMaxDecompressedSize = 4 * 1024 * 1024
37020+
3698837021
class PerMessageDeflate {
3698937022
/** @type {import('node:zlib').InflateRaw} */
3699037023
#inflate
3699137024

3699237025
#options = {}
3699337026

37027+
/** @type {boolean} */
37028+
#aborted = false
37029+
37030+
/** @type {Function|null} */
37031+
#currentCallback = null
37032+
37033+
/**
37034+
* @param {Map<string, string>} extensions
37035+
*/
3699437036
constructor (extensions) {
3699537037
this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover')
3699637038
this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits')
@@ -37002,6 +37044,11 @@ class PerMessageDeflate {
3700237044
// payload of the message.
3700337045
// 2. Decompress the resulting data using DEFLATE.
3700437046

37047+
if (this.#aborted) {
37048+
callback(new MessageSizeExceededError())
37049+
return
37050+
}
37051+
3700537052
if (!this.#inflate) {
3700637053
let windowBits = Z_DEFAULT_WINDOWBITS
3700737054

@@ -37014,13 +37061,37 @@ class PerMessageDeflate {
3701437061
windowBits = Number.parseInt(this.#options.serverMaxWindowBits)
3701537062
}
3701637063

37017-
this.#inflate = createInflateRaw({ windowBits })
37064+
try {
37065+
this.#inflate = createInflateRaw({ windowBits })
37066+
} catch (err) {
37067+
callback(err)
37068+
return
37069+
}
3701837070
this.#inflate[kBuffer] = []
3701937071
this.#inflate[kLength] = 0
3702037072

3702137073
this.#inflate.on('data', (data) => {
37022-
this.#inflate[kBuffer].push(data)
37074+
if (this.#aborted) {
37075+
return
37076+
}
37077+
3702337078
this.#inflate[kLength] += data.length
37079+
37080+
if (this.#inflate[kLength] > kDefaultMaxDecompressedSize) {
37081+
this.#aborted = true
37082+
this.#inflate.removeAllListeners()
37083+
this.#inflate.destroy()
37084+
this.#inflate = null
37085+
37086+
if (this.#currentCallback) {
37087+
const cb = this.#currentCallback
37088+
this.#currentCallback = null
37089+
cb(new MessageSizeExceededError())
37090+
}
37091+
return
37092+
}
37093+
37094+
this.#inflate[kBuffer].push(data)
3702437095
})
3702537096

3702637097
this.#inflate.on('error', (err) => {
@@ -37029,16 +37100,22 @@ class PerMessageDeflate {
3702937100
})
3703037101
}
3703137102

37103+
this.#currentCallback = callback
3703237104
this.#inflate.write(chunk)
3703337105
if (fin) {
3703437106
this.#inflate.write(tail)
3703537107
}
3703637108

3703737109
this.#inflate.flush(() => {
37110+
if (this.#aborted || !this.#inflate) {
37111+
return
37112+
}
37113+
3703837114
const full = Buffer.concat(this.#inflate[kBuffer], this.#inflate[kLength])
3703937115

3704037116
this.#inflate[kBuffer].length = 0
3704137117
this.#inflate[kLength] = 0
37118+
this.#currentCallback = null
3704237119

3704337120
callback(null, full)
3704437121
})
@@ -37093,6 +37170,10 @@ class ByteParser extends Writable {
3709337170
/** @type {Map<string, PerMessageDeflate>} */
3709437171
#extensions
3709537172

37173+
/**
37174+
* @param {import('./websocket').WebSocket} ws
37175+
* @param {Map<string, string>|null} extensions
37176+
*/
3709637177
constructor (ws, extensions) {
3709737178
super()
3709837179

@@ -37235,21 +37316,20 @@ class ByteParser extends Writable {
3723537316

3723637317
const buffer = this.consume(8)
3723737318
const upper = buffer.readUInt32BE(0)
37319+
const lower = buffer.readUInt32BE(4)
3723837320

3723937321
// 2^31 is the maximum bytes an arraybuffer can contain
3724037322
// on 32-bit systems. Although, on 64-bit systems, this is
3724137323
// 2^53-1 bytes.
3724237324
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length
3724337325
// https://source.chromium.org/chromium/chromium/src/+/main:v8/src/common/globals.h;drc=1946212ac0100668f14eb9e2843bdd846e510a1e;bpv=1;bpt=1;l=1275
3724437326
// https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-array-buffer.h;l=34;drc=1946212ac0100668f14eb9e2843bdd846e510a1e
37245-
if (upper > 2 ** 31 - 1) {
37327+
if (upper !== 0 || lower > 2 ** 31 - 1) {
3724637328
failWebsocketConnection(this.ws, 'Received payload length > 2^31 bytes.')
3724737329
return
3724837330
}
3724937331

37250-
const lower = buffer.readUInt32BE(4)
37251-
37252-
this.#info.payloadLength = (upper << 8) + lower
37332+
this.#info.payloadLength = lower
3725337333
this.#state = parserStates.READ_DATA
3725437334
} else if (this.#state === parserStates.READ_DATA) {
3725537335
if (this.#byteOffset < this.#info.payloadLength) {
@@ -37279,7 +37359,7 @@ class ByteParser extends Writable {
3727937359
} else {
3728037360
this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => {
3728137361
if (error) {
37282-
closeWebSocketConnection(this.ws, 1007, error.message, error.message.length)
37362+
failWebsocketConnection(this.ws, error.message)
3728337363
return
3728437364
}
3728537365

@@ -37886,6 +37966,12 @@ function parseExtensions (extensions) {
3788637966
* @param {string} value
3788737967
*/
3788837968
function isValidClientWindowBits (value) {
37969+
// Must have at least one character
37970+
if (value.length === 0) {
37971+
return false
37972+
}
37973+
37974+
// Check all characters are ASCII digits
3788937975
for (let i = 0; i < value.length; i++) {
3789037976
const byte = value.charCodeAt(i)
3789137977

@@ -37894,7 +37980,9 @@ function isValidClientWindowBits (value) {
3789437980
}
3789537981
}
3789637982

37897-
return true
37983+
// Check numeric range: zlib requires windowBits in range 8-15
37984+
const num = Number.parseInt(value, 10)
37985+
return num >= 8 && num <= 15
3789837986
}
3789937987

3790037988
// https://nodejs.org/api/intl.html#detecting-internationalization-support
@@ -38373,7 +38461,7 @@ class WebSocket extends EventTarget {
3837338461
* @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
3837438462
*/
3837538463
#onConnectionEstablished (response, parsedExtensions) {
38376-
// processResponse is called when the "responses header list has been received and initialized."
38464+
// processResponse is called when the "response's header list has been received and initialized."
3837738465
// once this happens, the connection is open
3837838466
this[kResponse] = response
3837938467

0 commit comments

Comments
 (0)