mirror of
https://github.com/dawidd6/action-send-mail.git
synced 2026-10-31 01:38:34 +07:00
node_modules: update (#358)
Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com>
This commit is contained in:
1 parent
0d58990203
commit
b6b2177ec4
13 files changed
+154
-34
No files matched your search
+91
-10
@@ -1,4 +1,5 @@
|
||||
'use strict'
|
||||
const buffer = require('node:buffer')
|
||||
const { Transform } = require('node:stream')
|
||||
const { isASCIINumber, isValidLastEventId } = require('./util')
|
||||
|
||||
@@ -23,11 +24,19 @@ const COLON = 0x3A
|
||||
*/
|
||||
const SPACE = 0x20
|
||||
|
||||
const defaultMaxEventSize = buffer.kStringMaxLength
|
||||
|
||||
const DATA = Buffer.from('data')
|
||||
const EVENT = Buffer.from('event')
|
||||
const ID = Buffer.from('id')
|
||||
const RETRY = Buffer.from('retry')
|
||||
|
||||
// Each data field creates a string node, and each unconsumed input chunk keeps
|
||||
// a Buffer wrapper. The absolute ceiling protects the large default byte limit.
|
||||
const minParserItems = 1024
|
||||
const maxParserItems = 131072
|
||||
const bytesPerParserItem = 128
|
||||
|
||||
function isASCIINumberBytes (buffer, start) {
|
||||
if (start >= buffer.length) {
|
||||
return false
|
||||
@@ -66,6 +75,12 @@ function isFieldName (line, length, field) {
|
||||
return true
|
||||
}
|
||||
|
||||
function createMaxEventSizeExceededError () {
|
||||
const error = new Error('EventSource message size exceeded')
|
||||
error.aborted = false
|
||||
return error
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} EventSourceStreamEvent
|
||||
* @type {object}
|
||||
@@ -114,6 +129,13 @@ class EventSourceStream extends Transform {
|
||||
pos = 0
|
||||
lineChunkIndex = 0
|
||||
linePos = 0
|
||||
lineLength = 0
|
||||
eventSize = 0
|
||||
eventFieldSizes = { data: 0, event: 0, id: 0, retry: 0 }
|
||||
dataLineCount = 0
|
||||
maxEventSize
|
||||
maxDataLines
|
||||
maxBufferedChunks
|
||||
|
||||
event = {
|
||||
data: undefined,
|
||||
@@ -124,6 +146,7 @@ class EventSourceStream extends Transform {
|
||||
|
||||
/**
|
||||
* @param {object} options
|
||||
* @param {number} [options.maxEventSize]
|
||||
* @param {eventSourceSettings} options.eventSourceSettings
|
||||
* @param {Function} [options.push]
|
||||
*/
|
||||
@@ -135,6 +158,12 @@ class EventSourceStream extends Transform {
|
||||
super(options)
|
||||
|
||||
this.state = options.eventSourceSettings || {}
|
||||
this.maxEventSize = options.maxEventSize ?? defaultMaxEventSize
|
||||
const parserItemLimit = this.maxEventSize > 0
|
||||
? Math.min(maxParserItems, Math.max(minParserItems, Math.floor(this.maxEventSize / bytesPerParserItem)))
|
||||
: 0
|
||||
this.maxDataLines = parserItemLimit
|
||||
this.maxBufferedChunks = parserItemLimit
|
||||
if (options.push) {
|
||||
this.push = options.push
|
||||
}
|
||||
@@ -152,6 +181,14 @@ class EventSourceStream extends Transform {
|
||||
return
|
||||
}
|
||||
|
||||
// A leading line ending can immediately release the currently buffered
|
||||
// line, even when it uses the final allowed chunk slot.
|
||||
if (this.maxEventSize > 0 &&
|
||||
this.chunks.length >= this.maxBufferedChunks &&
|
||||
chunk[0] !== LF && chunk[0] !== CR) {
|
||||
callback(createMaxEventSizeExceededError())
|
||||
return
|
||||
}
|
||||
this.chunks.push(chunk)
|
||||
|
||||
// Strip leading byte-order-mark if we opened the stream and started
|
||||
@@ -230,7 +267,12 @@ class EventSourceStream extends Transform {
|
||||
|
||||
// In any case, we can process the line as we reached an
|
||||
// end-of-line character
|
||||
this.parseLine(this.readLine(), this.event)
|
||||
try {
|
||||
this.parseLine(this.readLine(), this.event)
|
||||
} catch (error) {
|
||||
callback(error)
|
||||
return
|
||||
}
|
||||
this.consumeCurrentByte()
|
||||
// A line was processed and this could be the end of the event. We need
|
||||
// to check if the next line is empty to determine if the event is
|
||||
@@ -239,15 +281,28 @@ class EventSourceStream extends Transform {
|
||||
continue
|
||||
}
|
||||
|
||||
// readLine() is only called after CR or LF. Reject an unfinished line
|
||||
// before it can retain an unbounded number of decoded chunks. The seven
|
||||
// extra bytes allow the longest field prefix ("event: " or "retry: ").
|
||||
if (this.maxEventSize > 0 && ++this.lineLength > this.maxEventSize + 7) {
|
||||
callback(createMaxEventSizeExceededError())
|
||||
return
|
||||
}
|
||||
this.advanceCursor()
|
||||
}
|
||||
|
||||
callback()
|
||||
}
|
||||
|
||||
_destroy (error, callback) {
|
||||
this.chunks.length = 0
|
||||
this.clearEvent()
|
||||
super._destroy(error, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Buffer} line
|
||||
* @param {EventStreamEvent} event
|
||||
* @param {EventSourceStreamEvent} event
|
||||
*/
|
||||
parseLine (line, event) {
|
||||
// If the line is empty (a blank line)
|
||||
@@ -281,6 +336,13 @@ class EventSourceStream extends Transform {
|
||||
}
|
||||
|
||||
if (isFieldName(line, fieldLength, DATA)) {
|
||||
if (this.maxEventSize > 0 && this.dataLineCount >= this.maxDataLines) {
|
||||
throw createMaxEventSizeExceededError()
|
||||
}
|
||||
const valueBytes = line.length - valueStart
|
||||
const dataSize = this.eventFieldSizes.data + (event.data === undefined ? 0 : 1) + valueBytes
|
||||
this.setEventFieldSize('data', dataSize)
|
||||
|
||||
const value = line.toString('utf8', valueStart)
|
||||
|
||||
if (event.data === undefined) {
|
||||
@@ -288,11 +350,13 @@ class EventSourceStream extends Transform {
|
||||
} else {
|
||||
event.data += `\n${value}`
|
||||
}
|
||||
this.dataLineCount++
|
||||
return
|
||||
}
|
||||
|
||||
if (isFieldName(line, fieldLength, RETRY)) {
|
||||
if (isASCIINumberBytes(line, valueStart)) {
|
||||
this.setEventFieldSize('retry', line.length - valueStart)
|
||||
event.retry = line.toString('utf8', valueStart)
|
||||
}
|
||||
return
|
||||
@@ -300,20 +364,29 @@ class EventSourceStream extends Transform {
|
||||
|
||||
if (isFieldName(line, fieldLength, ID)) {
|
||||
if (isValidLastEventIdBytes(line, valueStart)) {
|
||||
this.setEventFieldSize('id', line.length - valueStart)
|
||||
event.id = line.toString('utf8', valueStart)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (isFieldName(line, fieldLength, EVENT)) {
|
||||
const value = line.toString('utf8', valueStart)
|
||||
|
||||
if (value.length > 0) {
|
||||
event.event = value
|
||||
if (valueStart < line.length) {
|
||||
this.setEventFieldSize('event', line.length - valueStart)
|
||||
event.event = line.toString('utf8', valueStart)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setEventFieldSize (field, size) {
|
||||
const eventSize = this.eventSize - this.eventFieldSizes[field] + size
|
||||
if (this.maxEventSize > 0 && eventSize > this.maxEventSize) {
|
||||
throw createMaxEventSizeExceededError()
|
||||
}
|
||||
this.eventSize = eventSize
|
||||
this.eventFieldSizes[field] = size
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {EventSourceStreamEvent} event
|
||||
*/
|
||||
@@ -344,6 +417,12 @@ class EventSourceStream extends Transform {
|
||||
this.event.event = undefined
|
||||
this.event.id = undefined
|
||||
this.event.retry = undefined
|
||||
this.eventSize = 0
|
||||
this.dataLineCount = 0
|
||||
this.eventFieldSizes.data = 0
|
||||
this.eventFieldSizes.event = 0
|
||||
this.eventFieldSizes.id = 0
|
||||
this.eventFieldSizes.retry = 0
|
||||
}
|
||||
|
||||
hasPendingEvent () {
|
||||
@@ -379,14 +458,16 @@ class EventSourceStream extends Transform {
|
||||
syncLineStartToCursor () {
|
||||
this.lineChunkIndex = this.chunkIndex
|
||||
this.linePos = this.pos
|
||||
this.lineLength = 0
|
||||
this.dropConsumedChunks()
|
||||
}
|
||||
|
||||
dropConsumedChunks () {
|
||||
while (this.lineChunkIndex > 0) {
|
||||
this.chunks.shift()
|
||||
this.lineChunkIndex--
|
||||
this.chunkIndex--
|
||||
if (this.lineChunkIndex > 0) {
|
||||
const consumed = this.lineChunkIndex
|
||||
this.chunks.splice(0, consumed)
|
||||
this.lineChunkIndex = 0
|
||||
this.chunkIndex -= consumed
|
||||
}
|
||||
|
||||
if (this.chunkIndex === this.chunks.length) {
|
||||
|
||||
+4
-1
@@ -11,6 +11,7 @@ const { isNetworkError } = require('../fetch/response')
|
||||
const { delay } = require('./util')
|
||||
const { kEnumerableProperty } = require('../../core/util')
|
||||
const { environmentSettingsObject } = require('../fetch/util')
|
||||
const { getGlobalDispatcher } = require('../../global')
|
||||
|
||||
let experimentalWarned = false
|
||||
|
||||
@@ -292,6 +293,7 @@ class EventSource extends EventTarget {
|
||||
|
||||
const eventSourceStream = new EventSourceStream({
|
||||
eventSourceSettings: this.#state,
|
||||
maxEventSize: this.#dispatcher.eventSourceOptions?.maxEventSize,
|
||||
push: (event) => {
|
||||
this.dispatchEvent(createFastMessageEvent(
|
||||
event.type,
|
||||
@@ -470,7 +472,8 @@ webidl.converters.EventSourceInitDict = webidl.dictionaryConverter([
|
||||
},
|
||||
{
|
||||
key: 'dispatcher', // undici only
|
||||
converter: webidl.converters.any
|
||||
converter: webidl.converters.any,
|
||||
defaultValue: () => getGlobalDispatcher()
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
+7
-9
@@ -1950,8 +1950,10 @@ async function httpNetworkFetch (
|
||||
// 19. Run these steps in parallel:
|
||||
|
||||
// 1. Run these steps, but abort when fetchParams is canceled:
|
||||
fetchParams.controller.onAborted = onAborted
|
||||
fetchParams.controller.on('terminated', onAborted)
|
||||
if (!fetchParams.controller.resume) {
|
||||
fetchParams.controller.on('terminated', onAborted)
|
||||
}
|
||||
|
||||
fetchParams.controller.resume = async () => {
|
||||
// 1. While true
|
||||
while (true) {
|
||||
@@ -2120,8 +2122,6 @@ async function httpNetworkFetch (
|
||||
}
|
||||
location = headersList.get('location', true)
|
||||
|
||||
this.body = new Readable({ read: resume })
|
||||
|
||||
const decoders = []
|
||||
|
||||
const willFollow = location && request.redirect === 'follow' &&
|
||||
@@ -2139,7 +2139,7 @@ async function httpNetworkFetch (
|
||||
// CVE fix similar to urllib3 (GHSA-gm62-xv2j-4w53) and curl (CVE-2022-32206).
|
||||
const maxContentEncodings = 5
|
||||
if (codings.length > maxContentEncodings) {
|
||||
reject(new Error(`too many content-encodings in response: ${codings.length}, maximum allowed is ${maxContentEncodings}`))
|
||||
this.abort(new Error(`too many content-encodings in response: ${codings.length}, maximum allowed is ${maxContentEncodings}`))
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -2172,6 +2172,8 @@ async function httpNetworkFetch (
|
||||
}
|
||||
}
|
||||
|
||||
this.body = new Readable({ read: resume })
|
||||
|
||||
const onError = this.onError.bind(this)
|
||||
|
||||
resolve({
|
||||
@@ -2218,10 +2220,6 @@ async function httpNetworkFetch (
|
||||
fetchParams.controller.off('terminated', this.abort)
|
||||
}
|
||||
|
||||
if (fetchParams.controller.onAborted) {
|
||||
fetchParams.controller.off('terminated', fetchParams.controller.onAborted)
|
||||
}
|
||||
|
||||
fetchParams.controller.ended = true
|
||||
|
||||
this.body.push(null)
|
||||
|
||||
+14
@@ -249,6 +249,20 @@ class ByteParser extends Writable {
|
||||
if (isControlFrame(this.#info.opcode)) {
|
||||
this.#loop = this.parseControlFrame(body)
|
||||
this.#state = parserStates.INFO
|
||||
|
||||
// If the automatic PONG/CLOSE reply filled the socket's write buffer,
|
||||
// stop parsing and resume on drain so a PING flood cannot grow the
|
||||
// outbound queue without bound.
|
||||
const socket = this.ws[kResponse].socket
|
||||
if (this.#loop && socket.writableNeedDrain) {
|
||||
this.#loop = false
|
||||
socket.once('drain', () => {
|
||||
if (this.destroyed) return
|
||||
this.#loop = true
|
||||
this.run(callback)
|
||||
})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if (!this.#info.compressed) {
|
||||
if (!this.writeFragments(body)) {
|
||||
|
||||
Reference in new issue
Block a user